go to文は 有害である

「goto」というのは、プログラム言語の機能としては、最も基本的かつ単純なもののひとつです。これを使いこなせないようなプログラマは平均的といえるのでしょうか?

使ってはならないのは「goto」ではない | 株式会社きじねこ

については、ダイクストラは 「go to文は有害である」中の、

The go to statement as it stands is just too primitive; it is too much an invitation to make a mess of one's program.

go to文はムチャクチャ プリミティブに立脚しすぎてるから、プログラムを容易に混乱の渦へ誘ってしまう。

Go To Statement Considered Harmful

と上手く対比になっていて面白いと思いました。


わたしの個人的スタンスとしてgo to文は、原則禁止、もしくは メタフロー制御生成構文として利用するに留めるのが良いかな、と思います。


* * *


ダイクストラが、「go to文は有害である」中で 言いたかったことは、

More recently I discovered why the use of the go to statement has such disastrous effects, and I became convinced that the go to statement should be abolished from all "higher level" programming languages

近年、go to 文を使うことが なぜこんなにも悲惨な結末をもたらすのかを 見いだすにつれ、わたしは、go to 文は 「高レベル」と言われる全てのプログラミング言語から 根絶すべきと 確信するに至った。

Go To Statement Considered Harmful

ということであり、このエッセイから40年経った現在、高級言語ではほぼ ダイクストラの言うところの go to文 はサポートされていません。この「ダイクストラの言う所の go to 文」とは、C の goto などのような関数内に制限されたものとは違う 真にどこにでもジャンプ可能なものを差します。

ダイクストラなので「構造化プログラミング論」と絡めて語っちゃいますが、構造化プログラミングの「構造化」の目的は、ずばり抽象化を行うことで可読性を向上し、その結果コードリーディングによる品質の向上を期待することです。

関数でコードをくるみ、そのコードを抽象する名前を付けることで関数の中を見ることなく、一つ上の抽象階層から一群の処理の流れを把握出来るようにする、見た目の規模を減少させることによる可読性の向上、そのこと自体が構造化のメリットです。

一方、カプセルの内側を開けないと 制御の流れがどうなっているのか予断を許さなくしてしまう go to文の存在は、「存在しうる」というだけで 抽象化を台無しにしてしまいます。これはデータにおける グローバル変数とちょうど双子の関係です。

真のgo to文が根絶された結果、現状わたしたちプログラマはその職務の中で、グローバル変数を多用した「真のスパゲッティコード」のみ目にします。この真のスパゲティの超絶破壊力*1と同等以上の悲惨な結末を 真のgo to文はもたらします。グローバル変数スパゲティだけでも手に負えないのに、それ以上の魔王級なシロモノがあったとしたら...。そんなモノに悩まされない今を過ごせるというだけで、「本物のプログラマ」でないわたしは、とてもダイクストラに足を向けて寝るなんて出来ません。先生、

I became convinced that the go to statement should be abolished from all "higher level" programming languages

その確信は正しかったわっ!


* * *


一方で、C の 関数内ジャンプに制限された goto は、上記の「秘技:カプセル破り!」を行えないため、「goto文は有害である」でいう所の「有害」には当たりません。先のグローバル変数との対比でいえばローカル変数みたいな物ですね。よって、そう言う意味では C の goto はジャンジャン使っても構わない、と言うことになります。

ですがわたしは、(C のような)「構造化手続き言語の関数内」という制限された規模においても、非構造ジャンプは可読性を大きく損うと考えています。なぜなら 構造化言語の関数程度の粒度ですら「人の脳の規模への脆弱性」を顕在化させるに十分な程度に大規模だからです。

途中return が そこから先のコードを読み捨てられるのと違い、goto は「なんでもあり」の制御構造なので、流れを「ちゃんと」読まなくてはいけなくなるのですが、それは、構造化プログラミングが除去しようとしている「読まなくてはいけないコードの規模」(脳への負担)を増してしまい、結果「見ただけで正しいとすぐわかる」状態を容易に逸してしまうのです。gotoの存在はコードを塊の数珠つなぎで見ることをダメにします。ですから わたしは、goto を使う場合はよく練られた「イディオム」からチョイスして使うくらいに留めるべきだと考えています。イディオムであれば、読むとき「あぁ、このイディオムね」と 認識することができ、一行一行なめ回すような読み方をしなくても済みます。

goto の有用な例として よく挙げられるのはエラーシナリオに一気に飛ばすイディオムとかですね。C の goto 文は そう言う有用な制御構造を作るための メタフロー制御生成のための 「裏機能」だと捉えるべきじゃないかな?と思っています。


* * *


goto禁止規約の多くは、goto のメリットとデメリットを天秤にかけた結果「原則禁止」とした方がメリットがあると考えられるからです。もちろんgoto が役に立つケースや、goto を上手に使った方がコードがスッキリするケースは ありますが、それは極々限られた頻度であり、「規則は単純な方がよい」の規則により、原則禁止とする チームが多いのだと、わたしは理解しています。許可するのであれば、「ごく限られたパターン」への適用に限り例外とする、イディオムでの運用に限るべきであり、それは 一見 goto の字面をした、けれども gotoではない制限された制御構造である、と認識すべきと考えます。

つまり C に残された goto は、新しい制御構造を作るためのメタ機能と捉えるのが良いのかな、と思うわけです。ただし、goto を使って作った新しい制御構造を、新たな名前付き制御構文とする機能は残念ながら無いので、「goto」とタイプすることを 平時にも禁止出来ません。そう言う意味で「イディオムのみの許可」という落としどころが良いなぁ、と思っています。


長くなりましたがここらで結論。「goto がうまく行くパターン(=イディオム)を知っている」を「平均的プログラマ」に求めるのは良いと思うのですが、

「goto」というのは、プログラム言語の機能としては、最も基本的かつ単純なもののひとつです。これを使いこなせないようなプログラマは平均的といえるのでしょうか?

使ってはならないのは「goto」ではない | 株式会社きじねこ

のように、「プリミティブな機能を元に自由にフロー制御を創作する」は、規約レベルでは禁止(goto は 規定のイディオムでの使用に限る)とすべき。インラインアセンブラとか、C# の unsafe モードとか、そう言うのの親戚で、ごくフツーの平均的プログラマは goto は 知らなくても使いこなせなくても OK くらいで、ちょうど良い塩梅だと思うのです。


蛇足

プリミティブなものは即ち低級であるわけで、高級言語からは排除すべきである一方、「つかいよう」で無限の可能性を持つわけで、完全な排除は硬直をもたらします。そのバランスの落としどころとして、「『一般の』プログラマが goto を使わせない」という規約は、バランスのとれた 理にかなう一つの形だと考えました。――まぁ、特に目新しい意見ではないですね。新鮮みがなくてすみません。ところで、

C言語で、「ポインタ」を濫用すると危険だからといって「ポインタを使用禁止」にしないように、「goto」だけを使用禁止にするのはおかしな話です。

使ってはならないのは「goto」ではない | 株式会社きじねこ

は、いろいろなものを示唆していて面白いと思いました。

主旨にあまり影響しない重箱つつきの揚げ足取りでアレですが、C に限れば この論は破綻しています。なぜなら、goto なしでCのプログラムを「現実的なコスト」&「読みやすさ」を犠牲にせずに書くことは出来ますが、ポインタ無しでこれらをクリアするのは多分ムリだからです。従って ポインタを免罪符に goto を許すという論理は通りません。

一方 C に限らない話であれば、ポインタは排除される方向にあり、Java はもちろん C++ でさえ、プリミティブすぎる「ポインタ」は そうでないより抽象的な「参照」や「(外部)イテレータ」に姿を変えています。そう言う意味で「ポインタは許されるのにgotoは許されないなんて納得イカン」は理にかなってるなぁ、と思いました。

*1:コードをいくら読んでもどう動くのか解らない。たった一行の修正ですら、それによりどうなってしまうのかも解らない。滅びの言葉を参照のこと