2017-05-14 19 views
3
trait foo[F] { 
    def test: F 
} 

class ah extends foo[(Int,Int) => Int] { 
    def test = (i: Int,j: Int) => i+j 
} 

Scalaがタイプについてスマートであることが分かっている理由は、タイプ(Int,Int) => Intからタイプtestを推測するのはなぜですか?それともまだ可能ですか?あるいは、これは私が気にしていない考えによって支えられているかもしれません。Scalaが特性型パラメータを推定しないのはなぜですか?

答えて

7

あなたの質問は、基本的に「スカラにはローカル型推論のみがあるのはなぜですか?」とか、あるいは「スカラに非ローカル型推論がないのはなぜですか?答えは、デザイナーが望んでいないからです。

これにはいくつかの理由があります。理由の1つは、ローカル型推論の最も妥当な代替案はグローバル型推論ですが、それはScalaでは不可能です:Scalaは別々のコンパイルとモジュール型のチェックを持っているので、コンパイラが全体を全体的に見ることはできませんプログラム。実際、Scalaには動的なコードのロードがあります。つまり、コンパイル時にコード全体がまだ存在する必要はありません。 Scalaでは、グローバルな型推論は単純に不可能です。私たちができることは、 "全体コンパイル単位型推論"ですが、それは望ましくないことです。型の注釈が必要かどうかは、コードを複数の単位でコンパイルするか1つだけコンパイルするかによって異なります。

もう1つの重要な理由は、モジュールの境界での型注釈が、型のための二重登録ブックのような二重チェックのように機能することです。言語でさえ、ハスケルのようなグローバル型の推論であっても、タイプ境界をモジュールの境界やパブリックインターフェイスに置くことが強く推奨されます。

第三の理由は、グローバルな型推論は時々代わり型注釈で失敗型チェッカの最後になるまで、それは、型inferencerは喜んで、ますます無意味タイプを推論沿っchugs場合、紛らわしいエラーメッセージをもたらすことができるということである実際のエラー(単純なタイプミスかもしれません)から離れた場所で、エラーサイトの元のタイプに接して接するだけのタイプエラーをあきらめます。これは、例えばHaskellで起こることがあります。 Scalaのデザイナーは有用なエラーメッセージを非常に重視し、良いエラーメッセージでそれらを実装する方法を理解できない限り、言語機能を犠牲にしています。 (でも、Scalaの非常に限られた型推論で、あなたは「FooProduct with Serializableを得た期待」のような「役に立つ」メッセージを取得できることに注意してください。)

をしかし、この例では何ができるかScalaのローカル型推論、働くことですあいまいなことができ、継承に基づいて、型パラメータを推測、あなたの特定の例

trait foo[F] { 
    def test: F 
} 

class ah extends foo[(Int, Int) ⇒ Int] { 
    def test = (i, j) ⇒ i + j 
} 

(new ah) test(2, 3) //=> 5 
+0

だからHindley-Milnerは 'test'の' foo'の型を推測することができますか? – slouc

+0

Scalaには、H-Mがサポートしていないサブタイプがあります。 (多くの関数型言語とは異なり、Scalaの型システムはHMからかなり離れています)。しかし、グローバル型の推論を持つ仮説言語では、サブタイプ化や演算子のオーバーロードがなくても、ゼロ型アノテーションを持つすべての型を推論することができます。 ''と '' j 'で、無名関数の戻り値の型は '+'演算子の整数である(覚えておいてください:演算子のオーバーロードはありません)、 'test'の型を無名関数の関数型、 'test'の戻り値の型になります。おもう。 –

+0

ええ、 'test'を推論するのに同意しましたが、' foo'の推論のためにその情報を使うことができるのだろうかと思っていました。 TBH私は、標準的なクラスのような構造がH-Mでサポートされているか許可されているか分からない(サブタイプの部分なしで、その型を返すメソッドを持つ単純な型パラメータ化されたクラス)。 – slouc

1

::他の方向に、そしてtestの戻り値の型から無名関数のパラメータの型を推論

trait A 
trait B extends A 

trait Foo[T] { 
    val x: T 
} 

trait Bar extends Foo[?] { 
    val x: B 
} 

?に入るタイプは、AまたはBのいずれかです。これは、Scalaがあいまいである可能性がある型を推論しない一般的な例です。

さて、あなたは

class Foo[T](x: T) 

trait Foo[T] { x: T } 

の間に類似性があることを観察することが適切である私は、おそらく類似性を一般に、いくつかの作品を見てきました(私が見えることはできません今すぐ見つけてください)。それは、理論的には、メンバに基づいた型パラメータの型推論を可能にすることができます。しかし、私はそれがこれまでになるかどうかはわかりません。

+0

それはDottyにありますか?つまり、Dottyにはジェネリックスはなく、抽象型のメンバーには向いていません。私。 '形質Foo [T]; IntFooクラスはFoo [Int] 'を拡張しています。実際は' trait Foo {type T}です。クラスIntFooはDottyのFoo {override type T = Int} 'を拡張します。 –

+0

その引数によって、 'def x = new B'の型を返すべきでもありません。その型は' A'または 'B'(または' Object'または 'Any')でもかまいません。 –

+0

@AlexeyRomanovあなたが正しいです。違いは、後に形質を拡張することができるため、型パラメーターが上限と下限の両方であることです。そのため、最も狭いタイプのものを推測するのは安全ではありません。 – Owen

関連する問題