为什么允许在带有两个 monad 值的函数上使用 `ap`,而不是在 monad 中包装的函数上使用?

Ed *_*Lim 4 monads haskell

这是哈斯克尔代码:

zipWith compare `ap` tail
Run Code Online (Sandbox Code Playgroud)

在哪里

zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
compare :: a -> a -> Ordering 
ap :: Monad m => m (a -> b) -> m a -> m b
Run Code Online (Sandbox Code Playgroud)

因此,根据我的理解,ap在这种情况下将需要一个包装在列表单子内的函数,即它的 need m (a -> b),这应该是由zipWith compare但我不知道如何实现的,因为zipWith compare将采用[a] -> [a] -> [Ordering]不太正确的类型。列表 monad 的幕后是否发生了一些导致必要的类型转换的事情?

该代码有效,不是我自己的作品,我只是想了解它是如何被允许的。

MMZ*_*526 8

这里的技巧是这a ->是一个 Monad!

\n

事实上,我们可以在普通的 ghci 会话中列出 Monad 的实例:

\n
ghci> :i Monad\ntype Monad :: (* -> *) -> Constraint\nclass Applicative m => Monad m where\n  (>>=) :: m a -> (a -> m b) -> m b\n  (>>) :: m a -> m b -> m b\n  return :: a -> m a\n  {-# MINIMAL (>>=) #-}\n    -- Defined in \xe2\x80\x98GHC.Base\xe2\x80\x99\ninstance Monoid a => Monad ((,) a) -- Defined in \xe2\x80\x98GHC.Base\xe2\x80\x99\ninstance (Monoid a, Monoid b) => Monad ((,,) a b)\n  -- Defined in \xe2\x80\x98GHC.Base\xe2\x80\x99\ninstance (Monoid a, Monoid b, Monoid c) => Monad ((,,,) a b c)\n  -- Defined in \xe2\x80\x98GHC.Base\xe2\x80\x99\ninstance Monad ((->) r) -- Defined in \xe2\x80\x98GHC.Base\xe2\x80\x99 <- This is the one!\ninstance Monad IO -- Defined in \xe2\x80\x98GHC.Base\xe2\x80\x99\ninstance Monad Maybe -- Defined in \xe2\x80\x98GHC.Base\xe2\x80\x99\ninstance Monad Solo -- Defined in \xe2\x80\x98GHC.Base\xe2\x80\x99\ninstance Monad [] -- Defined in \xe2\x80\x98GHC.Base\xe2\x80\x99\ninstance Monad (Either e) -- Defined in \xe2\x80\x98Data.Either\xe2\x80\x99\n
Run Code Online (Sandbox Code Playgroud)\n

因此,zipWith compare :: m ([a] -> Ordering)tail :: m [a]是,m或。((->) [a])([a] ->)

\n

这里的技巧是,函数箭头->确实是一个构建新类型的类型构造函数。一旦解决了这个问题,您就可以查看其 Monad 实例的实际实现(这并没有什么出乎意料的)。

\n

事实上,对于ap(我没有仔细检查,但我很有信心),它看起来像这样:

\n
ap :: (r -> (a -> b)) -- this is m (a -> b)\n   -> (r -> a)        -- this is m a\n   -> (r -> b)        -- this is m b\nap rab ra\n  = \\r ->             -- the result must be a function starting from r\n      (rab r)         -- gives (a -> b)\n      (ra r)          -- gives a, thus the body of the lambda gives b\n
Run Code Online (Sandbox Code Playgroud)\n

这也是SKI中的S-combinator。

\n