2016-04-20 5 views
2

Ich kann nicht mit folgendem Problem umgehen:
Ich habe eine Funktion, die foo :: a -> b -> ErrorT String IO Int
kehre ich weiß, dass es IO (Either String Int) zurückgibt.
Trotzdem habe ich auch komplexere Funktion, die zurückgibt:Haskell, wie mit nicht übereinstimmen Typen umgehen - Monaden

bar :: a -> b -> StateT Char (ReaderT Char (ErrorT String IO)) Int

Diese Funktion bar Anrufe foo funktionieren. Ich gehe auf folgende Sache:
Wenn foo genannt throwErrorbar wirft auch den gleichen Fehler. Wenn fooIntbar zurückgibt, wird auch Int zurückgegeben.

Es ist jedoch möglich, aufgrund unterschiedlicher Typen.

Ich weiß nicht, wie man das auf elegante Weise macht. Ich denke, dass meine Bestellung von Transformator Monad nicht in Ordnung ist.

+2

können Sie bitte ein Beispiel hinzufügen, wo Sie * 'foo' in' bar' anrufen - brauchen Sie wahrscheinlich nur den richtigen * Lift *? – Carsten

+0

Ja, vielen Dank. Ich habe 'lift $ lift $' benutzt und der Compiler hat es akzeptiert. Ich wusste es nicht - ich dachte, dass der Lift automatisch gemacht wird. –

+1

gut das würde die Verwendung von Monad-Stacks viel einfacher machen;) - aber egal - vielleicht möchten Sie Ihre eigene Antwort schreiben oder die Frage schließen, wenn es für Sie gelöst ist – Carsten

Antwort

2

So etwas sollte funktionieren:

bar x y = do 
    -- some code 
    z <- lift $ lift $ foo x y 
    -- some more code 
    return z 
2

Was ich in der Regel wird sie mit, was ich Transformator Monad Klassen genannt haben:

{-# LANGUAGE FlexibleContexts #-} 

import Control.Monad.Reader 
import Control.Monad.State 

inner :: ReaderT Char IO Int 
inner = do 
    a <- ask 
    lift $ print a 
    return 5 

inner' :: (MonadReader Char m, MonadIO m) => m Int 
inner' = do 
    a <- ask 
    liftIO $ print a 
    return 5 

outer :: StateT Char (ReaderT Char IO) Int 
outer = do 
    a <- get 

    b <- lift $ inner -- need to lift 
    c <- inner'   -- no need to lift 

    lift . lift $ print "need to lift twice to get to IO"    

    return 5 

main = runReaderT (runStateT outer 'b') 'a' 

Lassen Sie uns es brechen; Die erste inner Funktion hat einen konkreten Typ, den Sie direkt anheben müssen, um es zu benutzen. Wenn Sie jedoch nur Fähigkeiten in der Signatur parametrisieren, können Sie das Heben überspringen, solange es im Stapel eindeutig ist, wo diese Fähigkeit erhalten wird. In diesem Fall ist es klar, denn StateT Char (ReaderT Char IO) Int hat genau eine Instanz für MonadIO (aus IO) und genau eine Instanz von MonadReader Char (aus ReaderT ... Char).

Jetzt ist es egal wie viele hebt Sie müssen machen, solange die Instanz klar ist! Bedenken Sie:

outer' :: ErrorT String (StateT Char (ReaderT Char IO)) Int 
outer' = do 
    a <- inner'    -- still no need to lift! 
    b <- lift . lift $ inner -- need to double lift in this case 
    return 5 

Das letzte, was nicht offensichtlich sein könnte, ist, dass die Unterschrift von outer könnte auch in allgemeiner Weise ausgedrückt werden:

outer :: (MonadState Char m, MonadReader Char m, MonadIO m) => m Int 

Und es würde immer noch funktionieren ohne Aufzüge (gut sans liftIO für IO-Operationen, da Funktionen wie print werden in Form von IO definiert, nicht MonadIO m. im Vergleich dazu Funktionen wie ask und get in Bezug auf die jeweiligen MonadX Klasse definiert sind, die uns th überspringen erlaubt e Heben).

Verwandte Themen