查看State Monad的wiki,我正在尝试了解runState
和put
函数。
据我了解runState
,它需要State
的第一个参数,其中包含“Universe”,s
和值a
。它需要宇宙的第二个参数。最后,它返回(a, s)
,其中a
是新值,s
是新宇宙?
ghci> :t runState
runState :: State s a -> s -> (a, s)
示例:
ghci> let s = return "X" :: State Int String
ghci> runState s 100
("X",100)
但是,我不理解put
结果:
ghci> runState (put 5) 1
((),5)
由于runState
返回(a, s)
,为什么类型为a
的{{1}}?
我对上述尝试的解释没有信心。请纠正我,并在()
上回答我的问题。
答案 0 :(得分:6)
将put
与State
monad一起使用时,它有type s -> State s ()
。
put
将状态设置为其参数,就是这样。至于它的返回值:它本质上是一个虚拟值,因为返回没什么用处。
这在definition put s = state $ \ _ -> ((), s)
中也很明显。
答案 1 :(得分:1)
真正理解State
的最简单方法,恕我直言,只是为了研究代码并充分理解它,你可以从内存中实现它,就像我要做的那样:
import Control.Applicative
newtype State s a = State { runState :: s -> (a, s) }
instance Functor (State s) where
fmap f fa = State $ \s -> f (runState fa s)
instance Applicative (State s) where
pure a = State $ \s -> (a, s)
ff <*> fa = State $ \ s ->
let (f, s') = runState ff s
(a, s'') = runState fa s'
in (f a, s'')
instance Monad (State s) where
return = pure
ma >>= f = State $ \s ->
let (a, s') = runState ma s
(b, s'') = runState (f a) s'
in (b, s'')
get :: State s s
get = State $ \s -> (s, s)
put :: s -> State s ()
put s = State $ \_ -> ((), s)
modify :: (s -> s) -> State s ()
modify f = State $ \s -> ((), f s)
由于
runState
返回(a, s)
,为什么类型为()
?
这真的只是武断的。以上述为基准,我们也可以这样写modify
,get
和put
:
-- | Replaces the state with the result of applying it to the supplied
-- function. The result of the action is the original state.
modify :: (s -> s) -> State s s
modify f = State $ \s -> (s, f s)
-- `get` is just what you get when you `modify` with an identity function.
get :: State s s
get = modify (\s -> s)
-- This version of `put` differs from the canonical one because it returns
-- the old value.
put :: s -> State s s
put s = modify (\_ -> s)
在此版本中,modify
和put
具有与原始版本相同的效果,但是附加产生旧状态作为其结果。仅针对效果使用modify
和put
的客户通常不会注意到这种差异。
或者,modify
和put
的“返回旧状态”版本可以根据官方版本编写。例如:
swap :: s -> State s s
swap s = do
orig <- get
put s
return orig
因此,大多数这些操作是可以相互确定的,哪些是“基本的”而哪些不是......
答案 2 :(得分:1)
runState
函数在状态monad和初始状态中执行操作,并返回两者计算结果和最终状态。
runState
的一个典型用例是将一个带有初始值的复杂动作交给它,然后返回最终结果和状态。在您的示例中,操作是一个简单的原语put
。
put
操作采用新状态并为值生成()
(发音为unit)。它与putStrLn具有类型IO ()
的方式非常类似。它在monad中执行操作,但不会产生有用的值。
因此,如果初始状态为runState (put 5) 1
,则1
会被新状态5
吹走。状态计算的结果是put
()
的结果。
只是为了踢,让我们看一些更有趣的事情:
runState (puts 5 >> return "hello world!") undefined
--> ("hello world!", 5)
这里我们有两个与>>
粘在一起的动作(我把它读作&#34;然后&#34;,它是绑定>>=
的有限形式,我们只是从结果中删除结果左手边)。第一个操作将状态更改为5
,第二个操作不会触及状态,但会产生字符串&#34; hello world!&#34;然后它成为整个计算的价值。