型の目を、すりぬけるもの

よみもの+手を動かす時間:およそ25分

残しておいた宿題

ここまでの6つのレッスンで、型の目はずっと頼もしい味方でした。実行する前に、すべての道を読んで、種類のつじつまが合わないまちがいを残らず捕まえてくれました。

では、これは捕まえられるでしょうか。

1 / 0

レッスン3で「1 / 0 は素通りする」と予告だけして、通り過ぎたのを覚えていますか。今日はその宿題を回収する日——型の目の限界を、正直に見にいくレッスンです。

すりぬけ その1——0で割る

まず、実験です。型検査と実行、両方の返事を見くらべてください。

型の目(実行する前の検査)

型のまちがいは、見つかりませんでした。

評価された値

0では割れませんでした。

型の目は「型のまちがいは、見つかりませんでした。」と通しました。なのに実行は「0では割れませんでした。」と転びます。実行前の検査が見逃したまちがいを、実行が捕まえた——レッスン1と、立場が逆です。

なぜ通したのか。型の表を引くと、/ が受け取るのは「数 と 数」——1 は数、0 も数で、つじつまは完璧だからです。0は型ではなく、値なのです。

思い出してください。型の読みは「封を切らずに仕分ける」読み方でした。封を切らないからこそすべての道を読めるのですが、封を切らないからこそ、中身の——その数がたまたま0かどうか——は見えません。

よりみち:0を、数の国から追い出せないか

「0だけ別の型にすれば捕まえられるのでは」——よい着眼です。実際、「0でない数」のような細かい型を持つ言語は研究されていて、一部は実用にもなっています。ただし型を細かくするほど、つじつまの確認は難しくなり、書く人の手間も増えます。どこまで型に語らせるかは、言語設計の現在進行形の問いです。

すりぬけ その2——止まらないくりかえし

次の実験です。コース2で作ったwhileに、ご登場願います。

型の目(実行する前の検査)

型のまちがいは、見つかりませんでした。

i
評価された値

プログラムがいつまでも とまりませんでした。くりかえしの「終わる条件」は、いつか本当に成り立ちますか?

型の目を、たどってみます。i は数、i > 0 は「数 と 数」をくらべるので真偽、whileの条件としてつじつまが合い、i + 1 も数。どこにも、ほころびがありません。

なのに実行すると「プログラムがいつまでも とまりませんでした。」——燃料切れです。i は増える一方で、i > 0 が false になる日は来ません。

これは型の目の手抜きでしょうか。ちがいます——「このプログラムは止まるか」を実行せずに必ず言い当てることは、どんな検査にも原理的にできないと証明されているのです。停止性問題と呼ばれる、計算そのものの限界です(コース5で、証明まで詳しく歩きます)。

レッスン1で「実行前の検査には、登れない崖と、登れる丘がある」と言いました。型の目が止まらないループを通すのは、丘の上にとどまっている証拠——崖に手を出さないからこそ、型検査は必ず終わり、必ず答えを返せるのです。

すりぬけ その3——意図のまちがい

最後の実験が、いちばんたちが悪いものです。ab の平均を出すつもりで、(a + b) / 2 と書くべきところを——かっこを忘れて a + b / 2 と書いてしまったとします。

型の目(実行する前の検査)

型のまちがいは、見つかりませんでした。

ab
評価された値
102015

正しい版の答えは 15。では最後の行を a + b / 2 に変えてみてください。わり算が先にまとまるので b / 2 が 10、それに a を足して——答えは 20 になります。

注目してほしいのは、型の目がどちらも通すことです。どちらも数と数の合法な計算で、種類のつじつまに、けちのつけようがありません。しかも今度は実行さえ転ばず、まちがった答えが静かに出てきます。

前のレッスンで「型は意味までは語らない」と言いました。その延長線の、いちばん遠い地点がここです。「平均を出したい」という意図は、プログラムのどの行にも書かれていない——書かれていないものは、どんな検査にも読めません。

3つのすりぬけの、正体

並べてみると、すりぬけたものの正体が見えてきます。

すりぬけ型の目に見えなかったもの
1 / 0——その数が、たまたま0かどうか
止まらないwhile時間——実行が、いつか終わるかどうか
かっこ忘れの平均意図——何を計算したかったのか

型の目が見ているのは、種類のつじつま、それだけです。レッスン1の言い方に戻るなら——実行は一本の道しか歩けないかわりに、道の上の石ころにちゃんとつまずいてくれます。型の目はすべての道を見わたせるかわりに、道の上の石ころまでは見ていないのです。

それでも、型を使う理由

ここまで読んで、「型、思ったより頼りない」と感じたかもしれません。でも見方を変えてください——種類のまちがいというまちがいの一種類が、丸ごと消えるのです。

「数に真偽を足す」たぐいのまちがいは、どの道に隠れていようと、実行前に全部捕まる。書く人は、その種類の心配をしなくてよくなる。まちがいの種類がひとつ消えるだけで、残りの心配——値と、時間と、意図——に集中できます。

では残りは誰が見るのか。テストです——実行する検査であるテストは、実際の値でプログラムを走らせるので、0除算にもまちがった平均にもつまずけます。テストと型は競争相手ではなく、分業なのです。

ただし、ここでも正直でいましょう。二枚重ねても、すりぬけるまちがいはあります——完璧な検査は存在しません。だからこそ「どのざるが何をこせるか」を知っていることが、書く人の腕になるのです。

演習:すりぬけるか、捕まるか

次の3つを、実行する前に予言してください——型の目は通すでしょうか。通すなら、実行で何が起きるでしょうか。予言してから、上の実験室で確かめます。

  1. 10 / (5 - 5)
  2. 3から1ずつ減らして止めるつもりで、-+ と書いてしまった:i = 3 のあと while i > 0 { i = i + 1 }
  3. 1 + (2 == 2)
ヒント1(考え方)

それぞれのまちがいが「種類」「値」「時間」「意図」のどれに属するかを考えてください。型の目が捕まえられるのは、ひとつだけです。

こたえ

1は通します。5 - 5 は型の表では立派な「数」で、その値が0になることは見えません。実行すると「0では割れませんでした。」と転びます。

2も通します。型のつじつまは全部合っていて、実行すると燃料切れです。元をたどれば -+ の書きまちがい——意図のまちがいが時間のまちがいを生んだ例で、すりぬけは重なって起こることもあります。

3は捕まえます。真偽に数を足す——これは種類のつじつまの問題なので、型の目のど真ん中の守備範囲です。

このレッスンで分かったこと

  • 型の目が見ているのは種類のつじつまだけ。(0かどうか)・時間(止まるかどうか)・意図(何を計算したかったか)は見えない
  • 止まらないループを通すのは手抜きではない——「止まるか」の判定は原理的に不可能(停止性問題。コース5で証明まで歩く)
  • 意図のまちがいは、エラーにすらならずに、まちがった答えを静かに返す
  • それでも型を使うのは、まちがいの一種類が丸ごと消えるから
  • テストと型は、網の目のちがう二枚のざる——競争相手ではなく分業。ただし二枚重ねても、完璧にはならない