为什么使用 `join` 的函数组合可以改变函数输入?

Why can function composition with `join` change function inputs?

我最近开始学习 Haskell,我正在尝试进行以下函数组合 (join . mapM),但是从这个函数中得到了一些我不理解的奇怪类型。我认为 GHC 会假设 mapM 类型中的 t == mmapM 的输出将变为 m (m b) ,这将是可连接的,或者它不会,这将由于类型不匹配而出错。相反,发生了以下情况:

mapM :: (Traversable t, Monad m) => (a -> m b) -> t a -> m (t b)
join :: Monad m => m (m a) -> m a
join . mapM :: Traversable t => (a -> t a -> b) -> t a -> t b

我不明白这怎么可能。按照我的理解,函数组合应该使用第一个(或第二个取决于你如何看待它)函数的输入和第二个函数的输出。但是这里 mapM 的预期输入函数从一元函数变为二元函数,我不知道为什么。即使 GHC 不能像我所做的那样假设 t == m,这是我一半预料的,它应该因为类型不匹配而出错,而不是更改输入函数类型,对吧?这里发生了什么?

首先你将 mapM 专精于:

mapM' :: Traversable t => (a -> x -> b) -> t a -> x -> t b

(因为 (->) x 是一个 monad)

然后您将其进一步专门化为:

mapM'' :: Traversable t => (a -> t a -> b) -> t a -> t a -> t b

(我们只是将 x 固定为 t a

最后我们适当地专门化join

join' :: (x -> x -> r) -> x -> r

(同样,(->) x 是一个 monad)

希望成分 join' . mapM''

的原因变得更加明显
join' . mapM'' :: Traversable t => (a -> t a -> b) -> t a -> t b

也许以下内容会更有启发性:

flip mapT :: (Traversable t, Monad m) => t a -> (a -> m b) -> t (m b)
sequenceA :: (Traversable t, Monad m) =>                      t (m b) -> m (t b)
flip mapM :: (Traversable t, Monad m) => t a -> (a -> m b)            -> m (t b)

           flip liftM :: Monad m      => m a -> (a -> m b) -> m (m b)
 join                 :: Monad m      =>                      m (m b) -> m    b
(join .) . flip liftM :: Monad m      => m a -> (a -> m b)            -> m    b
(>>=)                 :: Monad m      => m a -> (a -> m b)            -> m    b

(在这里和那里使用一些比最通用的类​​型更专业的类型;还有 renamed mapT f = runIdentity . traverse (Identity . f))。

您的具体问题不太有趣。类型推导是一个完全机械的过程。某些实体必须兼容才能使整个表达式有意义,因此它们的类型必须统一:

 (join . mapM) a_mb  x  =       -- a_mb :: a -> m b
 = join (mapM  a_mb) x
 = join   ta_mtb     x          -- ta_mtb :: t a -> m (t b)

加入一个函数就是调用它两次,

 =        ta_mtb     x  x

这意味着 xt a 所以 mt a ->

        x :: t a 
 ta_mtb   :: t a -> m   (t b)
 ----------------------------
 ta_mtb x ::        m   (t b)
          ~       t a -> t b
          x ::    t a 
 ----------------------------
 ta_mtb x x ::           t b

因此a_mb :: a -> m b ~ a -> t a -> b.