Haskell a un équivalent de with
pour les fichiers, il est appelé withFile
. Ce:
with open("file1", "w") as f:
with open("file2", "r") as g:
k = g.readline()
f.write(k)
est équivalent à:
withFile "file1" WriteMode $ \f ->
withFile "file2" ReadMode $ \g ->
do k <- hGetLine g
hPutStr f k
Maintenant, withFile
pourrait ressembler à quelque chose monadique. Son type est:
withFile :: FilePath -> IOMode -> (Handle -> IO r) -> IO r
côté droit ressemble (a -> m b) -> m b
.
Une autre similitude: En Python, vous pouvez sauter as
, et en Haskell vous pouvez utiliser >>
au lieu de >>=
(ou do
bloc <-
flèche).
Donc je vais répondre à cette question: est - withFile
monadique?
Vous pourriez penser qu'il puisse être écrit comme ceci:
do f <- withFile "file1" WriteMode
g <- withFile "file2" ReadMode
k <- hGetLine g
hPutStr f k
Mais ce n'est pas de vérification de type. Et il ne peut pas.
C'est parce que dans Haskell le IO monade est séquentielle: si vous écrivez
do x <- a
y <- b
c
après l' a
est exécutée, b
est exécutée, puis c
. Il n'y a pas de "revenir en arrière"
pour nettoyer a
à la fin ou quelque chose comme ça. withFile
, d'autre part,
a proximité de la poignée lorsque le bloc est exécuté.
Il y a une autre monade, appelé la continuation de la monade, qui permet de faire de telles
les choses. Cependant, vous avez maintenant deux monades, IO et les continuations, et l'utilisation d'effets de deux monades à la fois nécessite l'utilisation de monade transformateurs.
import System.IO
import Control.Monad.Cont
k :: ContT r IO ()
k = do f <- ContT $ withFile "file1" WriteMode
g <- ContT $ withFile "file2" ReadMode
lift $ hGetLine g >>= hPutStr f
main = runContT k return
C'est laid. Donc la réponse est: un peu, mais qui exige de faire face avec beaucoup de subtilités qui rendent le tout assez opaques.
Python with
pouvez simuler seul un petit peu de ce que les monades peuvent y ajouter de la saisie et de la finalisation du code. Je ne pense pas que vous pouvez simuler par exemple
do x <- [2,3,4]
y <- [0,1]
return (x+y)
à l'aide de with
(il pourrait être possible avec certains sale hacks). Au lieu de cela, utilisez-le pour:
for x in [2,3,4]:
for y in [0,1]:
print x+y
Et il y a un Haskell fonction de ce - forM
:
forM [2,3,4] $ \x ->
forM [0,1] $ \y ->
print (x+y)
Je recommed de lecture à propos de yield
qui ressemble davantage à monades qu' with
:
http://www.valuedlessons.com/2008/01/monads-in-python-with-nice-syntax.html
Une question connexe: si nous avons des monades, faut-il des exceptions?
Fondamentalement non, au lieu d'une fonction qui renvoie Un ou retourne B, vous pouvez faire une fonction qui renvoie Either A B
. La monade pour Either A
va alors se comportent comme des exceptions - si une ligne de code renvoie une erreur, l'ensemble du bloc.
Toutefois, cela voudrait dire que la division aurait de type Integer -> Integer -> Either Error Integer
et ainsi de suite, pour attraper la division par zéro. Vous devrez détecter les erreurs (qui est explicitement mise en correspondance du modèle ou de l'utilisation de bind) dans le code qui utilise la division ou a même possibilité d'un moindre mal. Haskell utilise des exceptions pour éviter de le faire.