Haskell 单子连接函数

Haskell 单子连接函数,haskell,monads,Haskell,Monads,虽然monad在Haskell中使用bind和return函数表示,但它们也可以使用join函数表示,例如。我知道这个函数的类型是M(M(X))->M(X),但这实际上是做什么的呢?从同一个页面中,我们恢复这个信息join X=X>=id,了解bind和id函数是如何工作的,你应该能够弄清楚join的功能。它在概念上是做什么的,只需查看类型即可确定:它展开或展平外部一元容器/计算,并返回其中生成的一元值 它是如何做到这一点的,取决于你所处理的单子的类型。例如,对于列表monad,“join”相当

虽然monad在Haskell中使用bind和return函数表示,但它们也可以使用join函数表示,例如。我知道这个函数的类型是M(M(X))->M(X),但这实际上是做什么的呢?

从同一个页面中,我们恢复这个信息
join X=X>=id
,了解
bind
id
函数是如何工作的,你应该能够弄清楚
join
的功能。

它在概念上是做什么的,只需查看类型即可确定:它展开或展平外部一元容器/计算,并返回其中生成的一元值


它是如何做到这一点的,取决于你所处理的单子的类型。例如,对于列表monad,“join”相当于

实际上,在某种程度上,
join
是所有魔法真正发生的地方--
(>>=)
主要用于方便

所有基于
Functor
的类型类都使用某种类型来描述附加结构。对于
Functor
这个额外的结构通常被认为是一个“容器”,而对于
Monad
它往往被认为是“副作用”,但这些只是(偶尔会产生误导性的)简写——无论哪种方式都是一样的,实际上没有什么特别的[0]

与其他
Functor
s相比,
Monad
的显著特点是它可以将控制流嵌入到额外的结构中。它可以这样做的原因是,与在整个结构上应用单一平坦函数的
fmap
不同,
(>=)
检查单个元素并从中构建新结构

使用普通的
函子
,从原始结构的每一部分构建新结构将替代嵌套
函子
,每一层代表一个控制流点。这显然限制了实用性,因为结果是混乱的,并且具有反映所用流量控制结构的类型

一元“副作用”是具有一些额外性质的结构

  • 两种副作用可以组合为一种(例如,“do X”和“do Y”变为“do X,然后Y”),只要效果顺序保持不变,组合顺序就无关紧要
  • 存在“无所事事”的副作用(例如,“无所事事”和“无所事事”分组与“无所事事”分组相同)
join
函数只不过是一种分组操作:一种嵌套的monad类型,如
m(ma)
描述了两种副作用及其出现的顺序,
join
将它们组合成一种副作用

因此,就一元副作用而言,绑定操作是“获取一个具有相关副作用的值和一个引入新副作用的函数,然后将该函数应用于该值,同时组合每个副作用”的缩写

[0]:除了
IO
<代码>IO非常特殊

[1]:如果你将这些属性与
幺半群的一个实例的规则进行比较,你会发现两者之间有着密切的相似之处——这不是巧合,事实上这就是“只是内函子范畴中的一个幺半群,有什么问题?”这行是指。

到目前为止,我认为其他答案已经充分描述了join的作用。如果你在寻找一个更直观的理解…如果你想知道join“意味着什么”…那么不幸的是,答案会根据所讨论的monad而有所不同,特别是M(X)“意味着什么”和M(M(X))“意味着什么”

如果M是列表单子,那么M(M(X))是列表列表,join表示“展平”。如果M是Maybe monad,那么M(M(X))的一个元素可以是“Just(Just X)”、“Just Nothing”或“Nothing”,并且join意味着以逻辑方式将这些结构分别折叠为“Just X”、“Nothing”和“Nothing”(类似于camccann关于join作为组合副作用的回答)

对于更复杂的单子,M(M(X))成为一个非常抽象的东西,决定M(M(X))和连接的“平均值”变得更加复杂。在每一种情况下,它都有点像列表单子的情况,即你将两层单子抽象分解为一层,但意义会有所不同。对于状态单子,camccann关于组合两个副作用的回答是“砰”的一声:连接本质上意味着组合两个连续的状态转换。延续单元组尤其令人头疼,但数学上的连接在这里实际上相当简洁:M(X)对应于X的“双对偶空间”,数学家可以将其写成
X**
(延续本身,即X->R的映射,其中R是一组最终结果,对应于单对偶空间
X*
),join对应于从
X****
X**
的极其自然的映射。延拓单子满足单子定律的事实与数学事实相对应,即通常不太可能将对偶空间运算符
*
应用两次以上

但我离题了


就我个人而言,我试图抵制将单一类比应用于所有可能的单子类型的冲动;单子是一个过于笼统的概念,不能用单一的描述性类比来归类。join的含义将根据您在任何给定时间使用的类比而有所不同。

绑定操作映射:
ma->(a->mb)->mb
。在
ma
和(第一个)
mb
中,我们有两个
m
s。依我的直觉,理解绑定和一元操作在很大程度上取决于理解绑定和一元操作,以及这两个
m
s(一元上下文的实例)如何结合。我喜欢把Writer monad看作是理解
join
的一个例子。Writer可用于记录操作
ma
中有一个日志<代码>(a->mb)<