首页 > 解决方案 > TeletypeIO 的 StateMonad 实例

问题描述

所以,我有这个数据类型(它来自这里:https ://wiki.haskell.org/IO_Semantics ):

data IO a = Done a
      | PutChar Char (IO a)
      | GetChar (Char -> IO a)

我想为它写一个 StateMonad 实例。我已经为它编写了 Monad 和 Applicative 实例。

instance MonadState (IO s) where
  get    = GetChar (\c -> Done c)
  put    = PutChar c (Done ())
  state f = Done (f s)

我不认为我完全理解在这里应该做什么状态(之前被命名为“修改”)。

state :: (s -> (a, s)) -> m a

我也搞砸了声明。我真的不明白出了什么问题,更不用说如何解决了。感谢您的帮助。

Expecting one more argument to ‘MonadState (IO s)’
Expected a constraint,
but ‘MonadState (IO s)’ has kind ‘(* -> *) -> Constraint’
In the instance declaration for ‘MonadState (IO s)’

标签: haskelliomonadssemanticsstate-monad

解决方案


正如我在评论中提到的,您的类型实际上并不持有任何状态,因此StateMonad实例对它来说是荒谬的。

但是,由于这只是一个练习(也基于评论),我想从技术上实现该实例是可以的,即使它没有按照您的预期执行。

首先,你得到的编译器错误告诉你这个MonadState类实际上有两个参数——状态的类型和 monad 的类型,其中 monad 必须有 kind * -> *,也就是说,有一个类型参数,likeMaybe或 list , 或Identity.

在您的情况下,有问题的单子(不是真正的单子,但可以)是IO,并且您的“状态”的类型是Char,因为这就是您要注意getput。所以声明必须如下所示:

instance MonadState Char IO where

其次,state方法没有(s -> s) -> m s您声称的签名,而是(s -> (a, s)) -> m a. 见其定义。它应该做的是用一个函数在 monad 中创建一个计算m,该函数接受一个状态并返回“结果”加上新的(更新的)状态。

另请注意,这是对 State monad 最一般的操作,两者getput都可以表示为state

get = state $ \s -> (s, s)
put s = state $ \_ -> ((), s)

get这意味着您不必put自己实施。您只需要实现该state功能,并且get/put将来自默认实现。

顺便说一句,这也适用于另一种方式:如果您定义getand put,则定义 ofstate将来自默认值:

state f = do
    s <- get
    let (a, s') = f s
    put s'
    return a

现在,让我们看看这实际上是如何实现的。

's 参数的语义state是这样的:它是一个函数,它以某个状态为输入,然后根据该状态进行一些计算,这个计算有一些结果a,它也可能以某种方式修改状态;所以函数返回结果和新的修改状态。

在您的情况下,从“monad”“获取”状态GetChar的方式是 via ,而“返回”的Char方式是通过调用您传递给它的函数(这种函数通常称为“延续”)。

将状态“放入”回“monad”的方法是 via PutChar,它将Char您想要“放入”的参数作为参数,加上一些IO a代表计算“结果”的参数。

因此,实现的方法state是(1)首先“获取” Char,然后(2)将函数应用于它,然后(3)“放置”结果 new Char,然后(3)返回“结果”功能。把它们放在一起:

state f = GetChar $ \c -> let (a, c') = f c in PutChar c' (Done a)

作为进一步的练习,我鼓励你从我上面给出的定义开始,看看如何get以及put将如何展开,并逐步进行替换。在这里,我将为您提供几个初步步骤:

get = state $ \s -> (s, s)
    -- Substituting definition of `state`
    = GetChar $ \c -> let (a, c') = (\s -> (s, s)) c in PutChar c' (Done a)
    -- Substituting (\s -> (s, s)) c == (c, c)
    = GetChar $ \c -> let (a, c') = (c, c) in PutChar c' (Done a)
    = <and so on...>

推荐阅读