教育のようなプログラミング
本記事は、Mikel Evins氏のProgramming as teachingの翻訳を、本人の御了承を得て公開するものです。なお、関連記事であるOn repl-driven programmingの翻訳も公開していますので、どうぞご覧ください。
教育のようなプログラミング
2020-02-03 by Mikel Evins
ほとんどのプログラミングは 「大工仕事のようなプログラミング」 とでも呼ぶべきアプローチで行われます。私たちは作りたいもののアイデアを思いつくところから始めます。そのアイデアを分析して、必要な部品の集合にします。そして、作業台で必要な部品を作り、それを組み立てて完成させ、その出来栄えを検証して、目標にどれだけ近づいたかを評価するのです。
次に、施工上のミスや、当初のプランに間違いがあったなど、思い通りになっていない細部を修正する作業を繰り返します。
プログラミングには別のアプローチがあります。それは 「教育のようなプログラミング」 とでも呼ぶべきものです。このアプローチでは、まずランタイム(実行環境)を起動します。ランタイムはプログラムとして動作する方法をすでに知っていますが、私たちの特定のアプリケーションとして動作する方法を知らないだけなのです。それでランタイムと対話することで、私たちが必要とする機能を徐々に教えていきます。ランタイムがそれらのすべての機能を提供する方法を知れば完了です。蓄積された変更を成果物に保存し、その成果物が私たちの製品になります。
作業台で工作物を釘で打ち付けるというよりは、学生に新しい技術を教えるようなものです。
大工仕事プログラミングは教育プログラミングよりもはるかによく知られており、広く使われています。あえて言うならば、ほとんどのプログラマはプログラミングを教えるという選択肢があることすら知らないでしょう。人によってはその方が良い選択肢になるのに、残念なことだと思います。
私は、教育パラダイムが絶対的、客観的に優れているとは言いません。ただ、ある人たちにとってはより良いということです。たとえば私の場合、教育パラダイムでプログラミングをすることで、より幸せに、より速く、より生産的に仕事ができるようになりました。
私だけではありません。教育パラダイムを好むプログラマは他にもたくさんいます。このパラダイムが無名の少数派である理由は、大工仕事プログラミングに精通しているプログラマの数がはるかに多いからです。
ほとんどのプログラマが教育パラダイムが存在することさえ知らないようだという事実は、もしそれがもっと広く知られていれば、もっと多くの人がそれを使うだろうと私に思わせるものです。ほとんどの人がその存在を知らないのに、無名のパラダイムを好む人たちが皆すでにそれを使っている公算がどれほどあるというのでしょう?
ある意味では、どうでもいいことです。ソフトウェアの大部分は、大工仕事パラダイムで開発され、業界は繁栄しているのですから。異なるパラダイムを伝道することが本当に重要でしょうか? 少なくとも業界全体から見れば、そうではないでしょう。
一方で、プログラマである私たちのためには重要だと思います。もし私が教育プログラミングに触れていなかったら、おそらく自分がどれだけ幸せで生産的な仕事ができるかを知ることはなかったでしょう。教育というパラダイムを知っている世界は、少なくとも私にとってはより良い世界です。もし、教育パラダイムを好むプログラマが他にもいて、その人たちが教育パラダイムを知らないのなら、もっと良い世界が待っているはずです。彼らに必要なのはそれを知ることだけなのです。
では、教育プログラミングがそんなに素晴らしいアイデアなら、たとえそれが少数のプログラマにとって素晴らしいものだとしても、なぜもっとよく知られていないのでしょうか? それは、教育プログラミングには、大工仕事プログラミング以上に、いくつかの道具が必要だからだと思います。その道具を使えるようにするために余分な作業をしなければならないし、その道具について知ってからでないと、それを実行することはできないのです。
教育パラダイムを知らなくてもしっかりした大工仕事の作業台を作ることはできますが、逆は真ではありません。優れた「教育プログラミング」システムには、大工仕事用の道具がすべて必要になります。パーサー、データ構造、コードウォーカー、コードジェネレーターが必要です。ファイルI/Oやパフォーマンスツール、デバッガなども必要です。これらはすべて必要ですが、それ以外にも必要なものがあります。
教育のようなプログラミングとは、アプリケーションを定義する前に実行を開始し、実行中に機能を定義し、再定義することです。メモリの内容を調べ、制御構造を実行の途中で停止させ、その動的コンテキストを調べ、変更し、再定義することを意味します。スタック上に保留されている関数や、既存のインスタンスで使用されているデータ型の定義を更新し、アプリケーションの内部をいじっている間、アプリケーションが合理的に動作し続けることを期待していることを意味します。
これらの機能はすべてランタイムの実質的なサポートが必要で、うまく動作させたいなら、ランタイムは最初からそれをサポートするように設計されている必要があります。
Smalltalkシステムでは、そのようなプログラミングを徹底的にサポートしています。Common Lispや他の古いタイプのLispシステムも同様です。SmalltalkとCommon Lispが、そしてFORTHシステムがある程度、このようなサポートをしていることを除くと、「教育プログラミング」をする機能をすべて備えた開発システムはあまり多くありません。
これらの機能のいくつかは、現代の「大工仕事プログラミング」システムにも何らかの形で存在しますが、「教育プログラミング」システムにはなりません。実用的な「教育プログラミング」システムを構築するには、それをサポートするランタイム全体を一から設計する必要があるのです。どういうことか、例を挙げて説明しましょう。
ANSI Common Lisp規格では、UPDATE-INSTANCE-FOR-REDEFINED-CLASSというジェネリック関数が定義されています。ランタイムは、インスタンス化された後にクラスが再定義された値(オブジェクト)を検出すると、自動的にこの関数を呼び出して、新しい定義に適合するようにオブジェクトを再構築します。事実上、ランタイムはオブジェクトを古い定義ではなく、新しい定義のインスタンスとして遡及して作成するのです。
UPDATE-INSTANCE-FOR-REDEFINED-CLASSを特殊化する(特定のクラスに対応するメソッドを定義する)と、新しい定義に適合するようにインスタンスを正しく再初期化できるようになります。そうしないと、ランタイムは、新しい初期化を対話的に提供できる対話型セッションにあなたを誘います。その後、実行を再開すると、影響を受けるオブジェクトは、更新された定義から元々インスタンス化されていたかのように動作することができます。
「大工仕事プログラミング」から来た人は、なぜこのような関数が必要なのか、ましてや言語標準の一部であるべきなのかを疑問に思うかもしれません。しかし、Common Lispは経験豊富なLispのユーザと実装者によって設計されました。彼らは教えるようなプログラミングを実践してきました(ただし、彼らはそれを単に「プログラミング」と呼んでいました)。
Common Lispの設計者にとっては、Lispを起動して、自分たちの望むプログラムになるように定義ごとに教えていくのが普通の開発方法だったのです。何かを再定義したからといって、Lispを再起動しないといけないとは思っていなかったはずです; ばかばかしいです。彼らがやったことは再定義だけなんですから。そうではなくて「再定義して、そのまま継続すること」が期待されていたのです。ランタイムは、あなたが伝えた変更に合理的な方法で適応するのが仕事です。また、どうしたらいいかわからないときは、あなたに助けを求めるのもランタイムの仕事です。
UPDATE-INSTANCE-FOR-REDEFINED-CLASSは、ANSI Common Lisp規格で定義された標準プロトコルの一要素で、開発中のプログラムを実行し続けながら変更や再定義を行うことができます。規格では、クラスや関数の定義、バインディングの更新、動的環境のすべてを検査・変更できる対話型セッションにあなたを誘ってエラーの検出やエラーが発生した関数に新しい定義で実行を再開するように指示する機能、そして一般に実行プログラム自体との対話的会話を通じてプログラムの構築と配備のあらゆる側面を達成するための機能が説明されています。
Smalltalkシステムも同じような設計になっています。
このような開発環境は、パーツの総和を超えたものです。手に入れるためには、それをサポートする言語とランタイムを最初から設計する必要があります。大工仕事のためのシステムを教えるためのシステムに変えるには、一つずつ機能をパッチしていくのでは無理なのです。
「教育プログラミング」の機能一式をサポートしている開発ツールチェーンは非常に少ないです。しかし、そのほとんどは長い間使われてきたものです。新しい言語やランタイムは、古いLispやSmalltalkのシステムで具現化されたシステム全体のプログラミングパラダイムを全く知らず、理解せずに設計されていることがほとんどです。新しいLispでさえ、そのような理解なしに設計されたものがあります。
私は時々、「教育プログラミング」が消えつつあることを心配し、私の好きなパラダイムがいつか完全に消滅してしまうかもしれないと悲しくなることがあります。
もしそうなっても、ソフトウェア開発の終わりとか、そういう終末論的な話にはならないと思うんです。でも、それがなくなったら寂しいし、新しい世代のプログラマがそれを経験する機会がなくなったら悲しいと思います。
プログラムを実行しながら対話し、徐々に自分の望むアプリケーションに変えていくというのは、何か不思議な感じがします。一部のプログラマにとっては、これが最高の仕事のやり方なんです。私はそれを失いたくないですね。
訳者後記:
-
ANSI Common Lisp では、エラー等による実行中断時にユーザに関数を修正する機会を与えたり、修正後にその近傍から実行を再開したりする機能の提供に、通例 common lisp condition system を用いることになります(Smalltalkにも同様の機構があります)。それで、condition system の知識を得ることで、本記事の内容をより具体的に理解できます。
-
ただし、そういったことに関連する注解や、本記事の掘り下げに必要十分なcondition systemの内容について、本記事自体には注記しないことにしました。余分な注記が増えると原著のテイストが損なわれると考えたためです。
-
本稿の下訳作成には DeepL(free)を用いました。
Discussion