動的に型付けされた言語はユニタイプです
型システムを比較すると、動的型付けには利点がありません。動的型付けは、静的型付けの特殊なケースです。これは、すべての変数が同じ型を持つ静的型付け言語です。すべての変数をtype Object
にし、「オブジェクト」値をtypeにすることで、Javaで同じこと(簡潔さを除く)を実現できますMap<String, Object>
。
void makeItBark(Object dog) {
Map<String, Object> dogMap = (Map<String, Object>) dog;
Runnable bark = (Runnable) dogMap.get("bark");
bark.run();
}
そのため、リフレクションがなくても、静的に型付けされたほぼすべての言語で同じ効果を達成できますが、構文上の利便性は別です。追加の表現力は得られません。逆に、あなたが持っているより少ない動的型付け言語では、あなたがしているので、表現力を拒否された特定の種類の変数を制限する機能を。
静的に型付けされた言語でアヒルの樹皮を作る
さらに、静的に型指定された適切な言語を使用すると、bark
操作を持つ任意の型で機能するコードを作成できます。Haskellでは、これは型クラスです:
class Barkable a where
bark :: a -> unit
これは、あるタイプa
がBarkableと見なさbark
れるには、そのタイプの値を取り、何も返さない関数が存在する必要があるという制約を表します。
その後、Barkable
制約の観点から汎用関数を作成できます。
makeItBark :: Barkable a => a -> unit
makeItBark barker = bark (barker)
これは、makeItBark
がBarkable
要件を満たしているすべてのタイプで機能するということです。これは次のように見えるかもしれませんがinterface
、JavaやC#で、それは一つの大きな利点を持っている-のタイプを指定する必要はありません前まで、彼らは満足型クラスを。私が書いていないサードパーティのタイプであっても、そのタイプDuck
はBarkable
いつでも言うことDuck
ができます。実際、ライターが関数をDuck
記述していなくてもかまいません。次の条件を満たすbark
言語を伝えると、事後に提供できます。Duck
Barkable
instance Barkable Duck where
bark d = quack (punch (d))
makeItBark (aDuck)
これは、Duck
sがbarえることができ、barえる前にカモをdでることによってそのbarえ声機能が実装されていることを示しています。邪魔にならないように、makeItBark
アヒルを呼ぶことができます。
Standard ML
さらにOCaml
、同じ型クラスを複数の方法で満たすことができるという点で、さらに柔軟です。これらの言語では、整数は従来の順序を使用して順序付けすることができ、次に向きを変えて、割り切れる順序で並べることもできます(10 > 5
10は5で割り切れるなど)。Haskellでは、型クラスを1回だけインスタンス化できます。(これにより、Haskell bark
はアヒルを呼び出しても問題ないことを自動的に認識できます。SMLまたはOCaml では、複数の関数があるため、どの bark
関数を使用するかを明示する必要があります。)
簡潔
もちろん、構文上の違いがあります。あなたが提示したPythonコードは、私が書いたJavaの同等物よりもはるかに簡潔です。実際には、その簡潔さが動的に型付けされた言語の魅力の大きな部分です。ただし、型推論を使用すると、すべての変数の型を明示的に記述する必要がなくなるため、静的に型付けされた言語と同じくらい簡潔なコードを記述できます。静的に型付けされた言語は、動的な型付けのネイティブサポートも提供し、すべてのキャストとマップ操作(C#などdynamic
)の冗長性を取り除きます。
正しくても型が正しくないプログラム
公平を期すために、静的型付けは、型チェッカーで検証できない場合でも、技術的に正しいプログラムを除外する必要があります。例えば:
if this_variable_is_always_true:
return "some string"
else:
return 6
if
else分岐は発生しませんが、ほとんどの静的型付け言語はこのステートメントを拒否します。実際には、誰もこのタイプのコードを利用していないようです。タイプチェッカーにとって賢いものは、おそらくあなたのコードの将来のメンテナーがあなたとあなたの次の親族を呪うでしょう。適切な例として、誰かが4つのオープンソースPythonプロジェクトをHaskellに正常に翻訳しました。つまり、優れた静的型付け言語ではコンパイルできないものは何もしていませんでした。さらに、コンパイラーは、ユニットテストでは検出できないタイプ関連のバグをいくつか発見しました。
動的型付けについて私が見た中で最も強い議論は、言語の構文を任意に拡張できるLispのマクロです。しかし、Typed Racketは静的に型付けされたLispの方言であり、マクロを持っているため、静的な型付けとマクロは相互に排他的ではないようですが、同時に実装するのはおそらく困難です。
リンゴとオレンジ
最後に、言語には単なる型システムよりも大きな違いがあることを忘れないでください。Java 8より前は、Javaであらゆる種類の関数型プログラミングを行うことは事実上不可能でした。単純なラムダには、4行の定型的な匿名クラスコードが必要です。Javaは、コレクションリテラル(例:)もサポートしていません[1, 2, 3]
。また、ツール(IDE、デバッガー)、ライブラリ、およびコミュニティサポートの品質と可用性にも違いがある場合があります。誰かがJavaよりもPythonやRubyで生産性が高いと主張したとき、その機能の不均衡を考慮する必要があります。含まれているすべてのバッテリー、言語コア、および型システムを使用した言語の比較には違いがあります。
makeItBark(collections.namedtuple("Dog", "bark")(lambda x: "woof woof"))
。その引数はクラスではなく、匿名の名前付きタプルです。ダックタイピング(「if it quacks a ...」)を使用すると、基本的に制限がなく、構文オーバーヘッドのないアドホックインターフェイスを実行できます。Javaのような言語でこれを行うことはできますが、多くの乱雑なリフレクションが発生します。Javaの関数にArrayListが必要で、別のコレクションタイプを指定する場合は、SOLです。Pythonでも、それは実現できません。