我在Monad Transformers上遇到了一个问题,但我认为包含一些关于我如何达到当前状态的上下文是有帮助的,所以我首先对我的程序做一个粗略的解释:
该项目是一种简单(玩具)编程语言的解释器。我有一个用来代表评估的单子。它的定义如下:
type Eval a = ReaderT Environment (ExceptT String (State ProgState a))这很好,我可以很高兴地编写一个评估函数:
eval :: Expr -> Eval Value
eval (Apply l r) = ...
eval ...值数据类型有点奇怪,我嵌入了Value -> EvalM Value类型的Haskell函数。为此,我在定义中添加了一个泛型类型参数,然后使用EvalM实例化该参数。
data Value' m
= IntVal Int
...
| Builtin (Value' m -> m (Value' m))
type Value = Value' EvalM事情进行得很顺利,但后来我不得不编写一个函数,该函数使用Eval monad和IO操作大量交织代码。这看起来有点可怕:
case runEval ({-- some computation--}) of
Right (val, state') -> do
result <- -- IO stuff here
case runEvaL {-- something involving result --} of
...
Left err -> ...该函数有5个层次的嵌套,也是递归的.绝对丑陋:我希望适应使用Monad转换器将是解决方案:
type EvalT m = ReaderT Environment (ExceptT String (StateT ProgState m))这种重构相对来说是无痛的:它主要涉及更改类型签名,而不是实际的代码,但是存在一个问题:Builtin。给定一个将参数x应用于表单Builtin f的值的表达式,eval函数将简单地返回f x。然而,它有类型Eval Value,但是重构的eval需要有类型签名:
eval :: Monad m => EvalT m Value至于解决这个问题(即使之成为打字机),我可以想出几个解决方案,每个解决方案都有一个问题:
lift,在那里我可以带Eval a去EvalT m a。的话)
Value类型,使其由内部monad (即Value m = Value' (EvalT m) )索引。Value m的东西都必须由m参数化。我觉得这会不必要地扰乱任何包含code.的类型签名,这是一个问题,考虑到进行此更改的最初动机是清理我的Value。
当然,也许有一个更好的解决方案,我还没有想到。如有任何反馈/建议,将不胜感激:)。
发布于 2022-10-16 18:15:35
您可能会喜欢mmorph包。
-- since State s = StateT s Identity, it's probably also the case
-- that Eval = EvalT Identity, under some light assumptions about
-- typos in the question
liftBuiltin :: Monad m => Eval a -> EvalT m a
liftBuiltin = hoist (hoist (hoist generalize))或者,您可以在值中存储一个多态函数。一种方法是参数化变压器。
data Value' t = ... | Builtin (forall m. Monad m => Value' t -> t m (Value' t)
type Value = Value' EvalT另一种方法是使用mtl-style约束。
data Value = ... | Builtin (forall m. (MonadReader Environment m, MonadError String m, MonadState ProgState m) => Value -> m Value)最后这个,虽然冗长,但在我看来相当不错;我可能会从那里开始。
https://stackoverflow.com/questions/74086991
复制相似问题