2011-07-06 2 views
9

Un titre prêtant à confusion pour une question déroutante! Je comprends a) les monades, b) la monade IO, c) la monade Cont (Control.Monad.Cont), et d) la monade de transformateur suite ContT. (Et je comprends vaguement les transformateurs Monad en général - mais pas assez pour répondre à cette question.) Je comprends comment écrire un programme où toutes les fonctions sont dans la monade Cont (Cont r a), et je comprends comment écrire un programme où tous les fonctions sont dans la monade Cont/IO combinée (ContT r IO a).Echapper de la monade IO à l'intérieur de la monade Continuation

Mais je me demande comment je pourrais écrire un programme où certaines fonctions sont dans un Cont/monade IO combiné (ContT r IO a) et autres fonctions ne sont que dans la suite monade (Cont r a). Fondamentalement, je veux écrire l'ensemble du programme dans le style de continuation, mais seulement utiliser la monade IO si nécessaire (un peu comme dans le code Haskell "normal", je n'utilise que la monade IO si nécessaire).

Par exemple, considérons ces deux fonctions, dans un style non-continuation:

foo :: Int -> IO Int 
foo n = do 
    let x = n + 1 
    print x 
    return $ bar x 

bar :: Int -> Int 
bar m = m * 2 

Notez que foo EXIGE IO mais bar est pur. Maintenant, je compris comment écrire ce code en utilisant pleinement la poursuite monade, mais je devais enfiler IO par bar ainsi:

foo :: Int -> ContT r IO Int 
foo n = do 
    let x = n + 1 
    liftIO $ print x 
    bar x 

bar :: Int -> ContT r IO Int 
bar m = return $ m * 2 

Je ne veux tout mon code dans le style de continuation, mais je n » t souhaitez utiliser la monade d'E/S sur les fonctions qui ne l'exigent pas. Au fond, je voudrais définir bar comme ceci:

bar :: Int -> Cont r Int 
bar m = return $ m * 2 

Malheureusement, je ne peux pas trouver un moyen d'appeler une fonction Cont r a monade (bar) à l'intérieur d'une fonction ContT r IO a monade (foo). Existe-t-il un moyen de "soulever" une monade non transformée en une monade transformée? c'est-à-dire, comment puis-je changer la ligne "bar x" en foo afin qu'il puisse appeler correctement bar :: Int -> Cont r Int?

Répondre

17

C'est là Control.Monad.Class vient en Faites bar polymorphes dans ce monade il peut travailler.

bar :: MonadCont m => Int -> m Int 
bar m = return $ m * 2 

Notez que les instances liste au bas de la page que les instances de MonadCont connues au moment les documents ont été générés comprennent à la fois Cont r et Monad m => ContT r m. De plus, la classe MonadCont définit la fonction callCC, ce qui est nécessaire pour utiliser les fonctionnalités de continuation. Cela signifie que vous pouvez utiliser la pleine expressivité des suites au sein de bar, même si cet exemple ne le fait pas. De cette manière, vous écrivez des fonctions qui ne peuvent pas utiliser d'E/S, car elles n'ont pas de contrainte MonadIO, et leur type ne mentionne pas explicitement IO. Mais ils sont polymorphes dans quelle monade ils travaillent à l'intérieur, de sorte qu'ils peuvent être appelés de manière triviale à partir de contextes qui incluent des entrées/sorties.

+1

Merci. Ça marche. J'ai aussi trouvé ma propre solution, qui me donnait exactement ce que je voulais (je n'avais pas à changer 'Bar'):' liftCont :: Cont (m) a -> ContT r m a'; 'liftCont c = ContT $ runCont c'. Ma solution décompresse le 'Cont' et construit un' ContT'. Je pense que votre solution est plus agréable car elle est polymorphe et ne nécessite pas de manipulation réelle des structures de données, alors cochez pour vous. Mais je vais poster le mien comme une autre réponse, car c'est utile au cas où vous ne pouvez pas modifier 'bar'. Aussi +1 pour expliquer pourquoi il serait impossible d'utiliser IO dans 'bar'. – mgiuca

5

je trouve que cela fait exactement ce que je voulais (sans avoir à changer Bar):

liftCont :: Cont (m r) a -> ContT r m a 
liftCont = ContT . runCont 

Ce dépaquète Cont et construit un ContT.

Je peux alors utiliser liftCont pour appeler Bar de Foo:

foo n = do 
    let x = n + 1 
    liftIO $ print x 
    liftCont $ bar x 

Je ne pense pas que ce soit « mieux » que la solution de Carl (je lui ai donné la tique), mais je l'ai posté ici parce qu'il vous permet d'utiliser Bar sans modifier son type, si utile si vous ne pouvez pas modifier Bar. (Il a probablement moins bonnes performances que.)

Questions connexes