1
votes

Monades et ascenseurM

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 .


7 commentaires

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 type Fractional a => a ; les seuls types (intégrés) avec des instances Fractional sont Float et Double , et la division par 0 est définie pour ces types: le résultat est Infinity .


Pour une fonction où la division par 0 n'est vraiment pas définie, utilisez div ou quot . Même dans ce cas, ils lèvent une exception, que vous devrez explicitement intercepter et remplacer par Nothing .


@Kevin no, mdiv1 a b = if (b == 0) then Nothing else Just ((/) a b) , ou mdiv2 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).


3 Réponses :


3
votes

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.


2 commentaires

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 ..?



9
votes

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!


0 commentaires

2
votes

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.


0 commentaires