Utiliser Maybe(ou son cousin Eitherqui 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 indexOfretournera une Maybevaleur parce que vous vous attendez à ce que l'élément ne soit pas dans la liste. Cela revient un peu à revenir nulld’une fonction, sauf d’une manière qui respecte le type, ce qui vous oblige à traiter le nullcas. Eitherfonctionne 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...catchdéclaration. Pour la Eitherfonction, 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 Eithers'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..catchet, plus important encore, plus sémantiques. Utiliser une fonction comme <|>ou qui optionalrend vos intentions beaucoup plus claires que d’utiliser try...catchtoujours 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 Maybeet en Eithertant 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 Nothingdonné 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/ Eitherest 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 throwsinstructions) 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/ Eithersont 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 Maybeni Eitherremplacer 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 Maybevaleur.
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/ Eitherparce que je la trouve plus simple et plus flexible.