2

Da ich die folgenden DSL habe und dessen Dolmetscher (kostenlos Monad verwenden):Haskell: Memoising mit MonadState in einem FreeMonad Interpreter

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 

Ich mag den Interpreter intern ändern MonadState zu verwenden, so dass, wenn ein Thing bereits für einen bestimmten Id, dann abgerufen worden gibt es keinen zweiten Anruf SomeApi

Lets weiß, dass ich schon davon ausgehen, wie die memoised Version mit get und put, aber das Problem, das ich läuft diehabe schreiben 210 innerhalb runMyDsl. Ich dachte, die Lösung würde wie folgt aussehen:

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 .. 

Aber die Typen nicht fluchten, da run kehrt (.. , MonadState ThingMap m) => m a und evalStateTStateT ThingMap m a erwartet.

Antwort

2

Verwenden iterTM statt 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 

Gleichwertig können Sie iterT verwenden, wenn Sie zuerst MyDsl m a zu einem MyDsl (StateT Int m) ahoistFT lift mit erhöhen, etwa so:

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

Dies macht dsl in eine MyDsl (StateT Int m) a das beinhaltet eigentlich keine Zustandsaktualisierungen, obwohl run Zustandsübergänge beinhaltet.