On 07/07/17 17:04, Dmitry Petrashko wrote:
On 7 Jul 2017, at 17:17, Maurizio Cimadamore wrote:
I understand. In our mind, 'var' is simply a way to tell the
compiler "type missing here, please fill it out" - and we'd like
to keep the type inference as orthogonal as possible from other
related semantics.
This is also how it behaves in scala if you don’t ascribe the type in
pattern. The type will be filled in by typer. Based on
http://cr.openjdk.java.net/~briangoetz/amber/pattern-match.html
<http://cr.openjdk.java.net/%7Ebriangoetz/amber/pattern-match.html>
|exprswitch(b) { case Box(var n) -> n; }; |
is equivalent to this in Scala:
|x match { case Box(n) => n; }; |
Or am I misinterpreting?
They are the same yes. What I was trying to say, is that when there's no
manifest type you seem to let 'null' in (because of the lack of
instanceof under the hood), so two similarly looking patterns have
different behavior w.r.t. nulls, which I'm not wild about (but I have
never noticed it either having used Scala :-))
In Scala, the runtime uses |instanceof| test (Option 1 in your
writeup),
while the exhaustivity checkers uses type system and assumes
absence of null (Option 2).
For generics, we issue a warning that type parameters are
unchecked.
Have you considered this option?
So, you are saying that Scala does option 1 - but it tones it a
bit down by emitting a warning (rather than an harsh error) when
generic types are found.
This seems a sensible option - the only thing I don't understand
from your description - what does Scala do for nested patterns? Is
it another instanceof? If that's the case, does it means that I
cannot match against a List whose head is null with a normal
nested type test pattern?
Scala behaves the same way for top level and nested patters:
Yep - that's what I feared :-)
I think that, basically, the differences we're seeing here is that my
claim that 'case var t' is just a shorthand for 'case T t' is not as
obvious as I thought it would have been.
In Scala var patterns and test patterns are two different things, and
treated in quite different ways (which means different null behavior).
As I said in my earlier email, I have a feeling that keeping 'var'-ness
out of the equation might result in better compositionality (after all,
patterns are not the only place where we intend to use 'var').
Maurizio
Lets say that we have |case class Cons(hd: Object, tl: Cons)|
|x match { case Cons(a, b) => 1 // will match only if `x instanceof
Cons`. will match even if a is null and b is null case Cons(a, b:
Cons) => 2 // will not match if b is null, but a may be null case a:
Cons => 3 // will not match null, is equivalent to the first option
case a => 4 // always matches } |
now, details about type parameters and type inference:
|case class Cons[T](hd: T, tl: Cons) x match { case Cons(a, b) => 1 //
a will be inferred to be Any, our top type case Cons[Int](a, b) => 2
// warning will be emitted that Cons[Int] is unchecked. } |
In practice, people rarely see this warning due to important
observation that compiler uses:
|case class Cons[T](hd: T, tl: Cons) extends Seq[T] val x: Seq[Int] =
??? x match { case Cons[Int](a, b) => 2 // a warning will not be
emitted, as we know that T is the same as in Seq and we “trust” x. } |
Best,
Dmitry