【问题标题】:Folktale / fantasyland Maybe not working as expected民间故事/幻想世界可能没有按预期工作
【发布时间】:2018-11-03 20:38:15
【问题描述】:

阅读Frisbys guide to functional programming,目前在关于Maybe的章节。在appendix 书中建议使用folktalefantasyland

但是,在这两个库中,Maybe 似乎不像书中描述的那样工作。

const Maybe = require('folktale/maybe')
// const Maybe = require('fantasy-options')
const {
    flip, concat, toUpper, path, pathOr, match, prop
} = require('ramda')

console.log(
    Maybe.of('Malkovich Malkovich').map(match(/a/ig))
)
// Just(['a', 'a'])

Maybe.of(null).map(match(/a/ig))
//******************************
// TypeError: Cannot read property 'match' of null
//******************************
// Nothing

Maybe.of(
    { name: 'Boris' }
).map(prop('age')).map(add(10))
// Nothing

Maybe.of(
    { name: 'Dinah', age: 14 }
).map(prop('age')).map(add(10))
// Just(24)

在从书中复制的这个示例中,第一个语句可以正常工作,但第二个语句得到一个TypeError。这似乎完全违背了Maybe 的目的。还是我误会了什么?

Example repl.it

【问题讨论】:

  • Maybe.of 总是构造一个Maybe。也许看看Maybe.fromNullable,它根据输入构造MaybeNothing
  • Maybe 无法在语言级别替换 null。但是,Maybenull 具有相似的作用,因此您可以在应用程序级别替换它。 null 包裹在 Maybe 中仍然是 null 并且会导致提升函数的类型错误。
  • @ftor 是的,但是如果 .map fn 的值为空,难道不应该跳过运行它吗? map(fn) { return this.isNothing ? this : Maybe.of(fn(this.$value)); }我以为这就是Maybe的全部目的
  • 你可以为null构造一个具有特定行为的类型。但是你会失去这种类型的参数多态性。不过,我没有足够的经验来评估后果。为null破例或许还不错。无论如何,看看参数化。
  • @BillJohnston Maybe 的全部意义在于为null 提供更优雅的替代,而不是null 值的包装。识别null 并将它们变成Nothings 不是Maybe 的工作。 Frisby 的书使用(有点老套)的方法将包含 nullMaybe 视为 Nothing,但我认为这不是典型的,这几乎是一个实现细节。在 Folktale 中,创建Nothing 的方法是使用Maybe.Nothing(),但Folktale 还提供了Maybe.fromNullable(上面已经提到),它将nullundefined 转换为Nothing

标签: javascript functional-programming monads fantasyland folktale


【解决方案1】:

更新:2019 年 8 月

很高兴您提出这个问题,我最初也对行为上的差异感到惊讶。正如其他人所回应的那样,这归结为Frisby Mostly Adequate Guide implementation 的编码方式。 “不规则”实现细节与isNothings 函数实现屏蔽使用Maybe.of 传入的null 或未定义value 的方式有关:

get isNothing() {
    return this.$value === null || this.$value === undefined;
 }

如果您参考其他实现 - 然后使用 Maybe.of() 创建您的 Maybe 确实允许您传入 nullundefined 以获取 Just 案例的值并实际打印例如 Maybe.Just({ value: null })

相反,在使用 Folktale 时,使用 Maybe.fromNullable() 创建 Maybe,这将根据输入的值分配 JustNothing

这是所提供代码的工作版本:

const Maybe = require("folktale/maybe");

const {
  flip,
  concat,
  toUpper,
  path,
  pathOr,
  match,
  prop,
  add
} = require("ramda");

console.log(Maybe.of("Malkovich Malkovich").map(match(/a/gi)));
//-> folktale:Maybe.Just({ value: ["a", "a"] })

console.log(Maybe.fromNullable(null).map(match(/a/gi)));
//-> folktale:Maybe.Nothing({  })

最后,这里是一个 Maybe 的演示实现,编码为使用 fromNullable(类似于 Folktale 实现)。我从我认为强烈推荐的书 - Functional Programming In JavaScript by Luis Atencio 中获取了这个参考实现。他在第 5 章的大部分时间里都清楚地解释了这一点。

/**
 * Custom Maybe Monad used in FP in JS book written in ES6
 * Author: Luis Atencio
 */ 
exports.Maybe = class Maybe {
    static just(a) {
        return new exports.Just(a);
    }
    static nothing() {
        return new exports.Nothing();
    }
    static fromNullable(a) {
        return a !== null ? Maybe.just(a) : Maybe.nothing();
    }
    static of(a) {
        return Maybe.just(a);
    }
    get isNothing() {
        return false;
    }
    get isJust() {
        return false;
    }
};


// Derived class Just -> Presence of a value
exports.Just = class Just extends exports.Maybe {
    constructor(value) {
        super();
        this._value = value;
    }

    get value() {
        return this._value;
    }

    map(f) {
        return exports.Maybe.fromNullable(f(this._value));
    }

    chain(f) {
        return f(this._value);
    }

    getOrElse() {
        return this._value;
    }

    filter(f) {
        exports.Maybe.fromNullable(f(this._value) ? this._value : null);
    }

    get isJust() {
        return true;
    }

    toString () {
        return `Maybe.Just(${this._value})`;
    }
};

// Derived class Empty -> Abscense of a value
exports.Nothing = class Nothing extends exports.Maybe {
    map(f) {
        return this;
    }

    chain(f) {
        return this;
    }

    get value() {
        throw new TypeError("Can't extract the value of a Nothing.");
    }

    getOrElse(other) {
        return other;
    }

    filter() {
        return this._value;
    }

    get isNothing() {
        return true;
    }   

    toString() {
        return 'Maybe.Nothing';
    }
};

【讨论】:

  • 不错的答案!您能否也谈谈这种情况:Maybe.of("foo").map(s => null).map(s => s.toUpperCase())(假设s => null 是一个可以返回字符串或空值的函数)。我的直觉是,如果maps 链中的函数返回 null,则链必须停止。但现在我发现我错了。不过,我想知道,在这种情况下是否有一种惯用的方法来阻止链条?我试图返回Maybe.Nothing()而不是null,但它只是将Nothing包装成Just,所以大写函数获取Nothing的实例。
猜你喜欢
  • 1970-01-01
  • 2010-10-08
  • 1970-01-01
  • 2021-12-26
  • 2016-11-07
  • 2015-11-09
  • 1970-01-01
  • 2015-02-23
  • 1970-01-01
相关资源
最近更新 更多