理解Haskell中的绑定函数

Ale*_*nko 10 monads haskell

我熟悉类别理论中的monad(实际上它们是一个非常简单的概念),但>>=Haskell中的函数完全让我困惑.好的,所以将bind应用于一个M a和一个函数的值与a -> M u首先将monad应用于此函数,然后在指定的值上进行评估并将结果相乘:a >>= f是相同的join $ (fmap f) $ a.但这是如何自然地描述计算?是否有一些有用的方法可以帮助我理解它?

在某个地方是否有一些适合C++丛林新鲜事物的文章?

Dan*_*ton 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
Run Code Online (Sandbox Code Playgroud)

在这种情况下.,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 bv :: m a.这对我意味着fmap f v什么?那么fmap :: (c -> d) -> m c -> m d,在这种情况下c = ad = m b,所以fmap f :: m a -> m (m b).现在,当然,我们可以应用于v :: m a此功能,从而产生m (m b).但这种结果类型到底m (m b)意味着什么呢?

所述 m表示从产生的上下文f.所述 m表示上下文源自v(NB fmap不应干扰该原始上下文).

然后你join那个m (m b),将这两个背景粉碎在一起m a.这是定义的核心Monad:你必须提供一种粉碎上下文的方法.您可以检查各种Monad实例的实现细节,以尝试理解它们如何"粉碎"上下文.然而,这里的内容是,"内部上下文"在您将其与"外部上下文"合并之前是不可观察的.如果你使用v >>= f,那么就没有函数接收纯值并产生简单的monadic结果的实际概念.相反,我们理解原始语境中的行为.fam bfv


Tik*_*vis 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...
Run Code Online (Sandbox Code Playgroud)

然后我们可以折叠输入:

 foldl transition S1 [A, A, B, B]
Run Code Online (Sandbox Code Playgroud)

现在,我们如何使用此代码并将其推广到NFA?那么,NFA的过渡"功能"可以被认为是非确定性计算.所以我们定义如下:

transition S1 A = [S1, S2]
transition S1 B = []
Run Code Online (Sandbox Code Playgroud)

但是现在我们必须做一些奇怪的体操才能使用foldl!幸运的是,我们可以foldM改为.所以这里由monad建模的"计算"是非确定性转换函数.