Haskell 如何使用状态单子

Haskell 如何使用状态单子,haskell,state-monad,Haskell,State Monad,我已经问了一个关于理解状态单子的问题,但是现在我觉得这个应该是另一个问题 鉴于我实现了状态monad,我希望我的状态是一个类似于环境的数据结构: 环境 数据环境=环境{ envName::String, 文件名::[字符串] } 实例Show Env where show Env{envName=x,filename=xs}={envName:++x++ ,文件:[++foldr\t y->t++,++y xs++]} initEnv::IO Env initEnv=do name=listDi

我已经问了一个关于理解状态单子的问题,但是现在我觉得这个应该是另一个问题

鉴于我实现了状态monad,我希望我的状态是一个类似于环境的数据结构:

环境

数据环境=环境{ envName::String, 文件名::[字符串] } 实例Show Env where show Env{envName=x,filename=xs}={envName:++x++ ,文件:[++foldr\t y->t++,++y xs++]} initEnv::IO Env initEnv=do name=listDirectory 返回Env{envName=name,fileNames=names} 我不知道如何将此数据结构集成为State monad中的State,以便能够更改环境名称、打印或使用它。它可能看起来太宽泛了,但如果没有一个完整的例子,我无法理解它:

状态单子实现

newtype State sa=状态{run::s->a,s} 实例函子状态s,其中 fmap=Control.Monad.liftM 实例应用程序状态s,其中 纯=返回 =Control.Monad.ap 实例Monad状态s在哪里 返回a=State$\k->a,k >>=mf=State$\s->让a,s'=运行msin 跑步 我想要实现什么

readEnv::声明一个环境->环境 readEnv m= changeEnvName::状态a环境->状态a环境 changeEnvName m=-给定处于打包状态的环境, -我想换个名字 GetEnvFileLength::声明一个环境->[Int] GetEnvFileLength s a=s>>=GetLength GetLength::[String]->[Int] getLength xs=映射长度xs 另外,我知道我应该使用Reader或Writer monad,但我希望采用一种综合方法来理解所有东西是如何结合在一起的


有什么想法吗?

如果您正确地获得了类型签名,可能会更容易取得进展:

readEnv::State Env Env
changeEnvName::String -> State Env ()
getEnvFileLengths::State Env [Int]
如果您觉得这些类型的选择很奇怪,那么不妨尝试扩展新类型,看看之后它们是否更合理:

-- give me an initial environment from your store, I'll give you the new environment
-- to store and another copy of the environment as the result of the computation
readEnv :: Env -> (Env, Env)

-- give me a new name and the old environment, I'll give you a new environment and
-- a trivial acknowledgement that I'm done
changeEnvName :: String -> Env -> ((), Env)

-- give me an initial environment that you're storing, I'll give you the new
-- environment to store (actually same as the old one) and the result of the
-- length computations
getEnvFileLengths :: Env -> ([Int], Env)

changeEnvName不应具有类型State Env->State Env。如前所述,状态s描述的是改变状态类型s,而不是状态本身类型->状态环境类型。在大多数情况下,您无法编写函数State Env typek->typem,就像您的readEnv::State a Env->Env一样。改为使用readEnv::State Env Env Env,定义为readEnv=get。Env可能是State,而不是输出值。changeEnvName可以简单地使用State Env a类型-尽管我假定您希望能够指定新名称,在这种情况下,它具有type String->State Env a类型。要实现它,您可以使用预定义的put方法——尽管编写您自己的实现并不困难。请尝试阅读许多其他好的源代码。听起来execState就是你想要的。所以看看第一个方法,我只需要调用我的initEnv,但仍然要以某种方式摆脱IO monad并填充初始状态:readEnv=state$\\ \->initEnv>=\env->env,env@BercoviciAdrian在任何时候,这些值的实现都不应该提到您的initEnv。调用者有责任为您提供初始环境,因此调用者有责任以某种方式使用initEnv。