这是 Haskell 中众所周知的 >>= 运算符的签名
>>= :: Monad m => m a -> (a -> m b) -> m b
问题是为什么函数的类型是
(a -> m b)
代替
(a -> b)
我想说后一个更实用,因为它允许在定义的 monad 中直接集成现有的“纯”函数。
相反,写一个通用的“适配器”似乎并不难
adapt :: (Monad m) => (a -> b) -> (a -> m b)
但无论如何,我认为您已经拥有
(a -> b)
的可能性更大。而不是 (a -> m b)
.笔记。 我解释了我所说的“实际”和“可能”是什么意思。
如果您还没有在程序中定义任何 monad,那么,您拥有的函数是“纯”
(a -> b)
您将拥有 (a -> m b)
类型的 0 个函数只是因为你还没有定义m
.如果你决定定义一个 monad m
它需要有新的a -> m b
定义的功能。
最佳答案
原因是(>>=)
更一般。您建议的功能称为 liftM
并且可以很容易地定义为
liftM :: (Monad m) => (a -> b) -> (m a -> m b)
liftM f k = k >>= return . f
这个概念有自己的类型类,称为
Functor
与 fmap :: (Functor m) => (a -> b) -> (m a -> m b)
.每Monad
也是 Functor
与 fmap = liftM
,但由于历史原因,这不是( yet )在类型类层次结构中捕获。和
adapt
你建议可以定义为adapt :: (Monad m) => (a -> b) -> (a -> m b)
adapt f = return . f
请注意,有
adapt
相当于拥有 return
如return
可以定义为adapt id
.所以任何有
>>=
也可以同时具备这两个功能,但反之则不行。 There are structures that are Functors
but not Monads
.这种差异背后的直觉很简单:monad 中的计算可以依赖于先前 monad 的结果。重要的是
(a -> m b)
这意味着不仅仅是b
,也是它的“效果”m b
可以依赖 a
.例如,我们可以有import Control.Monad
mIfThenElse :: (Monad m) => m Bool -> m a -> m a -> m a
mIfThenElse p t f = p >>= \x -> if x then t else f
但是不能只用
Functor m
来定义这个函数约束,仅使用 fmap
.仿函数只允许我们改变“内部”的值,但我们不能把它“拿出”来决定采取什么行动。
关于haskell - 关于 >>= Monad 运算符的签名,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21221705/