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
接受两个参数,顺序为:
状态类型
单子
在这种情况下,monad 是 IO
:
instance MonadState _ IO where
...
而且你需要弄清楚用什么代替下划线
正如我在评论中提到的,您的类型实际上并没有任何状态,因此 StateMonad
实例对它来说毫无意义。
但是,由于这只是一个练习(也是基于评论),我想从技术上实现该实例是可以的,即使它没有按照您的预期执行。
首先, 你得到的编译器错误告诉你 MonadState
class 实际上需要 两个 arguments - state 的类型和 monad 的类型,其中 monad 必须有 kind * -> *
,也就是说,有一个类型参数,比如 Maybe
,或者 list,或者 Identity
.
在你的例子中,所讨论的 monad(不是真正的 monad,但还可以)是 IO
,而你的 "state" 的类型是 Char
,因为这就是你正在 get
ting 和 put
ting。所以声明必须如下所示:
instance MonadState Char IO where
其次, state
方法没有您声称的签名 (s -> s) -> m s
,而是 (s -> (a, s)) -> m a
。参见 its definition。它应该做的是在 monad m
中从一个函数中创建一个计算,该函数接受一个状态和 returns "result" 加上新的(更新的)状态。
另请注意,这是 State monad 上最通用的操作,get
和 put
都可以用 state
:
来表示
get = state $ \s -> (s, s)
put s = state $ \_ -> ((), s)
这意味着您不必自己实施 get
和 put
。您只需要实现 state
函数, get
/put
将来自默认实现。
顺便说一句,这也适用于其他方式:如果您定义 get
和 put
,state
的定义将来自默认值:
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)
作为进一步的练习,我鼓励你看看 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...>
所以,我有这个数据类型(来自这里: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
接受两个参数,顺序为:
状态类型
单子
在这种情况下,monad 是 IO
:
instance MonadState _ IO where
...
而且你需要弄清楚用什么代替下划线
正如我在评论中提到的,您的类型实际上并没有任何状态,因此 StateMonad
实例对它来说毫无意义。
但是,由于这只是一个练习(也是基于评论),我想从技术上实现该实例是可以的,即使它没有按照您的预期执行。
首先, 你得到的编译器错误告诉你 MonadState
class 实际上需要 两个 arguments - state 的类型和 monad 的类型,其中 monad 必须有 kind * -> *
,也就是说,有一个类型参数,比如 Maybe
,或者 list,或者 Identity
.
在你的例子中,所讨论的 monad(不是真正的 monad,但还可以)是 IO
,而你的 "state" 的类型是 Char
,因为这就是你正在 get
ting 和 put
ting。所以声明必须如下所示:
instance MonadState Char IO where
其次, state
方法没有您声称的签名 (s -> s) -> m s
,而是 (s -> (a, s)) -> m a
。参见 its definition。它应该做的是在 monad m
中从一个函数中创建一个计算,该函数接受一个状态和 returns "result" 加上新的(更新的)状态。
另请注意,这是 State monad 上最通用的操作,get
和 put
都可以用 state
:
get = state $ \s -> (s, s)
put s = state $ \_ -> ((), s)
这意味着您不必自己实施 get
和 put
。您只需要实现 state
函数, get
/put
将来自默认实现。
顺便说一句,这也适用于其他方式:如果您定义 get
和 put
,state
的定义将来自默认值:
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)
作为进一步的练习,我鼓励你看看 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...>