class C[T]
case class CC[T](value: C[T])
class Test[T] {
def method(arg: Any) {
arg match {
case CC(x: C[T]) => println("ok")
}
}
}
error: pattern type is incompatible with expected type;
found : test.C[T]
required: test.C[Any]
Note: T <: Any, but class C is invariant in type T.
You may wish to define T as +T instead. (SLS 4.5)
case CC(x: C[T]) => println("ok")
Why? Is it a bug or my misunderstanding how pattern matching works?
I know I should get an unchecked warning, because due to erasure it
cannot be verified at runtime that CC is really of type CC[T], but why a
compile time error?
Regards,
Piotr
The type of a pattern must conform to the expected type. In this case
the explicit type C[T] does not conform to the type of the case class
parameter.
Write case CC(x: C[t]) and it will compile and infer what it can for the
type parameter. If you want a type it doesn't infer, then you can cast
it in the pattern body: the pattern matcher is not intended as a general
purpose casting mechanism.
> But this does not:
>
> trait Base[T]
> case object Derived extends Base[Int]
>
> class Test[T] {
> def method(arg: Base[T]) {
> arg match {
> case Derived => println("ok")
> }
> }
> }
The type of a pattern must conform to the expected type. In this case
the type of Derived does not conform to Base[T] (where T is some fixed
but unknown T.) You can always make such matches compile anyway:
(arg: Any) match { ....
because everything conforms to Any.
But it does - CC[T] is the only class here that can accept an argument
of type C[T]. So I would expect the type inferencer to infer CC[T],
basing on the type of the argument. Just as it happens in this case,
which compiles perfectly:
case class CC[T](value: T)
class Test[T] {
def method(arg: Any) {
arg match {
case CC(x: T) => println("ok")
}
}
}
>
> Write case CC(x: C[t]) and it will compile and infer what it can for the
> type parameter.
This is not a solution, because it will infer CC[Any], and x: C[Any],
and I don't want it to.
If you want a type it doesn't infer, then you can cast
> it in the pattern body:
Right, forcing a cast from C[Any] to C[T] on the right side works, but I
feel it is then a slight deficiency of the current pattern matching
implementation. BTW: you can write it even shorter: CC(x) and x will be
automatically of type C[Any].
> the pattern matcher is not intended as a general
> purpose casting mechanism.
Well, this is debatable. I thought you designed Scala to be as much
orthogonal as possible. If typecasting the arguments on the left side
works for any "simple" type like T, how is C[T] different, except making
it harder at the compiler implementation side?
Uh, oh, now I noticed it is already implemented and works in this case:
scala> class C[T]
defined class C
scala> case class CC[T](value: C[T])
defined class CC
scala> val a = new C[Int]
a: C[Int] = C@13e55db
scala> CC(a) // look ma, here it doesn't have a problem
res0: CC[Int] = CC(C@13e55db)
>
>> But this does not:
>>
>> trait Base[T]
>> case object Derived extends Base[Int]
>>
>> class Test[T] {
>> def method(arg: Base[T]) {
>> arg match {
>> case Derived => println("ok")
>> }
>> }
>> }
>
> The type of a pattern must conform to the expected type. In this case
> the type of Derived does not conform to Base[T] (where T is some fixed
> but unknown T.)
Define exactly what it means "conforms". I believe the main purpose of
pattern matching is to find a pattern the object being tested matches.
So by definition, there will be patterns that don't match. I understood
that the compiler has to assure that every pattern has some chances to
be matched, so matching against totally unrelated types is treated as
error. But in this case, for sure there exist some T (exactly = Int)
such that Derived is a subclass of Base[T]. Because the compiler doesn't
really know what the T is at the moment of compilation, it cannot prove
my code is wrong. Therefore it should compile. And if it should not
compile, the first snippet using "case x: Derived =>" shouldn't too.
Anyway, thanks for the (x: Any) trick to make the compiler shut up. :
Regards,
Piotr
But it does - CC[T] is the only class here that can accept an argument
of type C[T]. So I would expect the type inferencer to infer CC[T],
basing on the type of the argument. Just as it happens in this case,
which compiles perfectly:
case class CC[T](value: T)
class Test[T] {
def method(arg: Any) {
arg match {
case CC(x: T) => println("ok")
}
}
}
>
> Write case CC(x: C[t]) and it will compile and infer what it can for the
> type parameter.
This is not a solution, because it will infer CC[Any], and x: C[Any],
and I don't want it to.
If you want a type it doesn't infer, then you can cast
> it in the pattern body:
Right, forcing a cast from C[Any] to C[T] on the right side works, but I
feel it is then a slight deficiency of the current pattern matching
implementation. BTW: you can write it even shorter: CC(x) and x will be
automatically of type C[Any].
> the pattern matcher is not intended as a general
> purpose casting mechanism.
Well, this is debatable. I thought you designed Scala to be as much
orthogonal as possible. If typecasting the arguments on the left side
works for any "simple" type like T, how is C[T] different, except making
it harder at the compiler implementation side?
Uh, oh, now I noticed it is already implemented and works in this case:
scala> class C[T]
defined class C
scala> case class CC[T](value: C[T])
defined class CC
scala> val a = new C[Int]
a: C[Int] = C@13e55db
scala> CC(a) // look ma, here it doesn't have a problem
res0: CC[Int] = CC(C@13e55db)
>
>> But this does not:
>>
>> trait Base[T]
>> case object Derived extends Base[Int]
>>
>> class Test[T] {
>> def method(arg: Base[T]) {
>> arg match {
>> case Derived => println("ok")
>> }
>> }
>> }
>
> The type of a pattern must conform to the expected type. In this case
> the type of Derived does not conform to Base[T] (where T is some fixed
> but unknown T.)
Define exactly what it means "conforms". I believe the main purpose of
But the following works:
scala> class Test {
| def method[T](arg: Base[T]) {
| arg match {
| case Derived => println("ok")
| }
| }
| }
defined class Test
Why is it that just moving [T] from the class to the method makes a difference?
--
Daniel C. Sobral
I travel to the future all the time.
I don't have time to attempt an explanation, but method and class type
parameters are treated completely differently during pattern matching.
See papers by emir and section 8.4 of the spec.
I don't have the least idea what this is supposed to demonstrate to ma.
It's not even a pattern match.
I'll have to ask you to reformulate your issues in terms of the
specification if you want to continue this, because regardless of what
you think it should do, what the spec says it should do is what is
relevant to the implementation.
> Because the compiler doesn't
> really know what the T is at the moment of compilation, it cannot prove
> my code is wrong. Therefore it should compile.
This is absolutely contrary to the design. It is not a goal of the
pattern matcher, or of type systems in general, to never issue errors on
correct programs. In fact it is provably impossible: all type systems
exclude some typesafe programs. The set of programs which pass a
typechecker are not "all those which cannot be proved wrong", they are
"all those which can be proved typesafe."
The question is how much help should the compiler provide. Take this:
5 match { case "abc" => true }
Should the compiler compile this, or should it complain that match is
impossible? This could be argued either way, but the benefit of static
typing is that the compiler can complain, so the bias here is to
complain whenever possible.
If you do this:
class Test[T] {
def method(arg: Base[T]) {
arg match {
case Derived => println("ok")
}
}
}
What you (and I, to be honest) thought happened was the compiler asked
if "Base[T]" can be "Derived". What actually happens is that the
compiler checks if "Derived" is a "Base[T]", and it is not. That is,
you cannot state that Derived <: Base[T] for all T.
You can make it work with:
class Test[T] {
def method(arg: Base[T]) {
(arg: Base[_]) match {
case Derived => println("ok")
}
}
}
which tells the compiler that "arg" might be any Base, really. The
curious thing is that the following works:
def method[T](arg: Base[T]) {
arg match {
case Derived => println("ok")
}
}
As it happens, this case is covered by this in the specification:
Let T be the type of the selector expression e and let a 1 , . . . , a
m be the type param-
eters of all methods enclosing the pattern matching expression. For
every a i , let L i
be its lower bound and Ui be its higher bound. Every pattern p ∈ {p 1
, , . . . , p n } can
be typed in two ways. First, it is attempted to type p with T as its
expected type. If
this fails, p is instead typed with a modified expected type T' which
results from T
by replacing every occurrence of a type parameter a i by undefined.
The spec goes on to provide further conditions, but, generally
speaking, But, roughly speaking, arg's type was Base[T], and T is a
type parameter of an enclosing method, so T' is Base[_], and Derived
can be typed as Base[_].
Best regards,
Piotr
W dniu 2011-05-20 23:11, Daniel Sobral pisze:
Best regards,
Piotr
W dniu 2011-05-20 23:11, Daniel Sobral pisze:
trait C[T]
case class CC1[T](value: T)
case class CC2[T](value: C[T])
class Test[T] {
def method(arg: Any) {
arg match {
case CC1(x: T) => // ok
case CC2(x: C[T]) => // error
}
}
}
<console>:15: error: pattern type is incompatible with expected type;
found : C[T]
required: C[Any]
Note: T <: Any, but trait C is invariant in type T.
You may wish to define T as +T instead. (SLS 4.5)
case CC2(x: C[T]) => // error
Questions:
1. Where it got that C[Any] from and why?
2. Why it doesn't complain on the first pattern in the same way - why
not an error "found T, required Any"?
Best regards,
Piotr
W dniu 2011-05-20 17:58, Paul Phillips pisze:
On 21/05/11 07:11, Daniel Sobral wrote:
> Should the compiler compile this, or should it complain that match is
> impossible? This could be argued either way, but the benefit of static
> typing is that the compiler can complain, so the bias here is to
> complain whenever possible.
The answer cannot be argued either way. The answer is equivalent to
the answer to this question: Is Scala a total language?
The answer to this question (and therefore both questions) is no. We
can also replace the word "should" with "can." It's not possible to
complain about exhaustive patterns in a non-total language such as Scala.
Try it. Upon failure, blame the universe.
- --
Tony Morris
http://tmorris.net/
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/
iEYEARECAAYFAk3XatcACgkQmnpgrYe6r61ZwQCgkAH9e1vufgOE4LJagKBa+WV6
1YEAn1CdLaihUmvIuyGKsz2EyIxo5YXk
=1k7I
-----END PGP SIGNATURE-----
> The answer cannot be argued either way. The answer is equivalent to
> the answer to this question: Is Scala a total language?
No, Tony, it plainly is not. The answer is equivalent to the answer to
*this* question: should the compiler complain about 5 == "abc"?
> The answer to this question (and therefore both questions) is no. We
> can also replace the word "should" with "can." It's not possible to
> complain about exhaustive patterns in a non-total language such as Scala.
First, no one said anything about exhaustive patterns. I know you want
to help, but discussing the taste of apples while we talk about the
smell of roses doesn't help anyone. Second, just because it is not
possible for the compiler to do so in *all* cases doesn't mean it is
not possible for the compiler to do so in some cases.
Can you reformulate these questions in terms of section 8.3 of the
specification? It's called "Type Parameter Inference in Patterns". The
questions I can attempt to answer (emphasis on attempt) are the ones
approximately of the form "the following excerpt from the specification
implies <something> so why does <something>..."