我熟悉类别理论中的monad(实际上它们是一个非常简单的概念),但Haskell中的>>=
函数完全让我困惑。好的,所以将bind应用于值M a
和函数a -> M u
与首先将monad应用于此函数,然后在指定值处对其进行评估并将结果相乘:{{1} }与a >>= f
相同。但这是如何自然地描述计算?是否有一些有用的方法可以帮助我理解它?
是否有一些好的文章不面向C ++丛林中的新人?
答案 0 :(得分:8)
考虑monadic函数组合运算符<=<
。这类似于.
,除了它适用于monadic函数。它可以简单地用>>=
来定义,所以学习一个会教会我们另一个。
(<=<) :: (a -> m b) -> (b -> m c) -> a -> m c
(f <=< g) x = g x >>= f
(.) :: (a -> b) -> (b -> c) -> a -> c
(f . g) x = g x |> f
where z |> h = h z
如果是.
,则g
首先“执行”,然后f
的输出执行g
。对于<=<
,g
及其效果首先“执行”,然后执行f
并执行其效果。说实际上,在“另一个”之前发生一个事实,因为并非所有的monad都以这种方式工作,这有点用词不当。
或许更准确地说f
可以利用g
提供的其他背景信息。但 并非完全正确,因为g
可能带走上下文信息。如果你想100%正确地描述monad,你真的必须走在蛋壳上。
但在几乎所有非常重要的情况下,f <=< g
表示monadic函数g
的效果(以及结果)将随后影响monadic函数f
的行为。
解决有关v >>= f = join (fmap f v)
考虑f :: a -> m b
和v :: m a
。这对fmap f v
意味着什么?那么fmap :: (c -> d) -> m c -> m d
,在这种情况下是c = a
和d = m b
,所以fmap f :: m a -> m (m b)
。现在,我们当然可以将v :: m a
应用于此函数,从而生成m (m b)
。但完全的结果类型m (m b)
是什么意思?
inner m
表示来自f
的上下文。 外部 m
表示源自v
的上下文(n.b。fmap
不应该干扰此原始上下文)。
然后你join
m (m b)
,将这两个背景粉碎成m a
。这是Monad
定义的核心:你必须提供一种将背景粉碎在一起的方法。您可以检查各种Monad
实例的实现细节,以尝试了解它们如何“粉碎”上下文。然而,这里的内容是,“内部上下文”在您将其与“外部上下文”合并之前是不可观察的。如果您使用v >>= f
,那么函数f
没有实际概念接收纯值a
并生成简单的monadic结果m b
。相反,我们理解f
在v
的原始上下文中执行。
答案 1 :(得分:6)
嗯。我认为考虑它的好方法是>>=
允许你撰写计算;计算本身的格式为a -> m b
。因此m b
只代表计算的结果。
因此计算只需要一些值并产生一些结果。这里的一个很好的例子是列表类型:a -> [b]
表示非确定性计算。它需要一个输入,但可以产生多个结果。就其本身而言,a -> [b]
作为计算是有意义的。但是你会如何结合这些呢?自然的答案是,您将在之前结果的所有上执行每个连续的“计算”。这正是>>=
对列表的作用。
真正帮助我看到其实用价值的一件事是考虑DFA和NFA。你可以想象在Haskell中写一个像DFA这样的DFA:
data State = S1 | S2 | S3 | S4 | Q
data Input = A | B
transition :: State -> Input -> State
transition S1 A = S2
transition S1 B = S3
-- and so on...
然后我们可以折叠输入:
foldl transition S1 [A, A, B, B]
现在,我们如何使用此代码并将其推广到NFA?那么,NFA的过渡“功能”可以被认为是非确定性计算。所以我们定义类似的东西:
transition S1 A = [S1, S2]
transition S1 B = []
但是现在我们必须做一些奇怪的体操才能使用foldl
!令人高兴的是,我们可以foldM
而不是{{1}}。所以这里由monad建模的“计算”是非确定性转换函数。
答案 2 :(得分:3)
从计算的角度来看,=<<
可能更容易理解(它只是flip (>>=)
)。它输入(=<<) :: (Monad m) => (a -> m b) -> m a -> m b
,对应于函数应用程序的类型,参见($) :: (a -> b) -> a -> b
。所以>>=
只是在monadic级别上翻转了函数应用程序。
此外,(>>=)
用于描述do
符号,do
在语法上非常符合命令式代码(在合适的monad中)。
答案 3 :(得分:3)
以下是关于它如何作为计算模型的粗略概念:具有M
实例的类型构造函数Monad
表示参数化数据结构,以及该结构的非参数部分可以包含其他信息。 return
和join
对应于结构的那些部分的某种幺半群。函数a -> M b
基于类型a
的输入在该结构中引入信息。因此,通过将函数a -> M b
提升为M a -> M b
,我们使用M
的参数信息来创建一些非参数信息,然后将其与已存在于类型值中的信息相结合M a
。
a -> M b
类型的非对称性质给出了非参数信息流的固有方向,而相关性要求意味着整体秩序是唯一重要的。
最终结果是通过某种具有内在因果概念的上下文来增强函数。