Une exception est pour l'anormal — pas pour un cas qu'on attendait.An exception is for the abnormal — not for a case you expected.
On a appris à lever et à structurer les erreurs ; ce dernier numéro demande l'inverse : quand NE PAS lever. Une exception déclare un événement exceptionnel — un état impossible, une invariant brisée. Mais beaucoup de cas qu'on traite par raise ne sont pas exceptionnels du tout : une clé absente, une fin de fichier, un utilisateur introuvable, une saisie invalide. Ce sont des issues attendues, prévues, fréquentes. Les lever coûte une stack trace construite puis jetée, et détourne le flux pour une situation normale. Souvent, une valeur de retour — un nil, un objet Result — dit mieux et moins cher ce que l'exception criait.We learned to raise and to structure errors; this last issue asks the opposite: when NOT to raise. An exception declares an exceptional event — an impossible state, a broken invariant. But many cases handled by raise aren't exceptional at all: a missing key, an end of file, a user not found, invalid input. These are expected, anticipated, frequent outcomes. Raising them costs a stack trace built then discarded, and diverts the flow for a normal situation. Often, a return value — a nil, a Result object — says better and cheaper what the exception shouted.
# « introuvable » est-il une ERREUR, ou un résultat ATTENDU ? # version exception : lève pour une absence pourtant banale def find_user(id) users[id] || raise(NotFound, "user #{id}") # construit une stack trace… end find_user(99) rescue nil # … qu'on jette aussitôt # version valeur : l'absence est un nil, dit sans drame def find_user(id) = users[id] # => nil si absent user = find_user(99) greet(user) if user # le cas « absent » fait partie du flux normal
L'exception est un détour de contrôle, justifié quand le cas est rare et anormal. Pour un cas prévu, le détour devient du bruit : la valeur de retour garde le flux droit, lisible, et sans le coût de la trace.The exception is a control detour, justified when the case is rare and abnormal. For an expected case, the detour becomes noise: the return value keeps the flow straight, readable, and without the trace's cost.
« Est-ce vraiment exceptionnel, ou un cas normal qu'un nil ou un fetch avec défaut exprimait mieux ? » Si l'absence, l'échec ou la fin sont attendus et fréquents, ce n'est pas une exception : c'est une issue à rendre comme valeur, et à traiter dans le flux."Is this really exceptional, or a normal case a nil or a fetch with a default would express better?" If the absence, failure or end are expected and frequent, it's not an exception: it's an outcome to return as a value, and handle in the flow.
Le Vol 7 referme sur le discernement. Le № 1 a donné le mécanisme, le № 2 l'arbre des types ; celui-ci pose la question d'avant tout cela — faut-il lever ?. Le dev venant d'un langage où l'exception est partout (ou de celui où tout est code d'erreur) doit réapprendre la ligne : l'exception pour ce qui ne devait pas arriver, la valeur de retour pour ce qui peut très bien arriver. Confondre les deux, c'est soit noyer le flux normal sous des rescue, soit perdre les vraies erreurs dans des codes de retour ignorés.Vol 7 closes on discernment. № 1 gave the mechanism, № 2 the type tree; this one asks the prior question — should you raise at all?. A dev from a language where exceptions are everywhere (or one where everything is an error code) must relearn the line: the exception for what shouldn't happen, the return value for what may well happen. Confusing the two either drowns the normal flow under rescues, or loses real errors in ignored return codes.