Haskell:在 FreeMonad 解释器中使用 MonadState 进行内存

标签 haskell memoization monad-transformers state-monad free-monad

鉴于我有以下 DSL(使用免费 Monad)及其解释器:

data MyDslF next =
    GetThingById Int (Thing -> next)
  | Log Text next

type MyDslT = FT MyDslF

runMyDsl :: (MonadLogger m, MonadIO m, MonadCatch m) => MyDslT m a -> m a
runMyDsl = iterT run
  where
    run :: (MonadLogger m, MonadIO m, MonadCatch m) => MyDslF (m a) -> m a
    run (Log message continue)      = Logger.log message >> continue
    run (GetThingById id' continue) = SomeApi.getThingById id' >>= continue

我想在内部更改解释器以使用 MonadState,这样如果已经为给定的 Id 检索了 Thing,则不会再调用 SomeApi

假设我已经知道如何使用 getput 编写内存版本,但我遇到的问题是运行 MonadStaterunMyDsl 内。 我认为解决方案类似于:

type ThingMap = Map Int Thing

runMyDsl :: (MonadLogger m, MonadIO m, MonadCatch m) => MyDslT m a -> m a
runMyDsl = flip evalStateT mempty . iterT run
  where
    run :: (MonadLogger m, MonadIO m, MonadCatch m, MonadState ThingMap m) => MyDslF (m a) -> m a
    run ..

但是类型并不一致,因为 run 返回 (.. , MonadState ThingMap m) => m a 并且 evalStateT 需要 StateT ThingMap m a.

最佳答案

使用iterTM而不是iterT:

runMyDsl :: (MonadLogger m, MonadIO m, MonadCatch m) => MyDslT m a -> m a
runMyDsl dsl = evalStateT (iterTM run dsl) Map.empty
  where
  run (Log message continue)      = logger message >> continue
  run (GetThingById id' continue) = do
    m <- get 
    case Map.lookup id' m of
      Nothing -> do 
         thing <- getThingById id' 
         put (Map.insert id' thing m)
         continue thing
      Just thing -> continue thing

同样,如果您首先使用 hoistFT 将 MyDsl m a 提升为 MyDsl (StateT Int m) a,则可以使用 iterT举起,像这样:

runMyDsl :: (MonadLogger m, MonadIO m, MonadCatch m) => MyDslT m a -> m a
runMyDsl dsl = evalStateT (iterT run (hoistFT lift dsl)) Map.empty

这使得 dsl 变成 MyDsl (StateT Int m) a,实际上不涉及任何状态更新,尽管 run 确实涉及状态转换。

关于Haskell:在 FreeMonad 解释器中使用 MonadState 进行内存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38392568/

相关文章:

haskell - 为什么单子(monad)转换器与堆叠单子(monad)不同?

haskell - 什么是 Levity 多态性

python - 为什么寻路机器人的这些动态规划解决方案中的一个比另一个更快?

javascript - 为什么我得到的闭包值为 "result"?

scala - monad 转换器是否适用于从服务获取 JSON?

Haskell 将不确定性与错误处理相结合

c# - 如何以编程方式检测副作用(编译时或运行时)?

haskell - 处理一个 monad 调用另一个 monad 的干净方法是什么?

haskell - MonadWriter 类中的冗余

haskell - writeTVar 的不安全版本