web-dev-qa-db-ja.com

型推論のトレードオフは何ですか?

すべての新しいプログラミング言語、または少なくとも一般的に使用されている型推論が使用されているようです。 Javascriptでさえ、さまざまな実装(Acscript、TypeScriptなど)を通じて型と型推論を得ました。それは私には素晴らしく見えますが、トレードオフがあるのか​​、なぜJavaまたは古い良い言語に型推論がないのか)と思いますか?

  • タイプを指定せずにGoで変数を宣言する場合(タイプなしのvarまたは:=構文を使用)、変数のタイプは右側の値から推測されます。
  • [〜#〜] d [〜#〜]動的言語のように型を冗長的に指定することなく、大きなコードフラグメントを書き込むことができます。一方、静的推論は型やその他のコードプロパティを推定し、静的な世界と動的な世界の両方のベストを与えます。
  • Rustの型推論エンジンはかなりスマートです。初期化中にr値のタイプを確認するだけではありません。また、変数を使用してその型を推測する方法も確認します。
  • Swiftは、型推論を使用して適切な型を計算します。型推論により、コンパイラは、コードをコンパイルするときに、指定した値を調べるだけで、特定の式の型を自動的に推定できます。
29

Haskellの型システムは完全に推論可能です(多態的な再帰、特定の languageextensions 、および恐ろしい monomorphism制限 を除いて)、それでもプログラマーはまだ頻繁に型を提供しています必要がない場合でも、ソースコードの注釈。どうして?

  1. 型注釈はドキュメントとして機能します。これは、Haskellのように表現力豊かな型に特に当てはまります。関数の名前と型を指定すると、通常、特に関数がパラメトリック多態性である場合、関数が何をするかをかなりよく推測できます。
  2. Typeアノテーションは開発を推進できます。関数の本体を記述する前に型シグネチャを記述することは、テスト駆動開発のようなものです。私の経験では、Haskell関数をコンパイルすると、通常は初めて機能します。 (もちろん、これは自動テストの必要性を未然に防ぎません!)
  3. 明示的な型は、仮定の確認に役立ちます。すでに機能しているコードを理解しようとするとき、正しい型注釈であると私が信じるものを頻繁にペッパーにします。コードがまだコンパイルされる場合、私はそれを理解したことを知っています。表示されない場合は、エラーメッセージを読みます。
  4. 型シグネチャを使用すると、ポリモーフィック関数を特殊化できます。非常にまれに、特定の関数がポリモーフィックでない場合、APIはより表現力豊かで有用です。推定されるよりもless汎用型を関数に指定しても、コンパイラーは文句を言わないでしょう。古典的な例はmap :: (a -> b) -> [a] -> [b]です。より一般的な形式(fmap :: Functor f => (a -> b) -> f a -> f b)は、リストだけでなく、すべてのFunctorsに適用されます。しかし、mapは初心者には理解しやすいと感じられたため、兄と一緒に暮らしています。

全体として、静的に型付けされているものの、エラーが発生しないシステムの欠点は、一般に静的型付けの欠点とほとんど同じです。このサイトなどでよく使われているディスカッション(「静的型付けの欠点」をググると、何百もの問題が発生します。炎の戦争のページの)。もちろん、上記の欠点のいくつかは、推論可能なシステムにおけるより少ない量の型注釈によって改善されます。さらに、型推論には独自の利点があります。 ホール駆動型開発 は型推論なしでは実現できません。

Java *は、あまりにも多くの型注釈を必要とする言語が煩わしいことを証明しますが、少なすぎると、上記で説明した利点を失うことになります。オプトアウト型の推論を使用する言語は、2つの極端な方法のバランスが取れています。

*素晴らしいスケープゴートであるJavaでも、一定量のlocal型推論を実行します。 Map<String, Integer> = new HashMap<>();のようなステートメントでは、コンストラクターのジェネリック型を指定する必要はありません。一方、MLスタイルの言語は通常globally推論可能です。

44

C#では、型推論はコンパイル時に行われるため、実行時のコストはゼロです。

スタイルの問題として、varは、タイプを手動で指定することが不便または不必要な状況で使用されます。 Linqはそのような状況の1つです。もう一つは:

var s = new SomeReallyLongTypeNameWith<Several, Type, Parameters>(andFormal, parameters);

これがないと、単にvarと言うのではなく、本当に長い型名(および型パラメーター)を繰り返すことになります。

明示的である場合は、型の実際の名前を使用して、コードの明確性を向上させます。

値が構築時に設定されるメンバー変数宣言など、型推論を使用できない状況や、インテリセンスを正しく機能させたい場合があります(HackerrankのIDEはインテリセンスを行いません)型を明示的に宣言しない限り、変数のメンバー)。

8
Robert Harvey

良い質問!

  1. 型には明示的な注釈が付けられていないため、コードが読みにくくなり、バグが増える可能性があります。もちろん、それを適切に使用すると、コードがよりクリーンになり、moreが読みやすくなります。あなたが悲観論者で、ほとんどのプログラマーが悪い(または、ほとんどのプログラマーが悪いであるような仕事)だと思う場合、これは正味の損失になります。
  2. 型推論アルゴリズムは比較的単純ですが、freeではありません。この種のものはコンパイル時間をわずかに増やします。
  3. 型には明示的な注釈が付けられていないため、IDEは何をしようとしているかを推測できず、宣言プロセス中にオートコンプリートや類似のヘルパーに悪影響を及ぼします。
  4. 関数のオーバーロードと組み合わせると、型推論アルゴリズムがどのパスを取るかを決定できない状況に陥り、醜いキャストスタイルアノテーションにつながる可能性があります。 (これは、たとえばC#の無名関数構文ではかなり発生します)。

そして、明示的な型注釈なしに奇妙なことをすることができないより難解な言語があります。これまでのところ、私が知っている一般的/人気/将来性があることを除いて、言及するのに十分有望なものはありません。

7
Telastyn

それは私には素晴らしく見えますが、トレードオフがあるのか​​、なぜJavaまたは古い良い言語に型推論がないのか)と思いますか?

ここでのルールではなく、Javaが例外です。 C++(私が信じるのは「古き良き言語」と見なされます:)ですら、C++ 11標準以降、autoキーワードを使用した型推論をサポートしています。変数の宣言だけでなく、関数の戻り値の型としても機能します。これは、一部の複雑なテンプレート関数で特に便利です。

暗黙的な型付けと型推論には多くの優れたユースケースがあり、実際に実行してはいけないユースケースもあります。これは時々好みの問題であり、議論の対象でもあります。

しかし、間違いなく優れたユースケースがあることは、それ自体が言語がそれを実装する正当な理由です。また、この機能の実装は難しくなく、実行時のペナルティはなく、コンパイル時間にも大きな影響はありません。

開発者に型推論を使用する機会を与えることには、実際の欠点はありません。

一部の回答者は、明示的な型付けが時々どのように良いかを推論し、それらは確かに正しいです。しかし、暗黙の型付けをサポートしないことは、言語が常に明示的な型付けを強制することを意味します。

したがって、本当の欠点は、言語しないが暗黙の型付けをサポートする場合です。これにより、開発者がそれを使用する正当な理由がないことが示されます。

4
Gábor Angyal

Hindley-Milner型推論システムとGoスタイル型推論の主な違いは、情報フローの方向です。 HMでは、型情報は、統一を介して前後に流れます。 Goでは、タイプ情報は転送のみを転送します。転送置換のみを計算します。

HMの型推論は、ポリモーフィックに型付けされた関数型言語でうまく機能する素晴らしい革新ですが、Goの作者は、多すぎることを試みているとおそらく主張します。

  1. 情報が順方向と逆方向の両方に流れるという事実は、HMタイプの推論が非常に非局所的な問題であることを意味します。型注釈がない場合、プログラム内のすべてのコード行が1行のコードの入力に影響している可能性があります。前方置換のみしかない場合は、型エラーの原因がエラーの前のコードにある必要があることがわかります。後に続くコードではありません。

  2. HMの型推論では、constraintsで考える傾向があります。型を使用する場合、その型に可能な型を制限します。結局、完全に制約されないままになっているタイプ変数があるかもしれません。 HMタイプ推論の洞察は、これらのタイプは実際には重要ではないため、それらは多態変数に変換されます。ただし、この余分な多型は、いくつかの理由で望ましくない場合があります。最初に、一部の人々が指摘したように、この余分なポリモーフィズムは望ましくない可能性があります。HMが一部の偽のコードの偽の多形型を結論付け、後で奇妙なエラーを引き起こす。次に、型がポリモーフィックのままになっていると、実行時の動作に影響を与える可能性があります。たとえば、過度に多態的な中間結果が 'showの理由です。 「読む」はHaskellではあいまいと見なされます。別の例として、ポリモーフィック値は、それらが評価されるタイプごとに複数回評価されなければならず、単相性の制限が動機付けられます。

1
Edward Z. Yang