Utiliser Maybe
(ou son cousin Either
qui fonctionne fondamentalement de la même manière mais vous permet de renvoyer une valeur arbitraire à la place de Nothing
) a un but légèrement différent de celui des exceptions. En termes Java, c'est comme avoir une exception vérifiée plutôt qu'une exception d'exécution. Cela représente une chose à laquelle vous devez vous attendre , plutôt qu'une erreur à laquelle vous ne vous attendiez pas.
Donc, une fonction comme indexOf
retournera une Maybe
valeur parce que vous vous attendez à ce que l'élément ne soit pas dans la liste. Cela revient un peu à revenir null
d’une fonction, sauf d’une manière qui respecte le type, ce qui vous oblige à traiter le null
cas. Either
fonctionne de la même manière, sauf que vous pouvez renvoyer des informations associées au cas d’erreur, c’est donc plus semblable à une exception que Maybe
.
Alors, quels sont les avantages de l' approche Maybe
/ Either
? D'une part, c'est un citoyen de première classe de la langue. Comparons une fonction Either
à une autre en lançant une exception. Dans le cas exceptionnel, votre seul recours réel est une try...catch
déclaration. Pour la Either
fonction, vous pouvez utiliser les combinateurs existants pour rendre le contrôle de flux plus clair. Voici quelques exemples:
Tout d’abord, supposons que vous vouliez essayer plusieurs fonctions qui pourraient se tromper jusqu’à ce que vous n’en obteniez pas. Si vous n'en obtenez pas sans erreur, vous souhaitez renvoyer un message d'erreur spécial. C'est en fait un modèle très utile, mais ce serait une douleur horrible try...catch
. Heureusement, puisqu'il ne Either
s'agit que d'une valeur normale, vous pouvez utiliser les fonctions existantes pour rendre le code beaucoup plus clair:
firstThing <|> secondThing <|> throwError (SomeError "error message")
Un autre exemple est d'avoir une fonction optionnelle. Supposons que vous ayez plusieurs fonctions à exécuter, y compris une qui essaie d'optimiser une requête. Si cela échoue, vous voulez que tout le reste s'exécute de toute façon. Vous pourriez écrire du code comme:
do a <- getA
b <- getB
optional (optimize query)
execute query a b
Ces deux cas sont plus clairs et plus courts que l’utilisation try..catch
et, plus important encore, plus sémantiques. Utiliser une fonction comme <|>
ou qui optional
rend vos intentions beaucoup plus claires que d’utiliser try...catch
toujours de gérer les exceptions.
Notez également que vous n'êtes pas obligé de remplir votre code de lignes telles que if a == Nothing then Nothing else ...
! Le but de traiter Maybe
et en Either
tant que monade est d'éviter cela. Vous pouvez encoder la sémantique de propagation dans la fonction bind afin que vous obteniez les contrôles null / error gratuitement. Le seul moment où vous devez vérifier est explicitement si vous voulez retourner autre chose que Nothing
donné Nothing
, et même alors il est facile: il y a un tas de fonctions de bibliothèque standard pour rendre plus agréable que de code.
Enfin, un autre avantage est que le type Maybe
/ Either
est simplement plus simple. Il n'est pas nécessaire d'étendre le langage avec des mots-clés ou des structures de contrôle supplémentaires: tout n'est que bibliothèque. S'agissant de valeurs normales, le système de types est simplifié: en Java, vous devez différencier les types (par exemple, le type de retour) des effets (par exemple, les throws
instructions) que vous n'utiliseriez pas Maybe
. Ils se comportent également comme n'importe quel autre type défini par l'utilisateur - il n'est pas nécessaire qu'un code spécial de traitement des erreurs soit intégré à la langue.
Un autre avantage réside dans le fait que Maybe
/ Either
sont des foncteurs et des monades, ce qui signifie qu’ils peuvent tirer parti des fonctions de contrôle de flux de monades existantes (qui sont assez nombreuses) et, en général, bien jouer avec d’autres monades.
Cela dit, il y a des mises en garde. D'une part, ni Maybe
ni Either
remplacer les exceptions non vérifiées. Vous voudrez un autre moyen de gérer des choses telles que la division par 0 simplement parce que ce serait difficile de voir chaque division renvoyer une Maybe
valeur.
Un autre problème est le retour de plusieurs types d’erreurs (ceci ne s’applique qu’à Either
). Avec des exceptions, vous pouvez placer différents types d'exceptions dans la même fonction. avec Either
, vous obtenez un seul type. Cela peut être surmonté avec un sous-typage ou un ADT contenant tous les différents types d'erreurs en tant que constructeurs (cette seconde approche est celle qui est habituellement utilisée dans Haskell).
Malgré tout, je préfère l' approche Maybe
/ Either
parce que je la trouve plus simple et plus flexible.