2

內由於我有以下的DSL(使用免費單子)及其解釋Memoising:哈斯克爾:使用MonadState一個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

沒有第二個電話讓我們假設我已經知道如何使用getput寫memoised版本,但我遇到的問題是運行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 aevalStateT預計StateT ThingMap m a

回答

2

使用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 

同樣地,你可以使用iterT如果先提高MyDsl m a使用hoistFT liftMyDsl (StateT Int m) a,就像這樣:

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

這使得dslMyDsl (StateT Int m) a雖然run確實涉及狀態轉換,但實際上並不涉及任何狀態更新。