Ausnahmen entstanden als Verallgemeinerung von Fehlern. Die erste Programmiersprache, die einen Ausnahmemechanismus enthielt, war Lisp in den frühen 1970er Jahren. Es gibt eine gute Zusammenfassung in Ein Muster der Sprachentwicklung von Gabriel und Steele. Ausnahmen (die noch nicht als Ausnahmen bezeichnet wurden) ergaben sich aus der Notwendigkeit, das Verhalten eines Programms im Fehlerfall festzulegen. Eine Möglichkeit besteht darin, das Programm anzuhalten, dies ist jedoch nicht immer hilfreich. Lisp-Implementierungen hatten traditionell die Möglichkeit, den Debugger bei einem Fehler aufzurufen, aber manchmal wollten Programmierer die Fehlerbehandlung in ihr Programm aufnehmen. So hatten die Lisp-Implementierungen der 1960er Jahre die Möglichkeit zu sagen: "Tun Sie dies, und wenn ein Fehler auftritt, tun Sie dies stattdessen." Ursprünglich stammten Fehler aus primitiven Funktionen, aber Programmierer fanden es bequem, absichtlich einen Fehler auszulösen, um einen Teil des Programms zu überspringen und zum Fehlerbehandler zu springen.
1972 erschien die moderne Form der Ausnahmebehandlung in Lisp in MacLisp: throw
und catch
. Die Software Preservation Group listet eine Menge Material zu frühen Lisp-Implementierungen auf, einschließlich des MACLISP Reference Manual Revision 0 von David Moon . Die Grundelemente catch
und throw
sind in §5.3 S.43 dokumentiert.
catch
ist die LISP-Funktion für strukturierte nicht-lokale Exits. (catch x)
wertet aus x
und gibt seine Werte zurück, mit der Ausnahme, dass, wenn während der Auswertung von x
(throw y)
ausgewertet werden soll, catch
sofort y
ohne weitere Auswertung zurückgegeben wird x
.
catch
kann auch mit einem zweiten, nicht bewerteten Argument verwendet werden, das als Tag zur Unterscheidung zwischen verschachtelten Fängen verwendet wird. (…)
throw
wird catch
als strukturierter nichtlokaler Austrittsmechanismus verwendet.
(throw x)
wertet aus x
und wirft den Wert auf den neuesten zurück catch
.
(throw x <tag>)
Löst den Wert von x
zurück auf den zuletzt catch
mit <tag>
oder ohne Beschriftung gekennzeichneten Wert aus .
Der Fokus liegt auf dem nichtlokalen Kontrollfluss. Es ist eine Form von goto (ein nur aufwärts gerichtetes goto), das auch als Sprung bezeichnet wird . Die Metapher ist , dass ein Teil des Programms führt den Wert auf die Exception - Handler zurück, und die Exception - Handler Fänge dieser Wert und gibt ihn zurück.
Die meisten heutigen Programmiersprachen packen das Tag und den Wert in ein Ausnahmeobjekt und kombinieren den Fangmechanismus mit einem Handhabungsmechanismus.
Ausnahmen sind nicht unbedingt Fehler. Sie sind eine Möglichkeit, einen Codeblock und die umgebenden Blöcke zu verlassen und zu entkommen, bis ein Handler für die Ausnahme erreicht ist. Ob so etwas als "Fehler" im intuitiven Sinne aufgefasst wird, ist subjektiv.
Einige Sprachen unterscheiden zwischen den Begriffen "Fehler" und "Ausnahme". Einige Lisp-Dialekte müssen beispielsweise throw
eine Ausnahme auslösen (Kontrollfluss für Benutzer, der einen nicht lokalen Exit ausführen soll, der nicht anzeigt, dass etwas „falsch“ gelaufen ist) und signal
einen Fehler auslösen (der darauf hinweist, dass dies der Fall ist) Etwas ist „schief gelaufen“ und kann ein Debug-Ereignis auslösen.