TeletypeIO 的 StateMonad 实例

StateMonad instance for TeletypeIO

所以,我有这个数据类型(来自这里: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)

我不认为我完全理解状态(它之前被命名为 'modify')在这里应该做什么。

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)’

MonadState 接受两个参数,顺序为:

  1. 状态类型

  2. 单子

在这种情况下,monad 是 IO:

instance MonadState _ IO where
  ...

而且你需要弄清楚用什么代替下划线

正如我在评论中提到的,您的类型实际上并没有任何状态,因此 StateMonad 实例对它来说毫无意义。

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

首先, 你得到的编译器错误告诉你 MonadState class 实际上需要 两个 arguments - state 的类型和 monad 的类型,其中 monad 必须有 kind * -> *,也就是说,有一个类型参数,比如 Maybe,或者 list,或者 Identity.

在你的例子中,所讨论的 monad(不是真正的 monad,但还可以)是 IO,而你的 "state" 的类型是 Char,因为这就是你正在 getting 和 putting。所以声明必须如下所示:

instance MonadState Char IO where

其次, state 方法没有您声称的签名 (s -> s) -> m s,而是 (s -> (a, s)) -> m a。参见 its definition。它应该做的是在 monad m 中从一个函数中创建一个计算,该函数接受一个状态和 returns "result" 加上新的(更新的)状态。

另请注意,这是 State monad 上最通用的操作,getput 都可以用 state:

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

这意味着您不必自己实施 getput。您只需要实现 state 函数, get/put 将来自默认实现。

顺便说一句,这也适用于其他方式:如果您定义 getputstate 的定义将来自默认值:

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

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

state参数的语义是这样的:它是一个函数,将一些状态作为输入,然后根据该状态执行一些计算,这个计算有一些结果a ,它也可能以某种方式修改状态;所以函数 return 既是结果又是新的、修改后的状态。

在你的情况下,从你的 "monad" 到 "get" 状态的方式是通过 GetChar,而其中的方式是 "returns" Char 是通过调用您传递给它的函数(此类函数通常称为 "continuation")。

将 "put" 状态返回到您的 "monad" 的方法是通过 PutChar,它将您想要 "put" 的 Char 作为参数,加上一些 IO a 代表计算 "result".

因此,实现 state 的方法是 (1) 首先 "get" Char,然后 (2) 对其应用函数,然后 (3) "put" 生成新的 Char,然后 (3) return 函数的 "result"。把它们放在一起:

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

作为进一步的练习,我鼓励你看看 getput 将如何展开,从我上面给出的定义开始,并执行步骤- 逐步替换。在这里,我将向您介绍几个第一步:

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...>