at posts/single.html

Duck Typing

rubyco(るびこ)の日記 - Duck Typingは大規模プロジェクトでも大丈夫か?に反応。コメント欄に書いてたら長くなっちゃったのでこっちに。

そういえば、Duck Typingでは「メソッド名がグローバル」になりますね…。ふと思ったのですが「大規模プロジェクトでメソッド名がコンフリクトしてDuck Typingが破綻する」という可能性はあるでしょうか?

(0) この問いかけ自体が無意味。なぜなら…(誰かがここを埋める)

(1) 大規模プロジェクトでもDuck Typingは破綻しない。なぜなら…(誰かがここを埋める)

(2) 大規模プロジェクトでDuck Typingは破綻する可能性はある。でも、その可能性はほかの型機構でも似たり寄ったり。

(3) その他。

んー。(2)かなぁ。  個人的には大規模かどうかはあんまり関係ない気がしてきた。

Duck Typing を使う基準として、以下の「どちらか」が満たされていると考えてみる。

  • 処理対象となる変数の型がほぼ予測可能であること。例えば、元の例のように整数か文字列のどちらかといった具合。
  • 処理対象のオブジェクトが呼び出すメソッド名が広く使われているものであること。 to_i や to_arr のように。

変数の型が予測可能であれば、別のセマンティクスを持つオブジェクトが紛れ込む可能性は低い。 また、広く使われている to_i や to_arr しか使わないのであれば、これまた別のセマンティクスを持つオブジェクトが紛れ込む可能性は低い。

問題が起きるとすれば、以下のような場合になる。

  1. 処理対象となる変数が様々な型を持つ可能性があり、そこに想定していない型が紛れ込んでしまった。
  2. ある型において、 to_i や to_arr を別の意味で再定義してしまった場合。
list = [100, "10", "AAA"]
list.each do |item|
  puts item.to_i + 1
end

でもこれは、 Duck Typing を使わなかったとしても発生する問題。

変数に何の型が入るか分からないのであれば、元の例の Integer(s) + 1 のように変数の型をキャストすることになる。 この場合でも、想定外の型が入ってしまうと、キャストに失敗してエラーとなるか、想定外の変換によって意図しない動作をするかになる。

厳密にチェックしようとすると、静的型付け言語を使うことになるんだろうけど、様々な型を扱おうとするとコンテナは Object 型になる*1。 そうするとコンテナに入る型は実行時にしか決まらないから、想定していない型が紛れ込んでしまった時の問題は動的型付けの場合と変わらなくなっちゃう。

ArrayList list = new ArrayList();
list.add(100);
list.add("10");
list.add("AAA");

Iterator item = list.iterator();
while (item.hasNext()) {
  System.out.println((Integer)item.next() + 1);
}

さらに厳密にやるなら、インタフェースを一つ定義して、処理対象となる型全てに対してインタフェースを実装してもらうようにすることになる。 でもそれで得られるメリットは、実行時に検出していた問題をコンパイル時に検出できるかどうかに過ぎない。 インタフェースを実装する時間と比較して、コストパフォーマンスがあるかどうかは分からない。 それに、インタフェースをベースにする場合でも、インクルードするインタフェースが増えるとメソッド名は衝突しちゃう。

結局、 Duck Typing を使っても使わなくても、問題は同じだと思う。 要は、多態性と複雑さのバランスをどう取るかで、バランス取りに失敗したら Duck Typing とインタフェースの実装のどちらも収集が付かなくなっちゃう。 ちょっと前にどこかで話題になっていた、「自由度が高すぎれば良い訳じゃない。選択肢を絞ることも大切」という話に似ているかも。

参考

https://www.amazon.co.jp/dp/4274066428

ちなみに、プログラミングRuby言語編(第2版)の第23章が、まるごとDuck Typingの話題になってる。

静的型付けによってプログラムの信頼性が向上するという証拠はない

Person オブジェクトを入れたら、 Person オブジェクトを取り出します。そうでないプログラムを書く人などいません。

結局、 duck typing というのはルールでもなんでもなく、あくまでプログラミングスタイルの1スタイルにすぎません。偏執狂的なチェックと柔軟なチェックのバランスをうまくとってプログラムを設計するようにしてください。

ということなので、適材適所で使っている限りは(2)なんだと思う。

関連する日記