例外を初めて実装した言語

リクルートアドベントカレンダー20日目の記事です。

adventar.org

例外を初めて実装した言語ってなんですかね?

最初にこの疑問を思ったのは、今も忘れない R-ISUCON 2021 というリクルートの社内ISUCONの運営で炎上していた時の話です。 ちなみに R-ISUCON 2021 は劇的な結果で終わっているので、興味のある方は見てみてください。

blog.recruit.co.jp

R-ISUCON 2021 では、 Node.js (TypeScript), Go, Java の3パターンの実装が出てくることが通例になっていまして、今回は Java の実装から Node.js, Go に適用していた時に一緒に実装していたメンバーからの疑問が『例外には色々な議論があるけれど、「例外を初めて実装した言語」ってどういう気持ちで実装したんだろう』という話が挙げられたので、そのネタを持ってきました。

ちなみにここで指している例外というのは、値を return した時の value が既定値以外かどうかでチェックする方法ではなく、専用の機構として例外という考え方を実装したのはどういう言語からで、どのくらいのタイミングからだろうかというのが気になったので調べてみた感じです。 return と if で既定値以外かどうかをチェックする仕組みも例外ハンドリングの一種だと思いますが、そうではなく、専用の仕組みとして例外を実装した言語は何なのかが知りたくなって聞いてみた感じです。

竹迫さんからのコメント:

takesako さんコメント
takesako さんコメント

koichikさんからのコメント:

koichikさんからのコメント
koichikさんからのコメント

実際には諸説あったので、いくつか紹介します。ただおそらく一番古いのは LISP であるという事になりそうです。

StackOverFlow からは PL/I

StackOverFlow からのコメントでは、 PL/I と CLU が挙げられていました。PL/I が 1964 年頃、 CLU が 1973 年頃に実装されていたものと言うことで、だいぶ昔の話ですね。

stackoverflow.com

PL/I は専用の機構として、 try catch 構文で表すものではなく、 SIGNAL という信号とそれを受け取る ON という専用の命令を持っていて、それを使った形で実装されているみたいです。なんか JavaScript のイベント駆動なエラー処理の方法と全く同じですね。

SIGNAL ERROR;

ON ERROR BEGIN;
.
.
END;

実際にはこの辺りに記述があります。

web.archive.org

ON-condition
An occurrence within a PL/I program of a condition that could cause a program interrupt, such as division by zero.

ON条件は PL/I のプログラム内で問題が発生した時にプログラムに割り込む事が可能になる。問題というのは例えば 0 で割った場合などを指す。

という記述がありますね。

例外的な状況に陥った時に割り込み命令のような形でエラーを出し、それによって緊急ハッチのように大域脱出な動きをするのが初期の例外という感じですね。こうなっちゃったらもう潔く死んでしまうという判断をするんでしょうけど、死ぬ前にログを書くなどの処理をしていたのでしょう。

CLU も例外ハンドリングができるようになっていますが、こちらの場合は Java で言うところの検査例外のように例外を補足する時に任意の例外を選んで補足するという when 句を使った書き方が可能になっています。

stack$pop(foo(mystack))
    except
    when empty:
        % handler code
        stream$putl(stderr, "popped empty stack")
    when foo_ex(i: int)
        stream$putl(stderr, "foo exception: " || int$unparse(i))
    when bar, baz (*):
        % ignore exception results, if any, of these
        % bar and baz may have different number and types of exception results
    others:
         % all other exceptions handled here but results are lost
    end
 % flow continues here

なんとなく、こちらの方が後発っぽいですが、最近の流れに親しきものを感じますね。

英語版 wikipedia からは LISP

en.wikipedia.org

Software exception handling developed in Lisp in the 1960s and 1970s.
 This originated in LISP 1.5 (1962), 
where exceptions were caught by the ERRSET keyword, 
which returned NIL in case of an error

1960年代から70年代にかけて、例外処理が開発されてきた。
これはLISP 1.5 (1962年から) が起源であり、
エラーの時に NIL を return する代わりに
 ERRSET キーワードによって例外をキャッチするものとして登場した。

こちらのほうが正確そうですね。 LISP が 1962 年から ERRSET なる機構を用意し、それが return ではない形で処理するための専用の機構ということです。これにはちゃんと続きがあります。

Error raising was introduced in MacLisp in the late 1960s via the ERR keyword. 
This was rapidly used not only for error raising,
 but for non-local control flow, and thus was augmented by two new keywords, 
CATCH and THROW (MacLisp June 1972), reserving ERRSET and ERR for error handling.

エラーを上げることは MacLisp 内に ERR キーワードを使って 1960 年代後半に導入されました。これは急速に利用用途が広がり、
いわゆるエラーを上げるという事だけではなく、ローカルの制御フローにも使われました。これにより2つの新しいキーワードが導入されます。 
CATCH と THROW (1972年) をローカルの制御フロー用のものとして使い、 ERRSET と ERR はそのままエラーを上げる用途として残りました。

おお、、、となると、最初は例外(エラー)を上げることと、 ローカル制御フロー用の例外的な状況、 所謂 Java で言う検査例外的なものはそもそも制御構文からして違ったわけですね。

ちなみに PL/I の SIGNAL / ON の構文は所謂エラーにも検査例外にも使われていたようです。ただ英語版 wikipedia には このような使い方をするのは現代では一般的ではない と言われてますね(JavaScript ...) 。

例外が発明された後

LISP が発明したものが MacLisp に専用構文として使われ、その後 C++ がこの機構を try catch throw を使った形で導入します。 C++ は例外処理の中で利用したリソースを解放するという目的でも利用されます。メモリの解放や open したファイルのクローズ等が必要になるため、大域的に脱出して終わりにするのではなく、 catch に入ってから後始末をやることがあります。ちなみに C++ にはデストラクタと呼ばれるオブジェクトの終了時に呼び出すメソッドがあり、 RAII (Resource Acquisition Is Initialization) という考え方も早くから使われていたのでfinally構文がなくてもリソース解放はできていました。また、 Java には finally 句があったり、 try-with-resource 構文があります。

Java は検査例外という堅牢(だけど、割と面倒な)仕組みを作り、例外処理をきちんとハンドリングさせようとしますが、 C# などの Java の影響を受けた言語にはそれが引き継がれませんでした。 Java の検査例外に対する批判は「結局例外処理を強制させようとしても、殆どのプログラマーが無視したり、そのまま投げたりしてるだけ」という話に繋がります。

この後は Go が多値の return で表現しつつ、エラーは panic で表現するなど、原点回帰していく流れをみせており、今日の流れにつながっています。

最後に

ひょんな雑談からこんな話に繋がりました。みなさんもぜひ一度は英語版の例外処理の項目を見ることをおすすめします。ちなみに Vue.js の話や React の話なんかも少しだけ書いてあります。

en.wikipedia.org