在 Haskell 的绑定运算符 (>>=) 中使用 return

Using return in Haskell's bind operator (>>=)

这是>>=的类型:

(>>=) :: Monad m => m a -> (a -> m b) -> m b

它需要一个函数作为第二个参数。

这里是 return 的类型:

return :: Monad m => a -> m a

Returns m a

这显然是类型检查:

(>>) :: Monad m => m a -> m b -> m b
x >> y = x >>= (\_ -> y)

但是为什么下面的类型检查和工作类似于上面的代码?

(>>) :: Monad m => m a -> m b -> m b
x >> y = x >>= return y

这里 return y 应该是 m a 类型而不是 a -> m a。那么它为什么有效?

函数有一个 monad 实例,它的 returnreturn x = \_ -> x(或等效的 return = const)。

因此,当您在需要函数的地方执行 return y 时,它只是选择函数 monad 的 return

你实际上在这里混合了两个不同的单子,这就是正在发生的事情。 x >>= return y 在这种情况下统一为

(>>) :: ∀ m a b . Monad m => m a -> m b -> m b
x >> y = x >>= (return :: m b -> a -> m b) y
       -- aka  return :: (m b) -> (a->) (m b)

其中 returnMonad (a->) 实例中实现:

instance Monad (->) a where
  return x = \_ -> x
  ...

它与 Monad m 实例没有任何关系。

至于为什么这个return在函数monad中运行:return :: m b -> a -> m b是在编译器开始推理类型类实例之前从环境中推断出来的.现在,类型 m b -> a -> m b,即 m b -> (a->m b),具有 mb -> amb 的形式。因此,签名 return :: Monad μ => α -> μ α 使编译器匹配 μ α ~ amb ~ a->m b。只有在这一点上,编译器才会真正为 return 选择 monad 实例,它通过观察 a -> m b 确实具有 μ α 的形式来做到这一点,其中 μ ~ (a->)α ~ m b。因此,它必须是 (a->) monad。