2020年5月17日日曜日

1: 「ダックタイピング」の明かされたエッセンス

<このシリーズの前の記事 | このシリーズの目次 | このシリーズの次の記事>


動機


「ダックタイピング」についてのよく知られている説明は、ただの言葉遊びにすぎません。そのような面白くもないナンセンスで私たちをごまかせるとたかをくくらせるのはやめましょう。

話題


About: Pythonプログラミング言語
About: 「ダックタイピング」

この記事の目次


開始コンテキスト


  • 読者は、オブジェクト指向プログラミングについての基本的知識を持っている。

ターゲットコンテキスト



  • 読者は、「ダックタイピング」の本当のエッセンスを理解する。

オリエンテーション


Hypothesizer 7
「ダックタイピング」についてのよく知られている説明は、「もしも、それがダックのように歩きダックのように鳴くのであれば、それはダックであるに違いない」というものだ...

なんだか、ダックがどのように歩いたり鳴いたりするかを誰もが知っているはずだと言わんばかりではないか。...問題は、私は知らないということだ。つまり、ある鳴き声を聞いても、「ああ、それはダックのだ!」とも「もちろん、それはガチョウのだ!」とも私には言えない。実際、どれだけの人が言えるだろうか?...

どれだけの人が、マガモの鳴き声とスズガモの鳴き声を判別できるだろうか?...ばかげてる?...とんでもない、そこが一番重要なのだ。だって、どの鳴き声もダックのものだとかガチョウのものだとか特定できるというということが担保されていなければ、上記のよく知られている説明は、ナンセンスだということになってしまう。


本体


1: 「ダックタイピング」のエッセンス


Hypothesizer 7
「ダックタイピング」は、「もしも、それがダックのように歩きダックのように鳴くのであれば、それはダックであるに違いない」として説明されている...

それは、それを言う前に答えていなければならない問題をはぐらかしている: 'どのようにダックが歩き鳴くか、または踊るかなどを、どのように私は知ることができるのか?'

「ダックタイピング」の世界における答えは、’コード全体を読まなければならない、一般的に言って’である。

1つのPythonファンクションを見てみよう。

@Python ソースコード
def functionA (a_duck, a_destination, a_key, a_music):
 ~
 functionB (a_duck.walks (a_destination))
 ~
 l_duck = a_duck
 ~
 functionC (l_duck.quacks (a_key))
 ~
 l_mallard = functionD (l_duck)
 ~
 return l_mallard.dances (a_music)

私はあるダックを'functionA'に渡さなければならないのだが、一体全体、'ダック'って何なのだ、実のところ?

えーと、'a_duck'を'functionA'内で探すと、そのダックは、1つの引数を持つ'walks'メソッドを持っていなければならないようだ、"a_destination"が何であると意図されているのか私は知らないのであるが("サムの屠殺場"のような文字列なのか、'132'のような場所コードなのか、'Destination'クラスのインスタンスなのか)。...当然、私は、'walks'メソッドの中を見て、"a_destination"がどのように使用されているかを理解しなければならない。しかしながら、どの'walks'なのだ?...つまり、1つの引数を持つ'walksメソッドを持つ多くのクラスがあるかもしれない。...実際には、どれであるかなど私は決定することができない、なぜなら、調査のこの段階ではそれらの全てが候補だからだ。したがって、私は、さらなるプロファイリングのために全ての候補クラスをリストアップしておくしかない...

次に、私は、'walks'のリターンタイプが何でなければいけないのかを知らなければならない。...そのリターン値が'functionB'に渡されるので、当然、私は、'functionB'の中を見て、それがどのように使用されているかを理解しなければならない。...待てよ、'functionB'はそれを別のファンクションに渡し、その別ファンクションがまた別のファンクションに渡し、さらにそれが続いているではないか。...私は、それら全てのファンクション群を見通さなけばならないのか?...当然だ!他にどうありえようか!...それら全てのファンクション群を見通した後でようやく、必要とされるリターンタイプに適合しないいくつかの候補を除外できて、まだいくつかの候補が残ることになる。...

次は何だ?当然、私は、'a_duck'だけを見るというわけにはいかず、'l_duck'も見なければならない、なぜなら、'l_duck'は同じダックオブジェクトを明らかに指しているのだから。まあ、今回はとてもラッキーだったな、だって、いつもそんなに明らかだは限らないから。...とにかく、そのダックは、1つの引数を持つ'quacks'メソッドを持っていなければならないが、その引数が何であるかを私は知らない。...当然、何をしなければならないか私は知っている: 複数の'quacks'メソッドの中を見るのだ。次に、当然、私は、'functionC'メソッドの中を見なければならず、それはリターン値を別のメソッドに渡しており、それが続く。...頑張れ、私!私にはできるぞ!

次は何だ?'functionD'メソッドだよね?元気をだせ、私!これが最終直線のはずだぞ!...待てよ、'functionA'でやったことを、'functionD'メソッドでまたもややり通さないといけないよな、実のところ...

...えーと、それで終わりか?いや、実は、'functionD'は本当はそのダックをリターンしていたのだ、つまり、その'l_mallard'というやつは、'a_duck'オブジェクトを指しているのだ。...ということは、実際には、ただ'a_duck'および'l_duck'のような明らかな同一物を探せばよいというほど単純ではないわけだ、教訓として。...とにかく、そのダックは、1引数を持つ'dances'メソッドも持っていなければならず、その引数が何であるかを私は知らない。...当然、私は、複数の'dances'メソッドの中を見なければならない...

そして、私は、'dances'のリターンタイプが何でなければならないかを知らなければならない、当然。...今回は、私は、'functionA'の全ての呼び出し元を探して、'functionA'のリターン値がどのように使用されているかを理解しなければならないようだ。...そのリターン値たちは、実は、いくつかのファンクションに渡されていて、それらのファンクションがまた別のファンクションたちに渡していて、それが続いている。...へこたれるな、私!私は強いんだ!私はあきらめはしない!

...それが...「ダックタイピング」だ。

実のところ、「ダックタイピング」のエッセンスは、'あるダックがどのように歩きかつ鳴き、または踊りなどするかは、からみあっている可能性のあるコードの全体によって暗黙的に定義される。'ということだ。

明示的にタイプ付けされた世界では、'functionA'だけを安全に変更できるが、「ダックタイピング」の世界では、'functionA'だけを安全に変更することはできない、なぜなら、その変更は、ダックがどのようであるかを変えてしまい、'functionA'の呼び出し元を壊してしまう可能性があるから。

...それが...「ダックタイピング」だ。

'functionB'や'functionC'や'functionD'への変更も、ダックがどのようであるかを変えて、'functionA'の呼び出し元を壊してしまう可能性があることも忘れてはならない。

...それが...「ダックタイピング」だ。

ファンクションというものの最大のメリットは、ファンクションへどのような変更をしても、変更がそのファンクション内部に隔離される(インターフェイスが保持されていればのことだ、勿論)ことだと私は考えていたが、「ダックタイピング」ではそうではない。

お分かりでしょう、...それが...「ダックタイピング」だ。

お分かりだろうが、前出のよく知られた説明は、実際には、「ダックはダックのような何かだ」と述べているだけのナンセンスだ、'ダック'とは何かを先に定義することなしに。

「ダックタイピング」は、本当は'タイプチェック'対'振る舞いチェック'に関するものではないことを確実に理解しよう。「振る舞いチェック」?違う、「ダックタイピング」はインターフェイスしかチェックしない(ここでは、'インターフェイス'という用語を、一般的な意味で使っている、特定のプログラミング言語の意味でではなく: 例えば、Javaインターフェイスはインスタンスメンバー変数の定義を持てないが、それはここでは問題ではない。任意のパブリックメンバーはここで私が意味する'インターフェイス'の一部だ、なぜなら、それは、オブジェクトがどのように外界とインターフェイスできるかに関するものだから)、そして、インターフェイスというのは、実のところ、タイプだ。繰り返すが、「ダックタイピング」は、'タイプチェック'か'振る舞いチェック'かという問題では全然なく、タイプが明示的に指定されているか否かという問題だ。


2: 私に「ダックタイピング」を弁護させてください


Hypothesizer 7
それでは、私に「ダックタイピング」を弁護させてください。

......えーと、......何が言えるだろうか?

ああ、「変数タイプを指定しても、妥当でない値が変数にセットされるのを本当に防げはしない: メートル整数と意図されている変数にミリメートル値をセットできてしまう、例えば。だから、'静的タイプチェック'など意味がないのだ!」と言える

はあ?'オールオアナッシング'ではない、と仰せられるのか?整数変数に非整数値がセットされるのを防げるのは、何も防げないよりもはるかに良い、と仰せられるのか?変数を特定のユーザー定義クラスタイプのものとして指定するのは、変数を'これが真偽値を格納しようが、整数値を格納しようが、文字列を格納しようが、リストを格納しようが、ユーザー定義クラスインスタンスを格納しようが知ったことか!'のままに放置するよりもはるかに良い、と仰せられるのか?何かが完全でないからそれは無意味だというような主張はばかげている、なぜなら、不死でもない種族が考え出したほとんど全ての事物はどのみち完全でないのだから、と仰せられるのか?...よくも言ったな!よくもそんなことを!そんな生意気な輩は私の声の大きさだけで黙らせなければならん、なぜなら、私はまさにその戦略をつらなかなければならないからだ、私に気に入らない改善は、そんな改善は完全でないという主張によって拒否するという戦略に。

...もしも、先程の弁護が十分でなければ、勿論、私はこう言える「必要なコメントを付ければいいだけだ!」。

はあ?そのコメントを書くのは、ただ変数タイプを書くよりも、もっと時間がかかるんじゃないか、と仰せられるのか?...そんなの余計なお世話だ!どんなに時間がかかろうが私はコメントを書くのだ!、もしくはそう言い張ることができる。...変数名の前(またはどこか)にただ'str'と書くのさえも嫌がる怠惰な人間である私が十分なコメントを書くとは信じられない、と仰せられるのか? ...なんたる侮辱!証拠を何もだせないだろう!、たとえ、私が全然コメントを書かないとしても。...コードチェックに使える変数タイプを書くのに少ない時間をかける代わりに、コードチェックに使えないコメントを書くのにもっと時間をかけるのは、ばかげてる、と仰せられるのか?...ばかげてるのはお前だ!私が本当にそんなコメントを書くと想定するなど全くばかげてる!

ああ、別のありえる弁護は、「必要なテストをすればよいだけだ!」だ、もしくは、「どうせテストをしなければいけないのだから、何も違わない。」というべきか。

はあ?必要となるテストの量が違う、と仰せられるのか?...量に言及するとはけしからん!コンパイルすることが十分なテストだと考えているようなお前のような者に批判されたくない!...それは誣告だ、と仰せられるのか?C++やJavaやその類で書かれた世の中のプロダクト群は、事実、テストされることなくリリースされてなどいない、少なくとも、そのほとんどは、と仰せられるのか?...それらには多くのバグがあるだろ、どちらにしろ!、と言わせてもらう。...Pythonによるプログラムがより優秀だというわけではない、と仰せられるのか?...そこいらのお粗末なPythonプログラマーたちが責められるべきであって、私がではない!...それは、C++プログラムなどでも同じことだ、と仰せられるのか?...お前は...でべそだ!...'少なくともタイプ群は一貫している'コードをデバッグするのと'タイプ群が滅茶苦茶かもしれない'コードをデバッグするのでは、効率上、大きな違いがある、と仰せられるのか?...お前が口を出すことじゃない!!...量や効率や何かの違いを持ち出す生意気なやつは黙らせることが肝要である。

...しかし、最強の弁護は、「心地よいのだ!面倒くさい変数タイプを書くことから開放されるのが楽しいのだ!」だ。どうだ?心地よいのだ、少なくとも私には、ということを誰も否定できはしない!

はあ?それは、不健康なスナックを食べるのが心地よいのと同じようなものだ、と仰せられるのか?...トレーニングしてカロリーを燃やせばいいだけだからね。...私が本当にトレーニングするのか、お尋ねか?...するつもりでいる。...それは、一時的な、近視眼的な快楽にすぎない、と仰せられるのか?私は、現在の快楽に飛びついたが故に、将来的な苦痛を受けるだろう、と仰せられるのか?...一体、何様のつもりだ?なぜ、お前は将来について確信しているのか?...「ダックタイピング」は、実際には、コードの中に記録されていない意図された変数タイプ群についての私の記憶に基づいている、と仰せられるのか?コードサイズおよびプロジェクトの直近性のために、意図された変数タイプ群が私の記憶内に収まっている限りは心地よく感じられるかもしれないが、それらが私の記憶から出てしまったら最後、頭痛の種になる、と仰せられるのか?...えーと、私は、スーパー記憶力を持っている、または、少なくともそう主張する。複雑なコードを永久に保持できる記憶力をお持ちでないのは気の毒なことだ。

...真面目な話、「ダックタイピング」はオーケーだ、もしも、プロジェクトが、1人で行なう、複雑でない、後に2度と変更されることのないプロジェクトであれば。

うん?同意されるのか?本当に?

オーケー!以上のように、私は、「ダックタイピング」に対する反論を完全に叩きのめした!


3: または...


Hypothesizer 7
実は、Pythonでは、静的タイプチェックが今ではできるようだ。

おお、...それで、話は変わってくる。

勿論、私は、「ダックタイピング」に対するすべての反対意見を叩き潰してやった、しかし、静的タイプチェックができるのにそれを決してしないとは言っていない、でしょう?

Pythonでどのように静的タイプチェックをできるかを、本シリーズの次の記事で見てみよう。


4: 結びとその先


Hypothesizer 7
これで、「ダックタイピング」のエッセンスを私は理解したようだ。

「ダックタイピング」のエッセンスは、'あるダックがどのように歩きかつ鳴き、または踊りなどするかは、からみあっている可能性のあるコードの全体によって暗黙的に定義される。'ということだ。

それは、私は、あるダックが何であるか(もしくは、その代わりに、'あるダックはどのように振る舞うか'と言ってもよい、もしも、誰かが 「ダックタイピング」は''ダックが何であるか'には関知せず、'あるダックはどのように振る舞うか'だけが重要なのだと言い張るのであれば)を知るためにコード全体を見なければならない、ということなのか?...そう、一般的に言えば。

...「ダックタイピング」はよいものなのか?...うーん、その質問にどう答えるかは、その人が、「不健康なスナックはよいものなのか?」にどう答えるかに強い正の相関があるかもしれない。

どちらにしろ、今では、ゲームチェンジャーがある: Pythonのための静的タイプチェック。...それは、どのようなものだろうか?本シリーズの次記事で、それを吟味してみよう。


参考資料


<このシリーズの前の記事 | このシリーズの目次 | このシリーズの次の記事>