2015-03-04 2 views
4

я сузили его до следующего куска кода:Это ошибка проверки типа?

trait A[T] { 
    def apply(t: T): Int 
} 

sealed trait P { 
    def apply(): Int 
} 

case class I[T](a: A[T], t: T) extends P { 
    def apply: Int = a(t) 
} 

case class X[T1, T2](a1: A[T1], a2: A[T2]) extends A[(T1, T2)] { 
    def apply(t: (T1, T2)): Int = 
    t match { 
     case (t1, t2) => a1(t1) + a2(t2) 
    } 
} 

object m { 
    def apply(p1: P, p2: P): P = 
    (p1, p2) match { 
     case (I(a1, t1), I(a2, t2)) => 
     I(X(a1, a2), (t2, t1)) // <-- Here 
    } 
} 

Как вы можете видеть, у меня есть ошибка типа в строке, помеченной <-- Here. И все же код компилируется без предупреждения, и с ошибкой ClassCastException во время выполнения. Код, чтобы играть с:

case class E() extends A[Int] { 
    def apply(t: Int): Int = t 
} 

case class S() extends A[String] { 
    def apply(t: String): Int = t.length 
} 

object Test { 
    def apply() = { 
    val pe: P = I(E(), 3) 
    val ps: P = I(S(), "abcd") 
    val pp: P = m(pe, ps) 
    pp() 
    } 
} 

Я знаю, что когда шаблон сопоставление иногда не Scala может проверить, что значение имеет правильный типа, но это обычно приводит к предупреждению компилятора.

Итак, это ошибка, или я что-то пропущу?

Обновление: Что меня беспокоит, так это то, что я могу сделать ошибку типа, и компилятор даже не предупредит меня. Я понимаю, что (t1, t2) - правильный порядок; но если я напишу его неправильно, я не обнаружу его до выполнения программы и, возможно, даже позже, хотя это явно ошибка типа.

+0

Мое предположение: ваш матч шаблон в строке перед строкой «ошибка» имеет правопреемников следующие типы: 'a1' и' a2' оба являются 'A [Any]' и 't1' и' t2 'оба являются' Any'. Вероятно, вы должны явно указать ожидаемые типы. – Conrad

+0

@Conrad, но см. Мой пример. Я добавил аналогичный код к этому билету, поэтому, надеюсь, они повысят предупреждение. Лично я считаю, что семантика шаблона соответствует тонкой, поэтому я думаю, что он должен предупреждать, когда код выражает недоказанное совпадение. Я знаю, что люди говорят, что совпадение шаблонов означает «больше безопасности для вас!» –

ответ

4

Возможно отсутствие предупреждения связано с этой:

https://issues.scala-lang.org/browse/SI-9188

Это не похоже, чтобы сделать что-нибудь полезное с параметром типа на А, если он не может доказать статически, что вы получили это неправильно ,

Последний матч здесь должен предупредить:

scala> val i = I(E(), 42) 
i: I[Int] = I(E(),42) 

scala> i match { case I(a: A[Int], x) => } 

scala> i match { case I(a: A[String], x) => } 
<console>:15: warning: non-variable type argument String in type pattern A[String] is unchecked since it is eliminated by erasure 
       i match { case I(a: A[String], x) => } 
           ^
<console>:15: error: pattern type is incompatible with expected type; 
found : A[String] 
required: A[Int] 
       i match { case I(a: A[String], x) => } 
           ^

scala> (i: P) match { case I(a: A[String], x) => } 
<console>:15: warning: non-variable type argument String in type pattern A[String] is unchecked since it is eliminated by erasure 
       (i: P) match { case I(a: A[String], x) => } 
            ^
<console>:15: error: pattern type is incompatible with expected type; 
found : A[String] 
required: A[Any] 
Note: String <: Any, but trait A is invariant in type T. 
You may wish to define T as +T instead. (SLS 4.5) 
       (i: P) match { case I(a: A[String], x) => } 
            ^

scala> (i: P) match { case I(a: A[Int], x) => } 
<console>:15: warning: non-variable type argument Int in type pattern A[Int] is unchecked since it is eliminated by erasure 
       (i: P) match { case I(a: A[Int], x) => } 
            ^
<console>:15: error: pattern type is incompatible with expected type; 
found : A[Int] 
required: A[Any] 
Note: Int <: Any, but trait A is invariant in type T. 
You may wish to define T as +T instead. (SLS 4.5) 
       (i: P) match { case I(a: A[Int], x) => } 
            ^

scala> (i: P) match { case I(a: A[_], x) => } 

scala> (i: P) match { case I(a: A[Any], x) => } 

Просто добавить:

scala> (i: P) match { case I(a: A[Any], x) => a("foo") } 
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer 
    at scala.runtime.BoxesRunTime.unboxToInt(BoxesRunTime.java:105) 
    at E.apply(<console>:33) 
    ... 33 elided 
+0

Да, это может быть очень хорошо. Благодаря! – MigMit

1

Короткий ответ: тип-стирание.

Когда вы на соответствие (p1, p2), все, что вы знаете о любом типе, что они каждый P, которые могли бы быть I[T], но не обязательно с тем же T. Если вы более подробно указали на использование I[T], вы можете получить предупреждение о стирании стилей, но поскольку вы столкнулись с типом до P, я предполагаю, что компилятор даже не потрудился проверить предупреждение. Ошибка? Может быть. Я бы назвал это скорее недостатком. В любом случае, поскольку информация типа T будет стерта, компилятор разрешит это.

Если становится немного более очевидной, если мы изменим типы параметров из m.apply в I[T], где T одинакова для обоих p1 и p2.

object m { 
    def apply[T](p1: I[T], p2: I[T]): P = 
    (p1, p2) match { 
     case (I(a1, t1), I(a2, t2)) => 
     I(X(a1, a2), (t2, t1)) 
    } 
} 

val pe = I(E(), 3) 
val ps = I(S(), "abcd") 

m(pe, pe).apply // same underlying type, works 
m(ps, ps).apply // same underlying type, works 
m(pe, ps).apply // doesn't compile 

Независимо от того, чего вы хотите в конце концов, я не знаю.

+0

Нет, я действительно хочу, чтобы эти 'p1' и' p2' были приложениями типов 'I' to * different *.Итак, да, я могу написать его как 'apply [T1, T2] (p1: I [T1], p2: I [T2])' , но я действительно хочу, чтобы это была функция на 'P's , – MigMit

+0

Но тогда вы пытаетесь смешать их там, где они несовместимы? –

+0

Я отредактирую сообщение. – MigMit

Смежные вопросы