为什么 runState 签名只有 state 参数?
Why runState signature has only state argument?
一个真实的例子:如果我心情好('good state'),当经理问我估算时,我给了他一个可靠的答案,但他敢吗连续做 3 次 ,中间没有免费零食,我的情绪发生了变化(我达到了 'bad state'),接下来的 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)
,而不是输入空间(ESTIMATE
或 Cookies
)。好像连听都不听就得回答!
所以我的问题是:如何使用 Haskell 的 State
monad 进行有状态计算和论证计算?
有状态计算得到一个输入、一个状态,return一个输出和一个新状态。所以类型将是 input -> state -> (state, output)
.
runState
只是一个部分应用的状态计算,它已经接受了它的输入。
另请注意,当您编写有状态函数时(即当您使用 >>=
绑定运算符或 do
符号时),您正是这样做的:您提供输入作为表达式,绑定负责仅传递 state.
您可以在不使用其 return 值的情况下调用 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
所代表的。
所以您实际上不需要状态操作的参数,因为您可以预先部分应用它们以获得 "pure stateful computations that have no input"(这些引号很吓人)。
听起来您要找的不是 State
,而是 StateT
,一个 monad 转换器,它为现有的 monad 添加了状态。
newtype StateT s m a = StateT (s -> m (a, s))
给定类型 s
的状态,一个 StateT s m a
动作 returns 一个 m
动作,当 运行 时产生一个结果和一个新的状态。 StateT s
是 MonadTrans
:
的实例
instance MonadTrans (StateT s) where
--lift :: Monad m => m a -> StateT s m a
lift ma = StateT $
\s -> ma >>= \a -> pure (a, s)
此外,如果 m
是 Monad
,那么 StateT s m
也是。
因此,如果您想在某些情况下使用 "input"(例如,IO
),您可以这样做:
do
s <- get
input <- lift getLine
when (annoying input && annoyed s) $ put Angry
特别是IO
,通常使用liftIO
更好,它可以举起一整堆变压器。
一个真实的例子:如果我心情好('good state'),当经理问我估算时,我给了他一个可靠的答案,但他敢吗连续做 3 次 ,中间没有免费零食,我的情绪发生了变化(我达到了 'bad state'),接下来的 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)
,而不是输入空间(ESTIMATE
或 Cookies
)。好像连听都不听就得回答!
所以我的问题是:如何使用 Haskell 的 State
monad 进行有状态计算和论证计算?
有状态计算得到一个输入、一个状态,return一个输出和一个新状态。所以类型将是 input -> state -> (state, output)
.
runState
只是一个部分应用的状态计算,它已经接受了它的输入。
另请注意,当您编写有状态函数时(即当您使用 >>=
绑定运算符或 do
符号时),您正是这样做的:您提供输入作为表达式,绑定负责仅传递 state.
您可以在不使用其 return 值的情况下调用 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
所代表的。
所以您实际上不需要状态操作的参数,因为您可以预先部分应用它们以获得 "pure stateful computations that have no input"(这些引号很吓人)。
听起来您要找的不是 State
,而是 StateT
,一个 monad 转换器,它为现有的 monad 添加了状态。
newtype StateT s m a = StateT (s -> m (a, s))
给定类型 s
的状态,一个 StateT s m a
动作 returns 一个 m
动作,当 运行 时产生一个结果和一个新的状态。 StateT s
是 MonadTrans
:
instance MonadTrans (StateT s) where
--lift :: Monad m => m a -> StateT s m a
lift ma = StateT $
\s -> ma >>= \a -> pure (a, s)
此外,如果 m
是 Monad
,那么 StateT s m
也是。
因此,如果您想在某些情况下使用 "input"(例如,IO
),您可以这样做:
do
s <- get
input <- lift getLine
when (annoying input && annoyed s) $ put Angry
特别是IO
,通常使用liftIO
更好,它可以举起一整堆变压器。