多くのブログ、および マニュアル自体 は、Juliaが 動的に型付けされた であると言います。しかし、このマニュアルを読んだところ、 静的に型付けされた と 型推論 、 F# のように聞こえます。
ティム・ホーリーの答えは非常に正しいですが、少し詳しく説明します。最初に、いくつかの用語を定義しましょう。私の定義に同意しないかもしれませんが、少なくともあなたは私が言っていることを知っているでしょう。私の見解では、静的言語と動的言語の主な違いは次のとおりです。静的言語では、式には型があります。動的言語では、値には型があります。
静的言語では、プログラム内のすべての式のタイプを決定するためのルールがあります。式のタイプにより、プログラムの動作が決まります。すべての式に対して一貫した型が決定されることを許可しないプログラムは、誤っていると見なされ、コンパイルされません。ポリモーフィズムが存在する場合、式のタイプは単一の具体的なタイプではない場合があります。パラメトリックポリモーフィズムは、同じコードに、タイプのパラメーターによってインデックス付けされた、具体的にタイプされたアルゴリズムのファミリー全体を記述する方法と考えることができます;サブタイプ多型は、静的な言語に限られた量の動的な振る舞いを導入するものと考えることができます。
一方、動的言語には、式に型を割り当てるためのルールがありません:型は、データがプログラムを流れる方法実行時によって暗示されます。一般に、式は潜在的にあらゆるタイプの値を生成する可能性があります。このため、型理論家は動的言語を「ユニタイプ」と表現することがあります。つまり、「型」が本質的に式のプロパティである静的な観点から、動的言語のすべての式はAny
型です。もちろん、それは型の静的な概念(式にとってのみ意味がある)を、型の概念が値に対してのみ意味を持つ言語に適用することです。
ジュリアは正にダイナミックな陣営にいます。型は式ではなく値のプロパティです。コードの結果タイプは、実行時に値がどのように流れるかによって決まります。この言語には、式を実行する前に式に型を割り当てるためのルールは含まれていません。ただし、多くの動的言語とは異なり、ジュリアには型について話すためのかなり洗練された言語があり、型に式に注釈を付けることができます。例えば、 x::T
は、x
がT
型の値であるというアサーションです。それが本当なら、x::T
はx
の値に評価されます。そうでない場合、エラーが発生し、式は値を返しません。メソッドシグネチャの型注釈の意味は少し異なります。既存の値の型をアサートする代わりに、対応する引数が指定された型である場合にのみメソッドが適用されることを示します。どちらの場合でも、次のコードはx
の値がT
型であると安全に想定できます。
[余談:「段階的」または「オプション」の型付けを行う一部の言語では、型注釈により言語が動的モードから静的モードに切り替わります。型注釈のないメソッドは動的です。型注釈のあるメソッドは静的です。静的コードには、すべての式に型を割り当てるためのルールがあり、コードはそれらを満たさなければなりません。これはJuliaの動作方法ではありません。型注釈のあるコードは動的であり、型注釈のないコードと同じセマンティクスを持っています。]
F#、OCaml、Haskellなどの言語の型推論は、式の型がどのように決定されるかの一部です。コンパイラが式の型を推測できない場合、プログラムは破損し、コンパイルされません。これらの言語はすべて何らかの形式のHindley-Milner型推論を使用します。これは、明示的な型を記述することなく、コードの構造から式の型を導出する非常に賢い方法です(これを型が暗示する動的言語と比較してください)コードの実行)。ほとんどの場合、型注釈はまったく必要ありません。これは、C++、C#、Javaなどの言語で必要になる可能性のある詳細な型宣言と比較して、非常に快適です。ただし、これは、式が事前に定義された型を持たないことが完全に受け入れられるという理由だけで、型注釈が不要なJuliaやPythonなどの動的言語とは異なります。Hindley-Milner言語では、 C++やJavaのように多くの型を記述する必要はないかもしれませんが、すべての式にはコンパイラが計算できる事前定義された型があります。
Juliaのコンパイラは型推論を行いますが、まったく異なります。すべての式が推論可能な型を持つ必要はありません。コンパイラはコードを分析して式のタイプを予測し、その情報を使用してより効率的なマシンコードを生成します。しかし、式の型を判別できない場合、大したことではありません。コンパイラは、実行時の型情報を使用して、とにかく動作する汎用コードを出力するだけです。 Juliaのほとんどの部分では、型推論は最適化にすぎません。コードはそれを使用しても使用しなくても同じように機能しますが、型推論が成功すると、はるかに高速に実行されます。
両方とも真実です。 Juliaは動的に型付けされますが、よく書かれたJuliaコードでは、型は通常推測できます。可能であれば、パフォーマンスが大幅に向上することがよくあります。
これに関するいくつかの議論があります FAQで 。
それは動的に型付けされますが、variable :: typeのような型を指定すると、その変数は静的に型付けされていると考えることができます(これにより、コンパイラが型を自動的に推論できなかった場合のパフォーマンスが向上します)
Getting Started with Julia Programming Language book からの抜粋です:
また、呼び出し時に渡されるパラメーターの種類を制限するために、引数のタイプを示すことも役立ちます。浮動小数点数の関数ヘッダーは、関数mult(x :: Float64、y :: Float64)のようになります。 mult(5、6)でこの関数を呼び出すと、エラーが発生します。ERROR: 'mult'にはmult(:: Int64、:: Int64)に一致するメソッドがありません。浮動小数点引数の整数パラメーターは受け入れません。
これは本の関数の例です:
function mult(x::Float64, y::Float64)
x * y
end
mult(5, 6) # raises an error
mult(5.0, 6.0) # => 30.0
本が正しいかどうかはわかりませんが、他のすべての答えと矛盾します。