J'ai un problème avec liftM
. Pour (+)
cela fonctionne bien, la fonction
madd a b = liftM2 (+) a b
me donne le résultat attendu Juste 5 `madd` Juste 7 = Juste 12
mais maintenant essayer avec (/)
cela me donne des résultats étranges.
mdiv a b = liftM2 (/) a b
maintenant l'opération Just 12 `mdiv` Just 0
me donne Just Infinity
pendant que j'attends Rien
.
3 Réponses :
Si vous démarrez GHCi et tentez l'opération de division 'nue', vous obtenez Infinity
:
Prelude Control.Monad> liftM2 (/) (Just 12) (Just 0) Just Infinity Prelude Control.Monad> liftM2 (/) (Just 12) Nothing Nothing Prelude Control.Monad> liftM2 (/) Nothing (Just 0) Nothing Prelude Control.Monad> liftM2 (/) (Just 12) (Just 3) Just 4.0 Prelude Control.Monad> liftM2 (/) Nothing Nothing Nothing
liftM2
vous permet simplement d'effectuer une opération à l'intérieur d'un contexte monadique. Dans le cas de Just 12
et Just 0
, ce contexte est Maybe
. Cela ne change pas le fonctionnement; il ne traite que de la variabilité introduite par le conteneur.
Prelude> 12 / 0 Infinity
Notez comment liftM2
gère les cas où l'un des arguments ou les deux sont Rien
. Une fonction qui prend deux arguments (comme /
ou +
) ne peut pas être appelée si vous n'avez pas exactement deux valeurs. liftM2
gère les cas où vous avez moins de deux valeurs en renvoyant Nothing
.
En revanche, s'il y a exactement deux valeurs, il appelle le fonction. Lorsque vous l'appelez avec Just 12
et Just 0
, vous avez exactement deux valeurs et l'opération /
est appelé, ce qui donne Infinity
.
C'est comme prévu.
la version spécifique de liftM2
pour les types Maybe
fait cela ("gère les cas où vous en avez moins de deux valeurs"). cela doit être dit explicitement, je pense, sinon cela ressemble toujours à une magie pour l'OP. si Peut-être
peut faire cela pour les valeurs manquantes, pourquoi ne pourrait-il pas le faire aussi pour les 0!
@ Will Ness oui pourquoi pas ..?
Les monades ne sont pas magiques, elles encapsulent simplement certains modèles de calcul.
liftM2 :: (a -> b -> c) -> Maybe a -> Maybe b -> Maybe c
et la version spécifique de la monade Peut-être
,
liftM2 :: Monad m => (a -> b -> c) -> m a -> m b -> m c
Il n'y a rien ici sur les nombres et la division- erreurs par zéro. Tout ce que nous savons ici est de savoir si une valeur monadique est Juste quelque chose
ou Rien
.
Cela nous permet nous de définir un coffre-fort fonction de division, mais elle ne le fera pas pour nous tout seul.
Haskell n'est pas un agent IA intelligent d'écriture de code. C'est juste un autre langage de programmation où un programmeur , et non un ordinateur, écrit des programmes. Et d'ailleurs, pourquoi devrait-il décider pour vous si vous préférez qu'une erreur de division par zéro à l'exécution se produise ou non!
D'autres réponses ont montré pourquoi cela fonctionne comme cela pour la monade Maybe
. Mais bien sûr, Maybe
n'est pas la seule monade. Vous voulez que cela fonctionne pour IO
, les analyseurs et toutes les autres monades aussi:
foo :: IO Double foo = liftM2 (/) (return 12) (return 0)
Que doit renvoyer foo
? Il ne peut pas s'agir de Rien
car ce n'est pas dans la monade Peut-être
.
Vous pouvez utiliser fail
, qui dans le Peut-être que
monade évalue à Rien
et plus généralement évalue à une sorte de valeur d'exception ou bien appelle erreur
en fonction de la monade.
Bien sûr, dans le cas particulier de 12/0
, la bonne réponse est vraiment Infinity
. Les fous du numérique qui ont défini la norme IEEE 754 l'ont fait de cette façon pour une raison.
Pourquoi vous attendez à
Rien
?Comme la division par 0 n'est pas définie, le meilleur awnser serait
Nothing
Une monade détermine simplement comment un calcul monadique peut être composé avec une fonction qui renvoie un calcul monadique du même type. Avec
Maybe
, le contexte monadique est des calculs qui peuvent échouer. Vous devez encore définir ce que signifie l'échec pour votre calcul.Donc, dans ce cas, je devrais ajouter quelque chose comme
mdiv a b = if (b == 0) then Nothing else liftM2 (/) a b
(/)
n'est défini que pour les valeurs de typeFractional a => a
; les seuls types (intégrés) avec des instancesFractional
sontFloat
etDouble
, et la division par 0 est définie pour ces types: le résultat estInfinity
.Pour une fonction où la division par 0 n'est vraiment pas définie, utilisez
div
ouquot
. Même dans ce cas, ils lèvent une exception, que vous devrez explicitement intercepter et remplacer parNothing
.@Kevin no,
mdiv1 a b = if (b == 0) then Nothing else Just ((/) a b)
, oumdiv2 a b = do {vb <- b; if (vb == 0) then Nothing else liftM2 (/) a b}
. vérifiez les types que vous obtenez pour eux (et pour votre version également, si techniquement des vérifications de type vous obtiennent une contrainte qui n'a pas beaucoup de sens).