Parcours
/
Elixir
Elixir
/
Exercices
/
Calculatrice RPN
Calculatrice RPN

Calculatrice RPN

Exercice d'apprentissage

Introduction

Erreurs

Les erreurs arrivent. En Elixir, même si l'on dit souvent « let it crash », il arrive qu'on doive rattraper l'appel de fonction pour revenir à un état connu et fiable afin de respecter un contrat logiciel. Dans certains langages, les erreurs servent de mécanisme de contrôle de flux, mais en Elixir, ce schéma est déconseillé. On peut souvent reconnaître les fonctions susceptibles de lever une erreur à leur nom : les fonctions qui lèvent des erreurs doivent avoir un ! à la fin de leur nom. À comparer avec les fonctions qui renvoient {:ok, value} ou :error. Regarde ces exemples de bibliothèque :

Map.fetch(%{a: 1}, :b)
# => :error
Map.fetch!(%{a: 1}, :b)
# => raises KeyError

Try/Rescue

Elixir propose une construction pour rattraper les erreurs avec try .. rescue.

try do
  raise RuntimeError, "error"
rescue
  e in RuntimeError -> :error
end

Examinons cette construction :

  • Ligne 1, le bloc est déclaré avec try.
  • Ligne 2, l'appel de fonction qui peut lever une erreur est placé ici, dans ce cas, on appelle raise/2.
  • Ligne 4, dans la section rescue, on filtre par motif sur le nom du module de l'erreur levée
    • du côté gauche de -> :
      • e correspond à la structure d'erreur.
      • in est un mot-clé.
      • RuntimeError est l'erreur que l'on veut rattraper.
        • Si on voulait rattraper toutes les erreurs, on pourrait utiliser _ à la place du nom du module ou omettre complètement le mot-clé in.
    • du côté droit :
      • les instructions à exécuter si l'erreur correspond.

Structures d'erreur

Les erreurs (parfois aussi appelées « exceptions ») que l'on rattrape de cette façon sont des structures. On rattrape très rarement les erreurs en Elixir. En général, l'erreur rattrapée est journalisée ou envoyée à un service de supervision externe, puis relancée. Cela signifie qu'on ne se soucie généralement pas de la structure interne de l'erreur concernée.

Dans le concept Exceptions, tu en apprendras plus sur les structures d'erreur, notamment comment définir ta propre erreur personnalisée.

Instructions

Pendant que tu travailles chez Instruments of Texas, on te confie une calculatrice expérimentale en notation polonaise inversée [RPN] écrite en Elixir. Ton équipe rencontre un problème : certaines opérations lèvent des erreurs et font planter le processus. On t'a demandé d'écrire une fonction qui encapsule la fonction d'opération afin que les erreurs puissent être gérées plus élégamment avec du code Elixir idiomatique.

1. Préviens l'équipe

Implémente la fonction calculate!/2 pour appeler la fonction d'opération en lui passant la pile comme seul argument. La fonction d'opération est définie ailleurs, mais tu sais qu'elle peut soit se terminer correctement, soit lever une erreur.

stack = []
operation = fn _ -> :ok end
RPNCalculator.calculate!(stack, operation)
# => :ok

stack = []
operation = fn _ -> raise ArgumentError, "An error occurred" end
RPNCalculator.calculate!(stack, operation)
# => ** (ArgumentError) An error occurred

Les noms de fonctions qui se terminent par ! avertissent les programmeurs que cette fonction peut lever une erreur

2. Encapsule l'erreur

En poursuivant tes recherches, tu remarques que beaucoup de fonctions utilisent des atomes et des tuples pour indiquer leur réussite ou leur échec. Implémente calculate/2 avec cette stratégie.

stack = []
operation = fn _ -> "operation completed" end
RPNCalculator.calculate(stack, operation)
# => {:ok, "operation completed"}

stack = []
operation = fn _ -> raise ArgumentError, "An error occurred" end
RPNCalculator.calculate(stack, operation)
# => :error

3. Transmets le message

Certaines erreurs contiennent des informations importantes dont tes collègues ont besoin pour garantir le bon fonctionnement du système. Implémente calculate_verbose/2 pour transmettre le message d'erreur. L'erreur est une struct qui possède un champ :message.

stack = []
operation = fn _ -> "operation completed" end
RPNCalculator.calculate_verbose(stack, operation)
# => {:ok, "operation completed"}

stack = []
operation = fn _ -> raise ArgumentError, "An error occurred" end
RPNCalculator.calculate_verbose(stack, operation)
# => {:error, "An error occurred"}
Modifie via GitHub Le lien s'ouvre dans une nouvelle fenêtre ou un nouvel onglet
Elixir Exercism

Prêt à commencer Calculatrice RPN ?

Inscris-toi sur Exercism pour apprendre et maîtriser Elixir avec 58 concepts168 exercices, et un vrai mentorat humain, le tout gratuitement.