私は新しいプログラミング言語が動的に型付けされていることをたくさん聞いていますが、言語が動的に型付けされているのに対して静的に型付けされていると言うと実際に何を意味するのでしょうか。
コンパイル時に変数の型がわかっていれば、言語は静的に型付けされます。いくつかの言語では、これはプログラマとしてあなたがそれぞれの変数がどんなタイプであるかを指定しなければならないことを意味します(例:Java、C、C++)。他の言語は何らかの形式の 型推論 、変数の型を推定する型システムの機能を提供します(例:OCaml、Haskell、Scala、Kotlin)。
ここでの主な利点は、あらゆる種類のチェックがコンパイラによって実行できることです。そのため、非常に早い段階で多くの些細なバグが発見されます。
例:C、C++、Java、Rust、Go、Scala
型が実行時の値に関連付けられており、変数/フィールド/ etcという名前ではない場合、言語は動的に型指定されます。これは、毎回型を指定する必要がないため、プログラマとしては少し早く書くことができることを意味します( 型推論 を指定した静的型付き言語を使用している場合を除く)。
例:Perl、Ruby、Python、PHP、JavaScript
とにかく静的型チェックを行うコンパイラがないため、ほとんどのスクリプト言語にはこの機能がありますが、インタプリタが変数の型を誤って解釈していることが原因のバグを探している場合があります。幸いなことに、スクリプトは小さくなる傾向があるので、バグに隠れる場所はそれほど多くありません。
ほとんどの動的型付け言語では、型情報を提供できますが、必須ではありません。現在開発されている1つの言語、 Rascal は、関数内で動的な型付けを可能にしながらも、関数シグネチャに対して静的な型付けを強制するハイブリッドアプローチを採用しています。
静的型付けプログラミング言語は、 実行時 とは対照的に コンパイル時 で型チェック(すなわち、型の制約を検証して強制するプロセス)を行います。
動的型付けプログラミング言語は、 コンパイル時 とは対照的に 実行時 で型チェックを行います。
Python(動的型付け)とGo(静的型付け)が型エラーを処理する方法を対比させた例です。
def silly(a):
if a > 0:
print 'Hi'
else:
print 5 + '3'
Pythonは実行時に型チェックをするので、
silly(2)
完璧に動作し、期待される出力Hi
を生成します。問題のある行がヒットした場合にのみエラーが発生します。
silly(-1)
プロデュース
TypeError: unsupported operand type(s) for +: 'int' and 'str'
該当する行が実際に実行されたためです。
一方、コンパイル時に型チェックを行います。
package main
import ("fmt"
)
func silly(a int) {
if (a > 0) {
fmt.Println("Hi")
} else {
fmt.Println("3" + 5)
}
}
func main() {
silly(2)
}
上記はコンパイルされず、次のエラーが発生します。
invalid operation: "3" + 5 (mismatched types string and int)
簡単に言うと、 静的に型付けされた言語では 変数の型は static です。つまり、一度変数を型に設定すると、それを変更することはできません。それは、型付けが参照する値ではなく変数に関連付けられているからです。
例えば、Javaでは:
String str = "Hello"; //variable str statically typed as string
str = 5; //would throw an error since str is supposed to be a string only
一方、 動的に型付けされた言語では variablesの型は dynamic です。つまり、変数を型に設定した後で、それを変更することができます。これは、型付けが変数自体ではなく、想定する値に関連付けられているためです。
例えばPythonでは:
str = "Hello" # variable str is linked to a string value
str = 5 # now it is linked to an integer value; perfectly OK
そのため、動的に型付けされた言語の変数は、 単なる総称ポインタ と型付けされた値として考えるのが最善です。
まとめると、 type は、言語自体ではなく言語で変数を記述します(または記述しているはずです)。 静的に型付けされた変数を持つ言語 動的に型付けされた変数を持つ言語 IMHOとして使用された方がよいでしょう。
静的型付け言語は一般にコンパイルされた言語です。したがって、コンパイラは型をチェックします(型は後で実行時に変更することはできないため、完全に理解しておいてください)。
動的型付け言語は一般に解釈されるため、型チェック(もしあれば)は実行時に使用されます。もちろんこれはいくらかのパフォーマンス上のコストをもたらし、動的言語(例えば、python、Ruby、php)が型付けされたもの(Java、c#など)ほどうまく拡大できない理由の一つです。別の観点から見ると、静的型付け言語は起動時のコストが高くなります。通常、より多くのコードを作成し、より困難なコードを作成します。しかし、それは後になります。
良いことは、双方が反対側から機能を借用しているということです。型付き言語はより動的な機能、例えばジェネリックや動的ライブラリをc#に取り入れており、動的言語はより多くの型チェック、例えばpythonの型注釈、またはPHPのHACK版を含んでいます。デマンド。
技術選択に関しては、どちらの側にも他のものよりも本質的な優位性はありません。あなたがより多くのコントロールから始めたいのか、それとも柔軟性があるのかは、単なる好みの問題です。作業に適したツールを選択し、切り替えを検討する前に、反対の観点から利用可能なものを必ず確認してください。
http://en.wikipedia.org/wiki/Type_system
静的型指定
プログラミング言語は、実行時ではなくコンパイル時に型チェックが行われるときに静的型付けを使用すると言われています。静的型付けでは、型は値ではなく変数に関連付けられます。静的型付け言語には、Ada、C、C++、C#、JADE、Java、Fortran、Haskell、ML、Pascal、Perl(スカラー、配列、ハッシュおよびサブルーチンの区別に関して)およびScalaが含まれます。静的型付けは、プログラム検証の限られた形式です(型の安全性を参照)。したがって、開発サイクルの早い段階で多くの型エラーを検出できます。静的型チェッカーは、コンパイル時に決定できる型情報のみを評価しますが、プログラムが実行される可能性があるすべての実行について検査済み条件が成立することを検証できるため、プログラムを実行するたびに型検査を繰り返す必要がありません。ランタイム型チェックを省略し、他の最適化を可能にすることによって、プログラム実行をより効率的にすることもできる(すなわち、より高速にするかまたはメモリを減らす)。
これらはコンパイル時に型情報を評価するため、実行時にしか利用できない型情報がないため、静的型チェッカーは保守的です。それらは実行時にうまく振舞うかもしれないいくつかのプログラムを拒絶するでしょう、しかしそれはうまくタイプされるために静的に決定されることができません。たとえば、実行時に式が常にtrueと評価される場合でも、コードを含むプログラムは次のようになります。
if <complex test> then 42 else <type error>
静的解析でelse分岐がとられないと判断できないため、型が正しくないとして拒否されます[1]。静的型チェッカーの保守的な振る舞いは、falseと評価されることがまれにしかない場合に有利です。静的型チェッカーは、めったに使用されないコードパスで型エラーを検出できます。静的型チェックなしでは、100%のコードカバレッジを持つコードカバレッジテストでさえ、そのような型エラーを見つけることができないかもしれません。値が作成されるすべての場所と特定の値が使用されるすべての場所の組み合わせを考慮する必要があるため、コードカバレッジテストではこのようなタイプエラーの検出に失敗することがあります。
最も広く使われている静的型付け言語は、正式には型保証されていません。それらは、プログラミング言語仕様に「抜け穴」があり、プログラマが静的型チェッカーによって実行される検証を回避するコードを書くことを可能にし、そしてより広範囲の問題に対処する。たとえば、JavaやほとんどのCスタイル言語には型打ちがあり、HaskellにはunsafePerformIOなどの機能があります。このような操作は実行時に安全でない可能性があります。
動的型付け
プログラミング言語は、その型チェックの大部分がコンパイル時ではなく実行時に行われるとき、動的に型付けされる、または単に「動的」と呼ばれます。動的型付けでは、型は変数ではなく値に関連付けられます。動的型付け言語には、Groovy、JavaScript、LISP、Lua、Objective-C、Perl(ユーザー定義型に関しては組み込み型は対象外)、PHP、Prolog、Python、Ruby、SmalltalkおよびTclが含まれます。静的タイピングと比較して、動的タイピングは、(例えば、プログラムがランタイムデータに基づいてタイプおよび機能を生成することを可能にすることによって)より柔軟であり得るが、先験的保証は少ない。これは、動的型付け言語が静的型チェッカーによって無効と判断される可能性のあるプログラムを受け入れて実行しようとするためです。
動的な型指定は実行時型エラーになる可能性があります。つまり、実行時には、値に予期しない型があり、その型には無意味な操作が適用されます。この操作は、プログラミングの間違いがあった場所、つまり間違った種類のデータが渡されるべきではない場所に渡された場所のかなり後に発生する可能性があります。これはバグを見つけるのを難しくします。
動的に型付けされた言語システムは、静的に型付けされた従兄弟と比較して、ソースコードの「コンパイル時」チェックを少なくします(たとえば、プログラムが構文的に正しいことをチェックします)。実行時検査は、動的情報とコンパイル時に存在した情報を使用できるため、潜在的により洗練されたものになる可能性があります。一方、ランタイムチェックでは、プログラムの特定の実行で条件が成立することのみがアサートされ、これらのチェックはプログラムの実行ごとに繰り返されます。
動的型付け言語での開発は、単体テストなどのプログラミング手法によってサポートされることがよくあります。テストはプロのソフトウェア開発における重要な習慣であり、動的型付け言語では特に重要です。実際には、正しいプログラム操作を保証するために行われるテストは、静的型検査よりもはるかに広い範囲のエラーを検出できますが、逆に、テストと静的型検査の両方が検出できるエラーを包括的に検索することはできません。テストはソフトウェアのビルドサイクルに組み込むことができます。その場合、プログラムユーザーが手動でテストを実行する必要がないという点で、「コンパイル時」のチェックと見なすことができます。
参考文献
- Pierce、Benjamin(2002)。型とプログラミング言語MITを押します。 ISBN 0-262-16209-1。
「動的に型付けされる」という用語は、残念ながら誤解を招くものです。すべての言語は静的に型付けされ、型は式のプロパティです(一部の人が考える値のプロパティではありません)。ただし、一部の言語には1つのタイプしかありません。これらはユニタイプ言語と呼ばれます。そのような言語の一例は、型付けされていないラムダ計算です。
型なしラムダ計算では、すべての用語はラムダ用語であり、用語に対して実行できる唯一の操作は、それを別の用語に適用することです。したがって、すべての操作は常に無限再帰またはラムダ項のいずれかとなりますが、エラーを通知することはありません。
ただし、プリミティブ型と算術演算で型指定されていないラムダ計算を強化する場合、2つのラムダ項を一緒に追加するなどの無意味な演算を実行できます:(λx.x) + (λy.y)
。これが発生したときにエラーを通知することが唯一の健全なことであると主張することができますが、これを行うには、各値に用語がラムダ用語か数値かを示すインジケーターをタグ付けする必要があります。加算演算子は、実際に両方の引数が数値としてタグ付けされていることを確認し、そうでない場合はエラーを通知します。これらのタグはnotタイプであることに注意してください。タイプはプログラムのプロパティであり、それらのプログラムによって生成される値ではないためです。
これを行うユニタイプの言語は、動的タイプと呼ばれます。
JavaScript、Python、Rubyなどの言語はすべてユニタイプです。繰り返しますが、JavaScriptのtypeof
演算子とPythonのtype
関数には誤解を招く名前があります。型ではなく、オペランドに関連付けられたタグを返します。同様に、C++のdynamic_cast
およびJavaのinstanceof
do not do型チェックを行います。
「ソースコードが翻訳されたとき」
「型チェック時」
5 + '3'
は、GoやPythonのような 強く型付けされた 言語における型エラーの例です。 2種類のマージ JavaScriptのように弱く型付けされた の言語では型エラーが発生しません('53'
が発生します)。
「静的&コンパイル済み」と「動的&解釈済み」の定義は非常に似ていますが、「型をチェックするとき」と「ソースコードを翻訳するとき」の定義は同じです。
言語がコンパイルされているか解釈されているかにかかわらず、同じ型エラーが発生します !これらの用語を概念的に分離する必要があります。
動的、解釈済み
def silly(a):
if a > 0:
print 'Hi'
else:
print 5 + '3'
silly(2)
Pythonは解釈され動的に型付けされているので、実行しているコードを変換して型チェックするだけです。 else
ブロックは実行されないため、5 + '3'
は見られないことさえあります。
静的に型付けされた場合はどうなりますか?
コードが実行されるまでも型エラーがスローされます。解釈されても、実行前に型チェックを実行します。
コンパイルされたらどうなる?
else
ブロックは実行時に変換/参照されますが、動的に型指定されているのでエラーにはなりません。動的型付き言語は実行されるまで型をチェックせず、その行は実行されません。
静的、コンパイル済み
package main
import ("fmt"
)
func silly(a int) {
if (a > 0) {
fmt.Println("Hi")
} else {
fmt.Println("3" + 5)
}
}
func main() {
silly(2)
}
実行前に型がチェックされ(静的)、型エラーがすぐに検出されます。型が解釈された場合でも、実行前に型がチェックされ、同じ結果になります。動的であれば、コンパイル中にコードを調べてもエラーにはなりません。
コンパイルされた言語は、静的に(動的に)入力されると実行時のパフォーマンスが向上します。型の知識は機械語コードの最適化を可能にします。
静的に型付けされた言語は、実行時に型を動的にチェックする必要がないため(実行前にチェックされるため)、実行時のパフォーマンスは本質的に優れています。
同様に、コンパイルされた言語は実行時に高速です。コードはその場で「解釈」または翻訳する必要がなく、すでに翻訳されているからです。
コンパイルされた言語と静的に型付けされた言語の両方が、それぞれ翻訳と型チェックのために実行されるまでに遅延があることに注意してください。
静的型付けは、実行中にエラーを見つけるのではなく、エラーを早期に検出します(特に長いプログラムに役立ちます)。それはあなたのプログラムのどこにおいても型エラーを許さず、変数が型を変更するのを防ぎ、それがさらに意図しないエラーに対してさらに防御するという点でより厳密です。
num = 2
num = '3' // ERROR
動的タイピングはより柔軟性があります。通常、変数による型の変更が許可されているため、予期しないエラーが発生する可能性があります。
静的に型付けされた言語はコンパイル時に型チェックを行い、型は変更できません。 (型キャストのコメントに夢中になってはいけません。新しい変数/参照が作成されます)。
動的型付け言語は実行時に型チェックを行い、変数の型は実行時に変更できます。
静的に型付けされた言語 :それぞれの変数と式はコンパイル時に既に知られています。
(int a;
aは実行時に整数型の値のみを取ることができます)
例:C、C++、Java
動的に型付けされた言語 :変数は実行時に異なる値を受け取ることができ、それらの型は実行時に定義されます。
(var a;
aは実行時にあらゆる種類の値を取ることができます)
例:Ruby、Python。
静的で型定義された言語は、そのスコープ全体で型を変数にバインドします(Seg:SCALA)動的に型指定された言語は、変数で参照される実際の値に型をバインドします。
C++、Java、Pythonのような動的型付け言語のような静的型付け言語は、変数の型の実行という点でのみ異なります。 静的に型付けされた言語は変数に対して静的なデータ型を持ちます。ここではコンパイル時にデータ型がチェックされるのでデバッグはずっと簡単です...これに対して動的に型付けされた言語は同じではありません。どちらのプログラムを実行しているかをチェックしたため、デバッグが少し困難です。
さらに、それらは非常に小さな違いがあり、強く型付けされたおよび弱い型付けされた=言語。強い型付けされた言語では、ある型を別の型として使用することはできません。一方、弱く型付けされた言語はeg.pythonを許します
動的に型付けされた言語は、どの変数型を使用する必要があるかを考えるというオーバーヘッドなしにアルゴリズムの概念を素早くプロトタイプ化するのに役立ちます(これは静的に型付けされた言語 eに必要です)。
静的型付き言語(コンパイラはメソッド呼び出しを解決し、参照をコンパイルします):
動的型付け言語(実行中のプログラムでの決定):