Javascript 为什么是单子';s bind()方法必须返回一个func,然后返回一个Monad?
有谁能告诉我中Javascript 为什么是单子';s bind()方法必须返回一个func,然后返回一个Monad?,javascript,functional-programming,Javascript,Functional Programming,有谁能告诉我中map/fmap(由Functor定义)和flatMap/bind/lift(由Monads定义)之间的区别吗?前者接受函数并返回单子,后者接受函数并返回函数,然后函数也返回单子。问题是为什么后者需要采取额外的步骤来返回单子 // Monads apply a function that returns a wrapped values to a wrapped value // and then return a wrapped value // Monads are chain
map/fmap
(由Functor定义)和flatMap/bind/lift
(由Monads定义)之间的区别吗?前者接受函数并返回单子,后者接受函数并返回函数,然后函数也返回单子。问题是为什么后者需要采取额外的步骤来返回单子
// Monads apply a function that returns a wrapped values to a wrapped value
// and then return a wrapped value
// Monads are chainable
class Wrapper {
constructor(val) {
console.log('Created a wrapped value ' + val);
this.val = val;
}
// Functor's `fmap` in Haskell
map(func) {
return new Wrapper(func(this.val));
}
// Monad's `>>=` (pronounced bind) in Haskell
flatMap(func) {
return func(this.val);
}
// Monad's `return` in Haskell
static of(val) {
return new Wrapper(val);
}
}
// Monad
console.log(Wrapper.of([1,2,3]).flatMap(function(val) {val.push(4);return Wrapper.of(val);})); //=> [1, 2, 3, 4]
// Functor
console.log(Wrapper.of([1,2,3]).map(function(val) {val.push(4); return val;})); //=> [1, 2, 3, 4]
因为它可以生成优雅方便的代码。例如,您有多个操作可以返回
MyObject
或null
(当然,在monad中,可能是选项
左右)。您可以轻松地将它们链接在一起,而无需实际检查它们是否返回null。如果任何操作返回null,那么所有其他操作都不会执行任何操作
//I assume Option is a kind of Wrapper
function canReturnNull(myVal) {
//something that can return Option(myChangedVal) or Option(null)
}
var result = Option(myObj).flatMap(canReturnNull1).flatMap(canReturnNull2).flatMap(canReturnNull3);
如果它返回一个值而不是一个monad,我就不能像那样将操作链接在一起
当然,对于monad选项来说是这样的。我建议阅读Haskell教程中的各种单子。
fmap/map
,在Haskell中通常定义为(a->b)->fa->fb
,在js中通常更像fa->(a->b)->fb
,期望提供的函数只转换单子中的值,结果单子由fmap
创建
bind/flatMap/chain
,具有fa->(a->fb)->fb的签名,具有更大的自由度---它可以返回超出Monad.of(singleValue)
所能表达的一元值
例如,List
monad有一个fmap
方法来返回一个长度相同的List
,其值由提供的函数转换,以便:
fmap (1 +) [1, 2, 3] == [2, 3, 4]
但是如果你想要一个不同长度的列表怎么办?使用fmap
是不可能的,因为fmap
总是返回相同长度的列表<代码>绑定
解决了这个问题
Prelude> [1..3] >>= \x -> [x..x+2]
[1,2,3,2,3,4,3,4,5]
bind
,或flatMap
,因为List
s通常被定义为将生成的列表
s串联在一起,这样您就可以返回一个列表
,表示“我希望该元素变为零,或者在生成的列表中变为2个或更多元素”您的示例:
// Monad
console.log(Wrapper.of([1,2,3]).flatMap(function(val) {val.push(4);return Wrapper.of(val);})); //=> [1, 2, 3, 4]
// Functor
console.log(Wrapper.of([1,2,3]).map(function(val) {val.push(4); return val;})); //=> [1, 2, 3, 4]
它过于简化了。如果你有一个a->b
类型的函数,就像你所做的(.push(4)::Array->Array
),把它转换成a->mb
(这是返回
),或者像你的地图那样直接把它提升到ma->mb
然而,我们正在研究一个案例,其中您的包装器是一个标识单子。考虑实际<代码>可能< /代码>:
divBy2 :: Int -> Maybe Int
divBy2 a = if a `mod` 2 == 0 then Just a `div` 2 else Nothing
此函数非常清楚地表达了中的计算。如果您有>=
,则可以轻松链接它:
pure 8 >>= divBy2 >>= divBy2
但是,如果您只有fmap
,就没有办法链接这样的函数!(不使用类似于join
,这是根据>>=
定义的)
具体地说,如果您有一个值5::Int
和一个函数Int->Maybe Int
,您可以将它输入到该函数中。但是一旦你完成了,并且有了一个Maybe Int
,你就需要从Maybe
中提取这个值,然后将它输入到下一个函数。这正是>=
正在做的事情
返回实际一元计算的函数非常强大,这就是它们比函子的优势所在。原因有很多:有时甚至不可能返回值。以JS承诺为例,假设bind
wasthen
。如果在中执行异步操作,则的回调不能返回值,但可以返回承诺
在大多数具有静态类型的语言中,回调不可能同时返回同步值或承诺。因此,这些语言中的承诺将有两种方法:fmap
仅允许将同步函数应用于承诺包装,bind
仅异步。请在问题中编写相关代码。所以甚至可以让你编写可执行的代码片段。@DenysSéguret我根据你的要求编辑了这篇文章!顺便说一句,我们称之为flatMap
不返回函数。它需要一个函数。返回值不正确。它是Wrapper{val:[1,2,3,4]}
实际上,但是Functor
也是可链接的。如果您查看该代码段,我有两个方法。它们都返回Monad类型,并且可以链接。在Javascript中,它们可以以相同的方式使用,因为它不是静态类型。而且,你的单子什么也没做。某些单子允许你做不同的事情。例如,选项/Maybe monad允许您链接可以返回null的操作,而无需显式检查它。单子是函子的一种子类,真的。Tks@ralh是你的答案。正如你所指出的,我确实理解某种单子。我只是对本例中的Monadbind()
方法感到困惑,因为它需要额外的步骤来返回Monad。试图弄明白为什么需要这样的实现。@babygau:不,它不“需要额外的步骤”。它允许你有一个额外的步骤。如果您的函数已经返回了一元类型(出于某种原因),则不能使用map
——您需要使用flatMap
。有些数据类型具有此功能,它们是单子,而其他数据类型则没有-它们只是函子。@Bergi,谢谢。我喜欢你的回答。Monads允许我有一个额外的工具来处理数据。谢谢你,在阅读了Haskell的一本书之后,我发现你的答案是最相关的!