2016-04-25 12 views
1

なぜこれは機能しませんか? Scalaは、case HasIntsの場合には、outer.type <: HasInts.typeを理解する必要があります。回避策(型メンバーを削除せずに)は何ですか?パターンマッチングと実在型

sealed trait Outer { 
    type Inner 
} 

case object HasInts extends Outer { 
    override type Inner = Int 
} 

def id(outer: Outer): outer.Inner = 
    outer match { 
    case HasInts => 0 
    } 

編集:私たちは実存からユニバーサルへの型のメンバを変更した場合otherhandで

、それがコンパイルされます。

sealed trait Outer[Inner] 

case object HasInts extends Outer[Int] 

def id[I](outer: Outer[I]): I = 
    outer match { 
    case HasInts => 0 
    } 

なぜそれが最初のケースではなく第二に働くだろうか?彼らは間違いなく同一です(実際にはDottyになります)。

+0

「外」という2つの定義には基本的な違いはありません。実際には同等です。あなたは 'id'の定義を' def id [I](外側:外側{type Inner = I}):I = ... 'と書くことができました。型フィールドは、実在型を意味するものではありません。 – jpcooper

答えて

2

従属型。コンパイラがIntを返すときに何であるかを特定する方法はありません。エラーがうまくそれを置く:

scala> def id(outer: Outer): outer.Inner = 
    | outer match { 
    |  case [email protected] HasInts => 0 : HasInts.Inner 
    | } 
<console>:15: error: type mismatch; 
found : HasInts.Inner 
    (which expands to) Int 
required: outer.Inner 
      case [email protected] HasInts => 0 : HasInts.Inner 
           ^

コンパイラは特にそれがIntであることを知っています。しかし、メソッドの定義によれば、それはouter.Innerを返します。従属型を静的に使用すると、outer.Inner以外の情報はありません。基本的には何も伝えません。

sealed trait Outer { 
    type Inner >: Int 
} 

をそれとも、できます:たとえば、以下の変更と、それは動作します

sealed trait Outer { 
    type Inner 
    def f: Inner 
} 

case object HasInts extends Outer { 
    override type Inner = Int 
    def f:Inner = 23 
} 

def id(outer: Outer): outer.Inner = outer.f 

私は1つは、右のそれらを得るために、その少し難しいように依存するタイプの方に行かなければならないかどうかわからないです(彼らは可能性があります一度dottyが入ってくると、後で削除されることもあります。versions)私は、私はむしろOuter[T]

+0

私は同意しません。 'case HasInts'節が成功すると、コンパイラはその場合outer.type <:HasInts.typeを知り、HasInts.Innerはfinalと= Intであるので、戻り値の型(その場合)はIntです。 fnの基本的な戻り値の型については、あなたが同じことをしたときに得られるものであればどんなものでも構いませんが、Inner型のメンバを汎用型にします(もし私が誤った言葉を間違えた場合は、アウター[インナー]。 – Golly

1

スカラ座を使用することができた場合は、単にouterマッチHasIntsouterの種類を絞り込むという事実を使用していません。ただ、はるかに簡単なケースのように:

val x: Object = ... 
x match { 
    case _: String => x.length 
} 

はコンパイルされません、代わりにあなたはcase s: String => s.lengthを記述する必要があります。

0outer: Outerの場合はouter.Innerと表示されている必要がありますが、もちろんそうではありません。

の回避策については

は、Jatinが言及したものに加えて、ブルートフォースもちろん

(outer match { 
    case HasInts => 0 
}).asInstanceOf[outer.Inner] 

があり、これはあなたのタイプをチェックする必要が代わりにコンパイラの正しい意味します!編集のために

:typeパラメータのケースのための特別なサポートがコンパイラ

In the method case, what you have here is a GADT: Patterns determine the type parameters of an corresponding methods in the scope of a pattern case.

でありしかし、そこタイプのメンバーには、このようなサポートはありません、と私はそれは次のようになり加えることを期待自明ではない。

+0

ヤック!この「回避策」は、どのような目的に役立つでしょうか? – Dima

+1

コンパイラがそれを証明できない場合でも、プログラムのアサートは有効です。この場合のように、ブランチの内側は 'outer == HasInts'を知るので、' 0'は 'outer.Inner'ですが、コンパイラはそれを認識しません。 –

+0

はい、それは私がその質問の意味です:コンパイラがそれを証明できない場合、 "プログラムを有効にする"という目的は何ですか?あるいは、別の言い方をすれば、プログラムが有効であることを証明するコンパイラに頼ることができない場合、スカラを使用する理由は何ですか? PythonやRubyで何かをまとめる方がずっと速くて簡単でしょうか? – Dima

0

関数の戻り値の型は引数に依存できないため、静的でなければなりません。これを考慮してください:

case object HasStrings extends Outer { 
    override type Inner = Int 
} 

val list = Seq(HasInts, HasStrings).map { obj => id(obj) } 

あなたがしたいことがうまくいくなら、上記の行は有効です。しかし、結果として生じるタイプの可能性があるのは何でしょうか?

メンバーを入力するのではなくジェネリックタイプを探していると思います。 (実際には、これはではなく、「実在のタイプ」です。これはまったく別の獣です)。

+0

"関数の戻り値の型は引数に依存できないため、静的でなければなりません。"はい、Scalaでは、パスに依存する型を検索できます。あなたが与えた例では、結果の型は 'Seq [x.Inner forSome {val x:Outer}]'です。 –

+0

@AlexeyRomanovは本当にありません。型は実際には 'Outer {type Inner>:Int}#Inner'の文字列ですが、構文上のニュアンスはその点を超えています。要点は、リストの要素を役に立つものにキャストしたり、単に「Any」として扱ったりすることなく、何もできないことです。それらは 'String'でも' Ints'でもなく、一般的なインターフェースを提示せず、共通のプロパティを提供しません。これは、パス依存型がまったく意図したものではありません。これはスカラー構文のperlコードです。 (また、パス依存型は、あなたが思っているようなものではありません)。 – Dima

+0

私はあなたが 'list'で何か役に立つことはできないと確信しています。これは、ラインが無効であるというクレームとは完全に異なります。 –

関連する問題