型システムの安全上の利点は何ですか?


47

JavaScriptを:良い部品ダグラス・クロックフォードによると、彼は彼の継承の章に言及し、

古典的な継承のもう1つの利点は、型システムの仕様が含まれることです。これにより、プログラマは明示的なキャスト操作を記述する必要がなくなります。これは、キャスト時に型システムの安全性の利点が失われるため、非常に良いことです。

それではまず、実際に安全とは何ですか?データ破損、ハッカー、システムの誤動作などに対する保護

型システムの安全上の利点は何ですか?これらの安全性の利点を提供できるように、型システムの違いは何ですか?


型システムがコンパイルされていない言語にメリットがあるかどうかはわかりませんが、コンパイルされた言語の長期的なユーザーとして、慎重に型をチェックするコンパイルされた言語は、 「コンパイル」段階を通過します。型のヒントとLintシステムはWeb Scripting(JavaScript)にとって価値があると言えると思います。もしそうなら、それらは十分に見られると思います。ダーツ誰か?Pythonのような動的言語は、静的型システムがなくても悪くはないようです。
ウォーレンP

1
現在、タイピングは行動的なものであり、構造的なものではないことを理解しています。残念なことに、ほとんどの最新のプログラミング言語には、型の振る舞いをアサートする方法がありません(良い質問については、この質問参照してください)。これにより、ほとんどの場合、型システムはほとんど役に立たなくなります。なぜなら、ここで言及されている単純な型エラーは、一般的な問題をチェックする賢いリンターによってキャッチされるからです。
ベンジャミングリュンバウム

4
@BenjaminGruenbaumあなたの記述は、OCamlのような言語には既に静的に存在しています。これは構造型付けと呼ばれ、実際にはかなり古く、名義型付けは新しいです。
-jozefg

2
@BenjaminGruenbaum:...何!?静的に型付けされた言語では明らかに決定不能ではありません。さもなければ、それらの言語用のコンパイラを書くことは不可能です。
BlueRaja-ダニーPflughoeft

6
@BenjaminGruenbaum:あなたのご意見は貴重であり、その紙は興味深いですが、それは、それはそれがあることを示しているので、「それはあまりにもJavaのような静的言語では、通常は決定不能だ」というあなたの主張うち負いませんである C#で決定可能にし、葉が質問を開きますJavaで決定できないかどうか。(とにかく、IME、静的に型付けされた言語のコンパイラが何かが適切に型付けされていると判断できない場合、それを拒否する(またはコンパイルに失敗する)ため、決定不能は型の穴ではなく厄介です-安全。)
ruakh

回答:


82

型システムはエラーを防ぐ

型システムは違法プログラムを排除します。次のPythonコードを検討してください。

 a = 'foo'
 b = True
 c = a / b

Pythonでは、このプログラムは失敗します。例外をスローします。Java、C#、Haskellなどの言語では、これは合法的なプログラムではありません。これらのエラーは入力プログラムのセットでは不可能なため、完全に回避できます。

同様に、より良い型システムはより多くのエラーを除外します。超高度な型システムにジャンプすると、次のようなことが言えます。

 Definition divide x (y : {x : integer | x /= 0}) = x / y

これで、型システムは0除算エラーがないことを保証します。

どのようなエラー

タイプシステムが防止できるエラーの簡単なリストを以下に示します。

  1. 範囲外エラー
  2. SQLインジェクション
  3. 一般化2、多くの安全性の問題(Perlでの汚染チェックの目的)
  4. 順不同エラー(initの呼び出しを忘れる)
  5. 値のサブセットを強制的に使用する(たとえば、0より大きい整数のみ)
  6. 邪悪な子猫 (はい、それは冗談でした)
  7. 精度低下エラー
  8. ソフトウェアトランザクショナルメモリ(STM)エラー(これには純度が必要です。これには型も必要です)
  9. 一般化8、副作用の制御
  10. データ構造に対する不変量(二分木はバランスがとれていますか?)
  11. 例外を忘れるか、間違った例外をスローする

そして、これもコンパイル時に行われることを忘れないでください。型エラーを単純にチェックするために、100%のコードカバレッジでテストを記述する必要はありません。コンパイラがあなたのためにそれを行います:)

ケーススタディ:型付きラムダ計算

では、すべての型システムの中で最も単純な、ラムダ計算と単純に型付けされたものを調べてみましょう。

基本的には2つのタイプがあります。

Type = Unit | Type -> Type

そして、すべての用語は変数、ラムダ、またはアプリケーションのいずれかです。これに基づいて、適切に型付けされたプログラムが終了することを証明できます。プログラムがスタックしたりループしたりする状況は決してありません。これは、実際にはそうではないため、通常のラムダ計算では証明できません。

これについて考えてみましょう。型システムを使用して、プログラムが永遠にループしないことを保証できます。

動的型への迂回

動的型システムは、静的型システムと同じ保証を提供できますが、コンパイル時ではなく実行時です。実際、ランタイムであるため、実際により多くの情報を提供できます。ただし、特に終了などの静的プロパティについては、いくつかの保証が失われます。

したがって、動的型は特定のプログラムを除外するのではなく、例外のスローなど、明確に定義されたアクションに不正な形式のプログラムをルーティングします。

TLDR

その長短は、型システムが特定のプログラムを除外することです。プログラムの多くは何らかの形で壊れているため、型システムではこれらの壊れたプログラムを回避します。


25
多数のテストを作成するのと同等のコンパイルとして+1。
ダンニーリー

3
@DanNeely動的言語では、コードのすべての部分を実行して、型システムが無料でチェックするエラーをキャッチする必要があることを説明するだけです。そして、依存型付けされた言語では、実際にテストを型に完全に置き換えることができます。多くの場合、追加の正確性の定理を証明する必要があります
-jozefg

3
型システムがプログラムを終了しなければならないことを証明した場合、(おそらく)プリミティブ再帰関数を計算していることを証明することで終了します。これはクールだと思いますが、本物のチューリングマシンが解決できるクラスよりもかなり複雑度の低いクラスです。(中間値が大きくないという意味ではありません。アッカーマン関数は原始再帰的です…)
ドナルフェローズ

5
@DonalFellows Ackermann関数は、完全に計算可能な関数ですが、プリミティブな再帰的ではありません。
タイモン

4
@sacundimまさに、agdaのような言語は、オプションの全体チェックを可能にし、まれに任意の再帰が必要な場合にうまく尋ねることができます。これは非常に滑らかなシステムです。
-jozefg

17

現実自体が入力されます。ウェイトに長さを追加することはできません。また、メートルにフィートを追加することもできます(どちらも長さの単位です)が、2つのうち少なくとも1つをスケーリングする必要があります。そうしないと、文字通り火星ミッションをクラッシュさせる可能性があります。

タイプセーフなシステムでは、異なる単位で表現された2つの長さを追加するとエラーになるか、自動キャストが発生します。


15

型システムは、単純なコーディングエラーを回避するのに役立ちますが、コンパイラーがこれらのエラーをキャッチできるようにします。

たとえば、JavaScriptおよびPythonでは、次の問題は実行時にのみ検出されることがよくあります。テストによっては、条件の品質/希少性が実際に実稼働に至る場合があります。

if (someRareCondition)
     a = 1
else
     a = {1, 2, 3}

// 10 lines below
k = a.length

厳密に型指定された言語でaは、それが配列であることを明示的に宣言する必要があり、整数を割り当てることはできません。このように、まれなケースでも、チャンスaはありませんlength


5
そして、WebStorm JavaScriptのようなIDEの賢いリンターは、「番号aのa.lengthへの可能な未定義参照」と言うことができます。これは、明示的な型システムを持つことによって私たちに与えられるものではありません。
ベンジャミングリュンバウム

4
1.静的に強くない2. @BenjaminGruenbaumはい、しかしこれはバックグラウンドで割り当てのグラフを追跡することで行われます。それは、物事がどこに向かっているのかを理解しようとするミニ通訳のように考えてください。タイプが無料で提供する場合よりもはるかに難しい
-jozefg

6
@BenjaminGruenbaum:暗黙的/明示的と強い/弱いを混同しないでください。たとえば、Haskellは他のほとんどの言語を恥じる非常に強力な型システムを持っていますが、特定の言語設計の決定により、ほぼ完全に普遍的な型推論も可能になり、本質的に明示的な型付けをサポートする強力な暗黙型型言語になりました(型推論器はあなたが書いたものから推測するだけで、あなたが意図したものではないからです!)
Phoshi

6
「強く型付けされた言語は、aが配列であることを明示的に述べることを強制します」それは間違っています。Pythonは強く型付けされており、それを必要としません。静的で強く型付けされた言語でさえ、型推論をサポートしている場合、それを必要としません(そして、今日のほとんどの主流言語は、少なくとも部分的にサポートしています)。
コンラッドルドルフ

1
@BenjaminGruenbaum:ああ、まあまあ。そうであっても、JS静的アナライザーが厳密に型指定された言語が提供するのと同じ種類の型チェックを実行できない場合があります。一般的な場合、それを解決するには停止問題を解決する必要があります。Haskellは、ほぼ100%の型推論を達成するために、かなりの数の設計上の決定を行う必要があり、C#/ Scalaはすべてを推論することはできません。もちろん、これらの場合、型を明示的に指定するだけでよいので問題ではありません。Javascriptでは、最高の静的アナライザーでさえコードをチェックできなくなります。
-Phoshi

5

ソフトウェア開発サイクルの早い段階でエラーをキャッチできれば、修正にかかる費用は少なくなります。最大のクライアントまたはすべてのクライアントがデータを失う原因となるエラーを考えてください。実際の顧客がデータを失った後にのみ検出された場合、そのようなエラーはあなたの会社の終わりかもしれません!本番環境移行するにこのバグを見つけて修正する方が明らかに安価です。

それほど費用のかからないエラーであっても、プログラマーがそれを見つけて修正できる場合よりも、テスターが関与している場合は、より多くの時間とエネルギーが費やされます。他のプログラマーが依存するソフトウェアをビルドできるソース管理にチェックインされない場合、より安価です。型の安全性により、特定のクラスのエラーがコンパイルされることさえ防止されるため、これらのエラーの潜在的なコストがほぼすべて排除されます。

しかし、それだけではありません。動的言語でプログラムを作成する人なら誰でも言うように、プログラムをコンパイルするだけで、細かい部分をすべて理解することなく、その一部を試すことができれば便利な場合があります。安全性と利便性の間にはトレードオフがあります。単体テストは動的言語を使用するリスクの一部を軽減できますが、適切な単体テストを作成および維持するには、タイプセーフ言語を使用するよりも高いコストがかかる可能性があります。

実験している場合、コードを1回だけ使用する場合(1回限りのレポートなど)、とにかくユニットテストを作成する必要がない場合は、動的言語がおそらく完璧ですあなたのために。大規模なアプリケーションを使用していて、一部を変更せずに残りの部分を変更したい場合は、タイプセーフが命を救います。エラーのタイプタイプセーフティキャッチは、リファクタリング時に人間が見落としたり間違ったりする傾向があるエラーの種類です。


これは、動的な型付けを短く売りますが、その主な利点については言及していません(言及したものは比較的重要ではないので便利です)。また、単体テストについて奇妙なことを暗示しているようです。はい、実行するのは難しく、コストがかかります。これは静的に型付けされた言語にも当てはまります。これは何を言おうとしているのですか?また、表現できるものとキャッチできるエラーの両方で、現在の型システムの制限(設計上)について言及していません。

@MattFenwick動的型付けの主な利点は何だと思いますか?
グレンペターソン

典型的な静的型システムは、多くの適切に型付けされたプログラムを設計上拒否します。(代替案)(ところで、私の批判は3番目と4番目の段落にのみ向けられました。)

4

前書き

型の安全性は、静的型付け(コンパイル済み、静的型チェック)および/または実行時(評価済み、動的型チェック)言語で実現できます。ウィキペディアによると、「... 強い型システムは、未チェックの実行時型エラーの可能性がないものとして説明されています(ed Luca Cardelli)。他の記述では、未チェックの実行時エラーがないことは、安全性または型安全性と呼ばれています... '

安全性-静的型チェック

古典的に、型の安全性は、C、C ++、Haskellなどの言語での静的型付けと同義語であり、コンパイル時に型の不一致を検出するように設計されています。これには、プログラムの実行時に潜在的に未定義またはエラーが発生しやすい状態を回避できるという利点があります。これは、ポインターの種類が一致しないリスクがある場合、たとえば、検出されない場合は壊滅的な結果につながる可能性がある場合に非常に役立ちます。この意味で、静的型付けはメモリの安全性と同義と見なされます。

静的型付けは完全に安全ではありませんが、安全性を高めます。静的に型付けされたシステムでさえ、壊滅的な結果をもたらす可能性があります。多くの専門家は、静的型付けを使用して、より堅牢でエラーが発生しにくい(ミッションクリティカルな)システムを作成できると考えています。

静的に型付けされた言語、doubleのfloat型への不一致または切り捨て、または整数型とfloat型の不一致が原因で発生する可能性がある、データの損失または数値処理の精度の損失のリスクを減らすのに役立ちます。

静的に型付けされた言語を使用すると、効率と実行速度が向上するという利点があります。ランタイムは、実行中にタイプを決定する必要がないという利点があります。

安全性-ランタイムタイプチェック

たとえば、Erlangは、仮想マシン上で実行される型宣言型の動的に型チェックされる言語です。Erlangコードはバイトコンパイルできます。Erlangはおそらく最も重要なミッションクリティカルなフォールトトレラント言語と考えられており、Erlangの信頼性は9 9(99.9999999%または年間31.5ミリ秒以下)であると報告されています。

Common Lispなどの特定の言語は静的に型指定されていませんが、必要に応じて型を宣言でき、速度と効率を向上させることができます。また、Pythonなどのより広く使用されているインタープリター言語の多くは、評価ループの下で、CやC ++などの静的に型付けされた言語で記述されていることにも注意してください。上記の定義では、Commom LispとPythonはどちらもタイプセーフと見なされます。


2
「強く型付けされた」ことに反対します。あなたは静的に型付けされることを意味します。強くタイプはかなり意味を運ばない、基本的に「Iこのタイプのシステムのように」と言って使われています
jozefg

@ jozefg良い点。投稿を修正します。
AsymLabs

3
インタプリタ言語と言うのも役に立ちません...言語の実装についてはい、言語自体はそうではありません。任意の言語を解釈またはコンパイルできます。また、編集後でも、強い型付けと弱い型付けという用語を使用しています。
エサイリア

3
@jozefg:強く型付けすることは、各値が固定型(整数、文字列など)を持っていることを意味するのに対し、弱型付けは、値を別の型の値に強制できることを意味します。そう。たとえば、Python(厳密に型指定された)では1 + "1"例外がスローされますが、PHP(弱い型指定)1 + "1"では生成されます2(文字列"1"は自動的に整数に変換されます1)。
ジョルジオ

1
Javaなどのこのような定義を持つ@Giorgioは、厳密に型指定されていません。しかし、多くの場合、そうであると主張されています。これらの言葉には意味がありません。強い/弱い型付けは、jozefgが言うように、「私はこの言語が好き/好きではない」というより正確な定義を持っています。
エサイリヤ

1

型システムの安全上の利点は失われます。

それではまず、実際に安全とは何ですか?データ破損、ハッカー、システムの誤動作などに対する保護

型システムの安全上の利点は何ですか?これらの安全性の利点を提供できるように、型システムの違いは何ですか?

型システムにはそのような否定的な見方があるように感じます。型システムは、エラーがないことを証明することよりも、保証することの方が重要です。後者は型システムの結果です。プログラミング言語の型システムは、コンパイル時に、プログラムが何らかの仕様を満たしていることを証明する方法です。

型としてエンコードできる仕様の種類は、言語に依存するか、より直接的に言語の型システムの強度に依存します。

最も基本的な種類の仕様は、関数の入出力動作および関数本体の内部の有効性に関する保証です。関数ヘッダーを検討する

f : (Int,Int) -> String

優れた型システムは、評価時にfがIntのペアを生成するオブジェクトにのみ適用され、fが常に文字列を生成することを保証します。

if-thenブロックなどの言語の一部のステートメントには、入出力の動作がありません。ここで、型システムは、ブロック内の各宣言またはステートメントが有効であることを保証します。つまり、正しい種類のオブジェクトに操作を適用します。これらの保証は構成可能です。

また、これはある種のメモリ安全条件を提供します。あなたが扱っている引用は、キャストに関するものです。場合によっては、32ビットのIntを64ビットのIntにキャストするのと同じように、キャストは問題ありません。ただし、一般的に、型システムはクラッシュします。

検討する

Foo x = new Foo(3,4,5,6);
f((Int)x,(Int)x);

キャストのため、xはIntに変換されるため、技術的には上記の型チェックが行われます。ただし、型チェックの目的を実際に無効にします。

別のより良い型システムを作ることができる1つのことは、BがAのサブタイプ(またはサブオブジェクト)でない限り、ケースの前のxが型Bであるキャスト(A)xを禁止することです。整数オーバーフロー/アンダーフロー攻撃の可能性を排除するため。

概要

型システムは、プログラムが何らかの仕様を満たしていることを証明する方法です。型システムが提供できる利点は、使用する型システムの強度によって異なります。


1

型システムについてまだ言及されていない利点の1つは、多くのプログラムが書かれているよりも多く読まれるという事実に集中しており、多くの場合、型システムは簡潔で簡単な方法で多くの情報を指定できるようにする場合がありますコードを読んでいる人によって消化されます。パラメーターの種類は説明的なコメントの代わりにはなりませんが、ほとんどの人は「int Distance;」と読む方が速いと感じるでしょう。またはDistance As Int32「距離は整数である必要があります+/- 2147483647」; さらに、パラメータタイプは、APIの特定の実装が実行することと、呼び出し元が依存する資格があるものとの間のギャップを減らすのに役立ちます。たとえば、APIの特定のJavascript実装が使用する場合数値形式に任意の文字列を強要ような方法でそのパラメータが、APIの他の実装があれば指定した文字列を誤動作することがあります場合は発信者がそのような行動に依拠することができ、またはされているかどうかは不明かもしれません。そのパラメータとして指定されている方法持つDoubleでしょうが文字列値は、渡される前に呼び出し元によって強制されなければならないことを明確にします;受け入れるオーバーロードと受け入れるDouble他のメソッドを持つString 文字列を保持している呼び出し元がそのようにそれらを渡すことが許可されることをいくらか明確にします。


0

それではまず、実際に安全とは何ですか?データの破損、ハッカー、システムの誤動作などに対する保護

他のすべての答えなど。一般に、「型安全」とは、コンパイラが正常にコンパイルするプログラムに型エラーが含まれないことを意味します。

さて、型エラーとは何ですか?原則として、任意の望ましくないプロパティを型エラーとして指定できます。また、一部の型システムでは、プログラムにそのようなエラーがないことを静的に保証できます。

上記の「プロパティ」とは、たとえば「すべてのインデックスが配列の範囲内にある」など、プログラムに適用される何らかの論理的命題を意味します。他のタイプのプロパティには、「すべての遅延ポインタが有効」、「このプログラムはI / Oを実行しない」、「このプログラムは/ dev / nullのみにI / Oを実行する」などがあります。プロパティは、タイプシステムの表現力に応じて、この方法で指定およびタイプチェックできます。

依存型システムは、最も一般的な型システムの1つであり、これを使用して、ほぼすべてのプロパティを適用できます。ただし、洗練されたプロパティはGödelの好意により不完全性の影響を受けるため、必ずしもそうすることは必ずしも容易ではありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.