这是哈斯克尔代码:
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 的幕后是否发生了一些导致必要的类型转换的事情?
该代码有效,不是我自己的作品,我只是想了解它是如何被允许的。
这里的技巧是这a ->是一个 Monad!
事实上,我们可以在普通的 ghci 会话中列出 Monad 的实例:
\nghci> :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\nRun Code Online (Sandbox Code Playgroud)\n因此,zipWith compare :: m ([a] -> Ordering)和tail :: m [a]是,m或。((->) [a])([a] ->)
这里的技巧是,函数箭头->确实是一个构建新类型的类型构造函数。一旦解决了这个问题,您就可以查看其 Monad 实例的实际实现(这并没有什么出乎意料的)。
事实上,对于ap(我没有仔细检查,但我很有信心),它看起来像这样:
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\nRun Code Online (Sandbox Code Playgroud)\n这也是SKI中的S-combinator。
\n