理解Haskell中的绑定函数

时间:2012-01-31 04:15:17

标签: haskell monads

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

是否有一些好的文章面向C ++丛林中的新人?

4 个答案:

答案 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 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)是什么意思?

inner m表示来自f的上下文。 外部 m表示源自v的上下文(n.b。fmap不应该干扰此原始上下文)。

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

答案 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表示参数化数据结构,以及该结构的非参数部分可以包含其他信息。 returnjoin对应于结构的那些部分的某种幺半群。函数a -> M b基于类型a的输入在该结构中引入信息。因此,通过将函数a -> M b提升为M a -> M b,我们使用M的参数信息来创建一些非参数信息,然后将其与已存在于类型值中的信息相结合M a

a -> M b类型的非对称性质给出了非参数信息流的固有方向,而相关性要求意味着整体秩序是唯一重要的。

最终结果是通过某种具有内在因果概念的上下文来增强函数。