Vous pouvez faire vos fonctions monade-agnostique en utilisant typeclasses au lieu de béton monade des piles.
Disons que vous avez cette fonction, par exemple:
bangMe :: State String ()
bangMe = do
str <- get
put $ str ++ "!"
-- or just modify (++"!")
Bien sûr, vous vous rendez compte qu'il fonctionne comme un transformateur ainsi, on pourrait écrire:
bangMe :: Monad m => StateT String m ()
Toutefois, si vous avez une fonction qui utilise une autre pile, disons - ReaderT [String] (StateT String IO) ()
ou que ce soit, vous devrez utiliser le redoutable lift
fonction! Alors comment est-ce que évitée?
L'astuce est de faire de la signature de la fonction même plus générique, de sorte qu'il est dit que l' State
monade peut apparaître n'importe où dans la monade de la pile. Ceci est fait comme ceci:
bangMe :: MonadState String m => m ()
Cela oblige m
à être une monade qui prend en charge l'état (quasi) n'importe où dans la monade de la pile, et la fonction va donc travailler sans avoir à le soulever pour une telle pile.
Il y a un problème; depuis IO
n'est pas une partie de l' mtl
, il n'a pas, d'un transformateur (par exemple, IOT
), ni d'une pratique de type de classe par défaut. Alors, que devez-vous faire lorsque vous voulez soulever IO actions de manière arbitraire?
Le sauvetage est MonadIO
! Il se comporte presque identique à MonadState
, MonadReader
etc, la seule différence étant qu'il est un peu différent d'un mécanisme de levage. Il fonctionne comme ceci: vous pouvez prendre n'importe quel IO
d'action, et d'utiliser liftIO
pour la transformer en une monade agnostique version. Donc:
action :: IO ()
liftIO action :: MonadIO m => m ()
Par la transformation de l'ensemble de la monadique actions que vous souhaitez utiliser de cette manière, vous pouvez, s'entrelacent monades autant que vous le souhaitez sans aucune fastidieux de levage.