2

内MonadStateを使用してMemoising私は、次の(無料モナドを使って)DSLおよびそのインタプリタ持っていることを考える:ハスケル:FreeMonad通訳

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を使用するために内部インタプリタを変更したいですThingがすでにId与え用に取得された、その後、SomeApi

への2回目の呼び出しはありません、私はすでにgetputを使用してmemoisedバージョンの書き方を知っていると仮定しますが、私が午前問題はを実行しています210内runMyDsl。 私は解決策は、のようになります考えていた:

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 aevalStateTStateT ThingMap m aを期待するので、整列しません。

答えて

2

代わりiterTの使用iterTM

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 liftを使用してMyDsl (StateT Int m) aMyDsl 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 

これはMyDsl (StateT Int m) adslを作りますrunには状態遷移が含まれていますが、実際には状態の更新は行われません。