Haskell:在FreeMonad解释器中使用MonadState进行记忆

Haskell:在FreeMonad解释器中使用MonadState进行记忆,haskell,memoization,monad-transformers,state-monad,free-monad,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 ::

鉴于我拥有以下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

假设我已经知道如何使用
get
put
编写备忘录版本,但我遇到的问题是在
runMyDsl
中运行
MonadState
。 我认为解决方案类似于:

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)=>ma
evalStateT
期望
StateT ThingMap ma

使用
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
这使得
dsl
变成了
MyDsl(StateT Int m)
,实际上不涉及任何状态更新,尽管
run
确实涉及状态转换

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