为什么runState签名只有状态参数?

时间:2016-03-23 20:28:08

标签: haskell state monads

一个现实生活中的例子:如果我心情愉快(状态良好'),当经理问我估计时,我会给他一个坚实的答案,但敢于他连续3次连续做了,中间没有一些免费小吃,我的情绪发生了变化(我的状态变得很糟糕),接下来的3次接近我问他不要用任何胡言乱语打扰我。

这是我平时的日志:

                             [ Mood: Good, Patience: 3 ]  -- 11:00 am, I'm happy
ESTIMATE -> "bla bla 6",     [ Mood: Good, Patience: 2 ]
ESTIMATE -> "bla bla 1",     [ Mood: Good, Patience: 1 ]
Cookies! -> "",              [ Mood: Good, Patience: 3 again! ]
ESTIMATE -> "bla bla 7",     [ Mood: Good, Patience: 2 ]
ESTIMATE -> "bla bla 2",     [ Mood: Good, Patience: 1 ]
ESTIMATE -> "bla bla 9",     [ Mood: BAD , Patience: -2 ] -- Enough!
ESTIMATE -> "Need a break!"  [ Mood: BAD , Patience: -1 ]
ESTIMATE -> "Deploynig!",    [ Mood: BAD , Patience: 0 ]
ESTIMATE -> "Lunch time!",   [ Mood: Good, Patience: 3 ]  -- Ok he needs me..
ESTIMATE -> "bla bla 6",     [ Mood: Good, Patience: 2 ]
...

现在这个我工作的模型似乎适合State Monad。

newtype State s a = State { runState :: s -> (a, s) } 

但是我该怎么做?签名有一个州的空间,在我的情况下是(Mood,Patience),而不是输入(ESTIMATECookies)。这就像我应该在没有听的情况下回答!

所以我的问题是:我如何使用State Haskell monad进行有状态计算而且进行有争议的计算?

2 个答案:

答案 0 :(得分:8)

有状态计算获取输入,状态并返回输出和新状态。因此类型将为input -> state -> (state, output)

runState只是部分应用的有状态计算,已经取得了输入。

另请注意,当您编写有状态函数时(即使用>>=绑定运算符或do表示法时),您可以执行以下操作:将输入作为表达式提供,并且绑定负责传递只围绕

您可以在不使用其返回值的情况下调用get,但随后会丢失。如果要使用它,则必须使用value <- get,然后提供value作为下一个有状态计算的显式输入。只有在通过状态时,绑定才会发挥作用。

实际例子:考虑函数:

doStuff :: Int -> State Int Int
doStuff x = do
    val <- get
    put $ val+x+1
    return 0

doStuff类型具有完全模式input -> state -> (state, output)。但input部分由x参数表示。 提供x后,您会收到state -> (state, output)类型的内容,这正是runState所代表的内容。

因此,您实际上并不需要有状态操作的参数,因为您可以事先部分应用它们以获得没有输入的纯状态计算&#34; (那些是可怕的引用)。

答案 1 :(得分:2)

听起来你正在寻找的不是State而是StateT,一个 monad转换器可以为现有的monad添加状态。

newtype StateT s m a = StateT (s -> m (a, s))

如果状态为s,则StateT s m a操作会返回m操作,该操作在运行时会生成结果和新状态。 StateT sMonadTrans的实例:

instance MonadTrans (StateT s) where
  --lift :: Monad m => m a -> StateT s m a
  lift ma = StateT $
    \s -> ma >>= \a -> pure (a, s)

此外,如果mMonad,那么StateT s m也是如此。

所以,如果你想采取&#34;输入&#34;在某些情况下(例如IO),您可以自由地这样做:

do
  s <- get
  input <- lift getLine
  when (annoying input && annoyed s) $ put Angry

特别是对于IO,使用liftIO通常会更好,这可以提升整堆变形金刚。