2017-01-26 4 views
1

Это this question.Согласование образцов с ADT и элементами типа

У меня есть ADT моделируется следующим образом:

sealed trait Foo { 
    type A 
    def method: A 
} 

case class Bar(p: T, q: S) extends Foo { 
    type A = Array[String] 
    def method: A = // implementation 
} 

case class Baz(p: T, q: S) extends Foo { 
    type A = Map[Array[String], Array[String]] 
    def method: A = // implementation 
} 

У меня есть еще один класс, который принимает подтип Foo в качестве одного из параметров конструктора, и делает конкретные вещи, основанные на будь то Bar или Baz.

В принятом ответе на вопрос связан, он предложил мне пользователем @marios, чтобы реализовать эту идею так:

class Qux[X <: Foo](foo: X) { 
    val m: X#A = foo.method 

    def process = m match { 
    case a: Bar#A => // do stuff with Bar 
    case b: Baz#A => // do other stuff with Baz 
    } 

Это работало хорошо, но привело к предупреждениям о непроверенных типов в зависимости от типа стирание (поскольку я согласен на Map, в случае Baz). Однако в моем конкретном случае их можно было безоговорочно игнорировать. Но я подумал, если я мог бы избежать этого, и попытался написать что-то вроде этого:

class Qux[X <: Foo](foo: X) { 
    def process(param: U) = { 
    val m: X#A = foo.method 
    foo match { 
     case Bar(_, _) => BarProcessor(m).doStuff(param) // where BarProcessor expects m to be an Array[String] 
     case Baz(_, _) => BazProcessor(m.keys).doStuff(param, m.values) // do related stuff using m, but where m is instead now a Map[Array[String], Array[String]] 
    } 
    } 
} 

Однако, в этом случае я получаю ошибки как

[error] Qux.scala:4: type mismatch; 
[error] found : X#A 
[error] required: Array[String] 
[error] case Bar(_, _) => BarProcessor(m).doStuff(rows) 
[error]        ^

Мой вопрос, почему? Кажется, что эти два фрагмента кода действительно не отличаются друг от друга, поэтому почему доступ к члену типа работает в первом случае, но не во втором?

ответ

2

Компилятор даже не более точного типа из foo в зависимости от ветви (т.е. внутри ветви, тип foo еще X, не Bar или Baz), намного меньше m. То, что вы можете написать вместо этого

def process(param: U) = { 
    foo match { 
     case foo: Bar => 
     val m = foo.method // m is Bar#A, i.e. Array[String] 
     BarProcessor(m).doStuff(param) 
     case foo: Baz => 
     val m = foo.method // m is Baz#A, i.e. Map[Array[String], Array[String]] 
     BazProcessor(m.keys).doStuff(param, m.values) 
    } 
    } 

Обратите внимание, что foo в шаблоне фактически новая переменная, которая затеняет внешний foo, так что это эквивалентно

foo match { 
     case bar: Bar => 
     val m = bar.method // m is Bar#A, i.e. Array[String] 
     // note that if you use foo here, it still has type Foo 
     BarProcessor(m).doStuff(param) 
     ...