为什么需要“Monad”才能使用“pure”?

pan*_*nic 10 haskell

我正在通过Luis Morillo 的 Haskell 初学者挑战 (snake-fury)学习 Haskell。\n练习 4中的任务之一是“实现Applicative新类型的实例”,其中新类型如下:

\n
newtype GameStep m a = GameStep {runGameStep :: ReaderT BoardInfo (StateT GameState m) a}\n
Run Code Online (Sandbox Code Playgroud)\n

这是我的第一次尝试,但没有编译。

\n
instance (Applicative m) => Applicative (GameStep m) where\n  pure a = GameStep $ pure a\n  (GameStep r) <*> (GameStep s) = GameStep $ r <*> s\n
Run Code Online (Sandbox Code Playgroud)\n

GHC 说

\n
    \xe2\x80\xa2 Could not deduce (Monad m) arising from a use of \xe2\x80\x98pure\xe2\x80\x99\n      from the context: Applicative m\n        bound by the instance declaration at src/GameState.hs:55:10-52\n      Possible fix:\n        add (Monad m) to the context of\n          the type signature for:\n            pure :: forall a. a -> GameStep m a\n          or the instance declaration\n
Run Code Online (Sandbox Code Playgroud)\n

(使用方法相同<*>

\n

如果我按照建议替换(Applicative m) => ...为,它就会编译(Monad m) => ...。但是,我不理解此错误消息,因为pure(以及<*>) 是为 定义的Applicative,而不是为 定义的Monad

\n

为什么Monad需要pure

\n

Fyo*_*kin 13

这是因为你m被包裹在里面StateT(然后在里面ReaderT,但这不会添加任何东西)。

当你写的时候GameStep $ pure a,它pure a不仅必须返回m a,而且ReaderT _ (StateT _ m) a

如果您查看 的文档StateT,您会发现它的Applicative定义为:

instance (Functor m, Monad m) => Applicative (StateT s m)
Run Code Online (Sandbox Code Playgroud)

因此,需要的不是您自己的实例Monad,而是StateT.


现在,原因 StateTMonad,为了实现<*>,它需要执行两个参数,但StateT执行顺序很重要,因为每次执行都可能修改状态。SoStateT的实现<*>必须使用底层Monad的实现>>=来定义参数的执行顺序并在它们之间线程化状态。