2012-04-27 24 views
6

この問題は私が書いているモジュールで生じましたが、私は同じ挙動を示す最小限のケースを作りました。なぜ型推論がここで機能しないのですか?

class Minimal[T](x : T) { 
    def doSomething = x 
} 

object Sugar { 
    type S[T] = { def doSomething : T } 
    def apply[T, X <: S[T]] (x: X) = x.doSomething 
} 

object Error { 
    val a = new Minimal(4) 
    Sugar(a) // error: inferred [Nothing, Minimal[Int]] does not fit the bounds of apply 
    Sugar[Int, Minimal[Int]](a) // works as expected 
} 

問題は、コンパイラはMinimalInt)のための内部パラメータを把握して管理することであるが、その後明らかapply一致しないNothingからTの他の発生を、設定します。これらは明らかに同じTです。最初のパラメータを削除すると、2番目のパラメータを削除するとTが定義されていないと不平を言うようになります。

コンパイラが最初のパラメータを推論できないという曖昧さがありますか、それともバグですか?私はこれをうまくやっていくことができますか?

詳細情報:このコードは、構文的砂糖の試行の簡単な例です。元のコードでは、|(a)|は係数がaであることを意味します。ここで、aはベクトルです。明らかに|(a)||[Float,Vector3[Float]](a)|と書くよりも優れていますが、残念ながらunary_|を使用してこれを簡単にすることはできません。

実際のエラー:構造タイプの境界を持ついくつかのすごみがあります

inferred type arguments [Nothing,Minimal[Int]] do not conform to method apply's type parameter bounds [T,X <: Sugar.S[T]]

答えて

9

これはScalaコンパイラのバグではありませんが、確かにScalaの型推論には限界があります。コンパイラはXS[T]の境界を決定してからXを解決することにしましたが、バインドには制約のないタイプの変数Tが記述されており、そこで修正されてNothingに進みます。これは再訪しませんTXが完全に解決されました...現在、この種のケースでは常に型推論が左から右に進みます。

あなたの例では、正確にあなたの本当の状況はその後、簡単な修正がある表す場合は、ここで

def apply[T](x : S[T]) = x.doSomething 

TMinimalS[T]に直接ではなく、仲介有界型の変数を経由して適合していることを、このような推測されます。

更新

ジョシュアのソリューションは、タイプTを推論の問題を回避するが、完全に異なる方法インチ(TがもはやXのバウンドに記載されているため)に

def apply[T, X <% S[T]](x : X) = x.doSomething 

desugars、

def apply[T, X](x : X)(implicit conv : X => S[T]) = x.doSomething 

型変数TXについて独立して解くことができません。これは、XがすぐにMinimalと推定され、暗黙の引き数convを満たすようにX => S[T]の値の暗黙検索の一部としてTが解決されることを意味します。 conformsscala.Predefは、この形式の値を製造しており、文脈ではMinimal型の引数が与えられ、TがIntとして推論されることを保証します。 Scalaでは、これをfunctional dependenciesのインスタンスとして見ることができます。

+0

はい、このソリューションは私の場合は正常に動作します。それはまたジョシュアの解決策よりもきれいです(申し訳ありませんジョシュア!)。なぜジョシュアのソリューションが機能するのか説明できますか? – Dylan

+0

Joshuaのソリューションがなぜ機能するのかを説明する回答が更新されました。 –

4

、代わりにS [T]にバインドビューを使用してみてください。

def apply[T, X <% S[T]] (x: X) = x.doSomethingが問題ありません。

+1

これはうまくいきますが、アプローチ間に違いはありませんか?この場合のビューは、スーパークラスにキャストしているだけですか?これは、この修正がコンパイラのバグに対する回避策に過ぎないことを意味しますか? – Dylan

+0

ああ、今見ましたが、 'S'はスーパークラスではなく、単なるビューです。したがって、ほとんどの場合、ビューバインディングは必要ではないにもかかわらず、より適切です。 – Dylan

関連する問題