【发布时间】:2018-09-04 12:08:48
【问题描述】:
我在 Typescript 中创建一个简单的 Monad 库只是为了好玩,遵循 https://github.com/fantasyland/fantasy-land 的精彩规范。
在这里,创作者提出了三个一元法则:
-
M.of(a).chain(f)等价于f(a)(左身份) -
m.chain(M.of)等价于m(正确的身份) -
m.chain(f).chain(g)等价于m.chain(x => f(x).chain(g))(关联性)
所以我实施了法律测试和这个经典实施。
import { setoid, functor, apply } from './common';
const isEmpty = (value: any) => value === null || value === undefined;
const chain = function<T, U>(fn: (val: T) => IMonad<U>) {
return this.isNone() ? this : fn(this.lift());
};
class Some<T> implements IMonad<T> {
private _value: T;
constructor(value: T) {
this._value = value;
}
lift = () => this._value;
of = (value: T) => new Some(value);
map = functor;
ap = apply;
flatMap = chain;
equals = setoid;
isSome = () => true
isNone = () => false
}
class None implements IMonad<any> {
constructor() {}
lift = () => { throw "Cannot get a value from None" };
of = (value: any) => new None();
map = functor;
ap = apply;
flatMap = chain;
equals = setoid;
isSome = () => false;
isNone = () => true;
}
class MaybeStatic implements IMonadStatic<any> {
of = (value:any) => !isEmpty(value) ? new Some(value) : new None();
}
const maybeStatic = new MaybeStatic();
export { maybeStatic as Maybe, Some, None, MaybeStatic };
运行测试我看到在None 的情况下Maybe monad left identity 规则没有通过测试。事实上:
Maybe.of(null).chain(x => Some(x+2)) !== (null + 2)
在这种情况下,Js/Ts 允许操作 null + 2 的事实似乎打破了第一条单子规则(其他语言可能不允许此操作)。我检查了很多 Maybe monad 的 javascript 和 typescript 实现,他们似乎完全忽略了这个问题。
所以问题:
- 为什么会这样?
- 我错过了什么吗?
-
x => Some(x+2)是None的有效参数还是应该只接受返回None的函数? - 如何纠正?
- 我是否也应该考虑
NaN(这也可以扩展除以零和其他奇怪情况(如undefined + 2)的可能效率?
编辑
- 这种异常似乎也出现在面向函数的语言中。我尝试使用原生
Option类型在 Scala here 中实现类似的案例。
【问题讨论】:
-
您是否使用
null/undefined值来编码None大小写?您应该使用显式标志并忽略该值,以便将chain实现为return this._hasValue ? fn(this._value) : this。 -
请发布您的完整代码,包括您如何实现
of、lift和None案例 -
"
x => Some(x+2)是chain的有效参数吗?" - 是的,当然,它的类型可能是int -> Maybe<int>。但是您的chain方法需要适用于任意 类型,例如double -> Maybe<string>(其中双精度值可以是NaN)或null -> Maybe<object>。 -
谢谢,我用Maybe Monad的完整实现编辑了这篇文章。
-
Maybe.of必须是Some才能不违反法律。当然,处理 Maybes 既可以处理某些事情,也可以处理任何事情,但构造始终是明确的。of表示“包装这个值”。None不包含任何内容。您可能希望实现类似Maybe.fromNullable的东西作为辅助方法,但这与 monad 无关。
标签: typescript functional-programming monads