Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/427.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 为什么是单子';s bind()方法必须返回一个func,然后返回一个Monad?_Javascript_Functional Programming - Fatal编程技术网

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
was
then
。如果在
中执行异步操作,则
的回调不能返回值,但可以返回承诺


在大多数具有静态类型的语言中,回调不可能同时返回同步值或承诺。因此,这些语言中的承诺将有两种方法:
fmap
仅允许将同步函数应用于承诺包装,
bind
仅异步。

请在问题中编写相关代码。所以甚至可以让你编写可执行的代码片段。@DenysSéguret我根据你的要求编辑了这篇文章!顺便说一句,我们称之为
flatMap
不返回函数。它需要一个函数。返回值不正确。它是
Wrapper{val:[1,2,3,4]}
实际上,但是
Functor
也是可链接的。如果您查看该代码段,我有两个方法。它们都返回Monad类型,并且可以链接。在Javascript中,它们可以以相同的方式使用,因为它不是静态类型。而且,你的单子什么也没做。某些单子允许你做不同的事情。例如,选项/Maybe monad允许您链接可以返回null的操作,而无需显式检查它。单子是函子的一种子类,真的。Tks@ralh是你的答案。正如你所指出的,我确实理解某种单子。我只是对本例中的Monad
bind()
方法感到困惑,因为它需要额外的步骤来返回Monad。试图弄明白为什么需要这样的实现。@babygau:不,它不“需要额外的步骤”。它允许你有一个额外的步骤。如果您的函数已经返回了一元类型(出于某种原因),则不能使用
map
——您需要使用
flatMap
。有些数据类型具有此功能,它们是单子,而其他数据类型则没有-它们只是函子。@Bergi,谢谢。我喜欢你的回答。Monads允许我有一个额外的工具来处理数据。谢谢你,在阅读了Haskell的一本书之后,我发现你的答案是最相关的!