ダックタイピング
2005年12月21日
おそらく私はナイーブだったのでしょうが、HumaneInterfaceに関する私の投稿がこれほど話題を呼ぶとは予想していませんでした。残念なことに、大部分はRubyのArrayとJavaのListのどちらが優れているかという議論に終始し、私が伝えようとしていた本質的な点は置き去りにされてしまいましたが、それでもいくつかの興味深い議論が生まれたと思います。
(ただし、RubyのArrayの方が優れている、あるいはRubyの方が優れていると言おうとしたわけではないことを指摘しておくべきだと思います。文脈によってはどちらが良いとも思いません。実際、RubyのArrayとJavaのListのどちらも気に入っています。設計は全く異なりますが。どんなソフトウェアにも欠陥がありますし、誰もがまさに自分が今欲しいと思う通りのクラスを書くわけではありませんが、自分のコードをそれらと競わせる気はありません。重要なのは、どちらも有用で、どちらも頻繁に使用しているということです ― それが例として思い浮かんだ理由です。)
これらの議論の一つから、ArrayとListの違いには、人間的な/最小限の哲学以外の理由もあることが分かりました。これらの理由の一つは、同様の機能が2つの言語で異なる役割を果たす方法に関するものです。
RubyのArrayには、リストを見たときに数人の人を困惑させたメソッドがいくつかあります。Pushとpopは、Elliotteが言ったように「スタックをリストにプッシュした」のです。shiftとunshiftもあります。これは正しく見えません ― Elliotteは再び「キューまたはスタックを使用している場合、ListクラスではなくQueueまたはStackクラスを使用すべきではないですか?」と言っています。
これを読んで、記憶の片隅にある考えが刺激されました。Smalltalk Best Practice Patternsという素晴らしい本を取り出しました。オブジェクト指向を真剣に理解したいと考えている人なら誰でも読むべき本です(奇妙な構文にもかかわらず)。
「多くの人がSmalltalkを使い始めるときに最初に作成するオブジェクトの一つがStackです。Stackは基本的なデータ構造であり、歌、物語、そして理論的なプログラミング言語に関する何百もの論文の中で語り継がれています…しかし、基本的なイメージにはStackクラスがありません。何度も作成されているのを見たことがありますが、長くは続きません。」
-- ケント・ベック
少なくとも当時、Smalltalkのアプローチは、RubyのArrayに相当するSmalltalkのOrderedCollection
を使用することでした。pushやpopはありませんでした。代わりに、KentはaddLast:
とremoveLast:
の使用を示しました。
Kentは、スタック(とキュー)の欠如について説明していません。「なぜSmalltalkにStackがないのですか?それは単に「そういうものだから」です。OrderedCollectionを使用してスタックをシミュレートすることが文化の一部になっています。」
私はこの点についてどう感じているか分かりません(そして、Kentの文章の中に不確実性を感じます)。キューのようなものを使用する場合、OrderedCollection new
ではなくStack new
と言い、addLast
とremoveLast
ではなくpush
とpop
を使用する方が理にかなっています。
この状況の一部は、静的言語と動的言語の違いによるものかもしれません。静的言語は厳密な型インターフェースを介してオブジェクトと通信しますが、動的言語には複数の役割を果たすことができるクラスがあります ― ダックタイピング。Javaにも、キューとしても機能するリストがあります:LinkedListですが、通常は共通の実装に気づかずに、個別のインターフェースを介して使用します。Smalltalkの考え方は、OrderedCollectionで必要なものが得られるので、なぜ別のクラスを作成する必要があるのでしょうか?Rubyは、その反応を反映し、そのコンテキストで使用している人々にとって意味のあるメソッド名を付け加えているようです。(ただし公平を期すために、rubyistが実際にArrayをスタックとして使用して満足しているのか、それとも後悔しているレガシーなのかは分かりません。)
もう一つの要因は、言語がこれらの構造の実装に推奨するものことです。チャールズ・ミラーが言ったように、「Javaの設計は小さなインターフェースと、ヘルパークラスの静的メソッドとして提供されるユーティリティ関数を提供します。Rubyの設計は、ミックスインされたユーティリティメソッドを持つ大きなクラスを提供します。」
おそらくこの結論の一つは、ある言語のクラスの機能を別の言語の値を使って判断することに注意する必要があるということです。ArrayはListと同等ですか?それとも、コレクションパッケージの様々なインターフェースと実装を含むListと同等ですか?それとも、さらに複雑なものですか?リストに対して78個ものことを行うという考えに嫌悪感を抱く人もいるかもしれませんが、Lisp使いならもっと多くのことを追加することを考えるでしょう。RubyのArrayには欠点がありますが、Javaのコレクションよりも使いやすく感じています。これが人間的なインターフェースのガイドラインによるものなのか、Rubyの構文サポートによるものなのかは分かりません。
全体的に見て、誰に同意するべきか、あるいはそれがそれほど重要なのかどうかは分かりません。これらの議論の多くと同様に、最も興味深いのは、両方の観点を理解しようとすることです。