世間の嫌われ者を、あえて擁護したり偏愛するつもりはないのですけど、goto文や大域変数は面白いし、好きだなー。過去の記事を参照しながら、goto文や大域変数の面白さについてナニヤラごたごたと書いてみます。
内容:
- gotoの棲み家
- 大域変数と例外の扱い方
- 必要悪としてのキャッチとミューテーション
gotoの棲み家
タチの良いgoto文は、エルゴット繰り返し(Elgot repetition)演算子とみなされます。エルゴット繰り返し演算子は、コンウェイ不動点演算子の双対です。そのことは次の記事で述べました。
あんまりタチの良くないgoto文もCPS(継続渡し方式)変換でだいたいは説明が付きます。
マイク・ステイ(Mike Stay)が指摘しているように、CPS変換は米田埋込みです。この解釈を採用するなら、goto文の意味(セマンティクス)は、米田埋込みの埋め込み先に存在します。圏Cを埋め込む先は関手圏 [Cop, Set] です。[Cop, Set] は、集合値関手とそのあいだの自然変換の圏ですが、C上の(集合値の)前層とも呼びます。つまり、goto文を含むプログラムの意味はベース圏上の前層のなかに棲んでいる、と考えることができます。
通常の関数に比べると、goto制御構造は随分と高いところ(higher order)に棲んでいます。上から目線だから嫌われるのでしょうか?
大域変数と例外の扱い方
状態と例外が双対であることは何度か述べています。
状態とは環境(の一部)であり、有り体に言えば大域変数です。「大域」とは言っても、スコープの取り方はいろいろあるので、クラスのインスタンス変数くらいまでスコープを制限すれば、十分に扱いやすいものです。このようなスコープの制限はインデックス付き圏でうまく表現できます。
一番望ましいのは、環境=大域変数をイミュータブルにすることでしょう。
イミュータブルな大域変数はコモナドとなり、例外との双対性が鮮明になります。
大域変数 | 例外 |
---|---|
デカルト構造 | 余デカルト構造 |
入力(域) | 出力(余域) |
直積 | 直和 |
終対象 | 始対象 |
コモナド | モナド |
必要悪としてのキャッチとミューテーション
僕は、非常事態に例外を投げるのはいいが、原則として例外のキャッチはすべきではないと思っています。サブプログラムやライブラリでなにか破滅的な事が起きたら、メインプログラムも破綻の危険性があるのでサッサと異常終了すべきだ、ということです。
しかしそうはいっても、現実には例外を捕捉して実行を続ける必要もあります。「望ましくはないが使わざるを得ない」という意味で、キャッチは必要悪です。「悪」と言いましたが、実は、キャッチと例外ハンドラーを含めた挙動も割ときれいな(悪くない)定式化を持ちます。
面白いのは、大域変数と例外の双対性を使って眺めると、例外をキャッチする行為の双対は大域変数を上書き変更することなのです。「例外をキャッチすべきではない」と「大域変数をミューテーションすべきではない」という方針が、双対な言明として繋がっているのです。
そして、例外のキャッチが「望ましくはないが使わざるを得ない」のと同様に、大域変数のミューテーションも「望ましくはないが使わざるを得ない」ものです。比較的に安全で構造的な例外処理パターンがあるのと同様に、大域変数を構造的にミューテーションする方法があります。それは単に、通常の例外処理の双対を取ればいいのです。
別な言い方をすると、プログラムの実行単位に偽の大域変数(環境)を渡して実行するような制御構造ですね。そのような制御構造を明白なプリミティブとして持っているプログラミング言語を知らないのですが、テストのときなどは実際に使っています。外部からの情報をDI(依存性注入)で渡したりするのは典型的な手法です。
例外処理と双対的な言語コンストラクトがあまりなかったので、DIなんてのが考案されたのかも知れませんね(憶測)。