gpt4 book ai didi

haskell - 关于 State 单子(monad)在 haskell 逝世的困惑

转载 作者:行者123 更新时间:2023-12-02 16:29:58 26 4
gpt4 key购买 nike

在Haskell中,州通过monad来提取和存储州。在以下两个示例中,都使用>>通过了State monad,并且进行了仔细的验证(通过函数内联和归约),确认该状态确实已传递到下一步。

但这似乎不是很直观。那么这是否意味着当我想通过State monad时,我只需要>>(或>>=和lambda表达式\s -> a,其中sa中不是免费的)?任何人都可以为这个事实提供直观的解释,而不必费心去减少功能吗?

-- the first example
tick :: State Int Int
tick = get >>= \n ->
put (n+1) >>
return n

-- the second example
type GameValue = Int
type GameState = (Bool, Int)

playGame' :: String -> State GameState GameValue
playGame' [] = get >>= \(on, score) -> return score
playGame' (x: xs) = get >>= \(on, score) ->
case x of
'a' | on -> put (on, score+1)
'b' | on -> put (on, score-1)
'c' -> put (not on, score)
_ -> put (on, score)
>> playGame xs


非常感谢!

最佳答案

真正归结为了解状态与s -> (a, s)同构。因此,在单声道操作中“包装”的任何值都是对某个状态s(产生a的有状态计算)进行转换的结果。

在两个有状态计算之间传递状态

f :: a -> State s b
g :: b -> State s c


对应于用 >=>组成它们

f >=> g


或使用 >>=

\a -> f a >>= g


结果是

a -> State s c


这是一个有状态的操作,它以某种方式转换某些基础状态 s,允许其访问某些 a,并产生某些 c。因此,允许整个转换依赖于 a,并且允许值 c依赖于某些状态 s。这正是您要表达状态计算的内容。整洁的东西(将这种机器表示为monad的唯一目的)是您不必费心传递状态。但是要了解它是如何完成的,请参考 hackage>>=的定义),暂时忽略一下它是一个转换器而不是最终的monad。

m >>= k  = StateT $ \ s -> do
~(a, s') <- runStateT m s
runStateT (k a) s'


您可以忽略使用 StateTrunStateT的包装和展开,此处 m的格式为 s -> (a, s)k的格式为 a -> (s -> (b, s)),并且希望生成有状态的转换 s -> (b, s)。因此结果将是 s的函数,要生成 b,可以使用 k,但首先需要 a,如何生成 a?您可以采用 m并将其应用于状态 s,从第一个单子动作 s'中获得修改后的状态 m,然后将该状态传递给 (k a)(类型为 s -> (b, s) )。在这里,状态 s已通过 m成为 s',并传递给 k成为某些最终的 s''

对于使用此机制的用户来说,这仍然是隐藏的,这对于monads而言是一件很整洁的事情。如果您希望状态随着计算的发展而变化,那么您可以通过表达为 State -actions的小步骤来构建计算,然后让 do -notation或bind( >>=)进行链接/传递。

>>=>>之间的唯一区别是您关心或不关心非状态结果。

a >> b


实际上等于

a >>= \_ -> b


因此,无论值是由操作 a输出的,您都将其丢弃(仅保留修改后的状态),并与其他操作 b继续(传递状态)。



关于你的例子

tick :: State Int Int 
tick = get >>= \n ->
put (n+1) >>
return n


您可以用 do表示法将其重写为

tick = do
n <- get
put (n + 1)
return n


虽然第一种方式可以更清楚地说明传递的内容,第二种方式可以很好地说明您不必关心它。


首先 get当前状态并将其公开(在简化设置中为 get :: s -> (s, s)), <-表示您确实关心该值,并且不想将其丢弃,底层状态也传递给了背景保持不变( get的工作方式)。
然后 put :: s -> (s -> ((), s))(在将不必要的括号删除到 put :: s -> s -> ((), s)之后等效)采用一个值将当前状态替换为(第一个参数),并产生有状态操作,其结果是您删除的无趣值 ()(因为您不使用 <-或因为您使用 >>而不是 >>=)。由于 put,基础状态已更改为 n + 1,因此将其继续传递。
return对基础状态不执行任何操作,它仅返回其参数。


总而言之, tick从某个初始值 s开始,在内部将其更新为 s+1并在侧面输出 s

另一个示例的工作方式完全相同, >>仅用于丢弃由 ()生成的 put。但是状态总是无处不在。

关于haskell - 关于 State 单子(monad)在 haskell 逝世的困惑,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42384473/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com