How to understand "pattern match with Singleton object" in scala? - scala

The context of my question is similar to some others asked in the forum, but I cannot find an exact match and it still remains a mystery to me after viewing those answers. So I appreciate it if someone can help. The context of my question is to match a singleton class object using a pattern match.
For example, if I am implementing a list structure of my own, like this
// An implementation of list
trait AList[+T] // covariant
case class Cons[+T](val head: T, val tail: AList[T]) extends AList[T]
case object Empty extends AList[Nothing] // singleton object
// an instance of implemented list
val xs = Cons(1, Cons(2, Cons(3, Empty)))
// pattern matching in a method - IT WORKS!
def foo[T](xs: AList[T]) = xs match {
case Empty => "empty"
case Cons(x, xss) => s"[$x...]"
}
println(foo(xs)) // => [1...]
// pattern matching outside - IT RAISES ERROR:
// pattern type is incompatible with expected type;
// found : Empty.type
// required: Cons[Nothing]
val r: String = xs match {
case Empty => "EMPTY"
case Cons(x, xss) => s"[$x...]"
}
println(r) // does NOT compile
To me they look like the same "matching" on the same "objects", how come one worked and the other failed? I guess the error had something to do with the different of matching expr in and out of methods, but the message given by the compiler was quite misleading. Does it mean we need to explicitly cast xs like xs.asInstanceOf[AList[Int]] when "matching" outside?

Compiler tells you that type of xs is Cons and it can't be Empty, so your first case is pointless.
Try this:
val r: String = (xs: AList[Int]) match {
case Empty => "EMPTY"
case Cons(x, xss) => s"[$x...]"
}
Or this:
val ys: AList[Int] = xs
val r: String = ys match {
case Empty => "EMPTY"
case Cons(x, xss) => s"[$x...]"
}
In this case compiler don't knows that case Empty is pointless.
It's exactly what you are doing with def foo[T](xs: AList[T]) = .... You'd get the same compilation error with def foo[T](xs: Cons[T]) = ....
In this particular example valid and exhaustive match looks like this:
val r: String = xs match {
// case Empty => "EMPTY" // would never happened.
case Cons(x, xss) => s"[$x...]"
}
Addition: you should make your AList trait sealed:
sealed trait AList[+T]
It allows compiler to warn you on not exhaustive matches:
val r: String = (xs: AList[Int]) match {
case Cons(x, xss) => s"[$x...]"
}
<console>:25: warning: match may not be exhaustive.
It would fail on the following input: Empty
val r: String = (xs: AList[Int]) match {
^

The parameter of foo is a AList[T], so in the first case the matching is being done on a AList[T]. In the second case the matching is being done on a Cons[+T].
Basically matching is done on a object type, not on a object.

Related

Scala List Case Pattern match Implementation Question:

I am trying to understand the scala implementation of List pattern match and
have question on how the first case statement(:: which splits the list by 1st, 2nd and rest) is implemented under the hood in List.scala
list match{
case h1 :: h2 :: rest => println(h2)
case _ =>
}
I was trying to understand by comparing this against Option Monad and below is my understanding on how Option case pattern match works:
When i wrap an object inside Option below is called from Option.scala and it gives me either Some or None
def apply[A](x: A): Option[A] = if (x == null) None else Some(x)
then When i pattern match on Option, one of the below is matched and I can call methods of the same
final case class Some[+A](x: A) extends Option[A] {
def isEmpty = false
def get = x
}
case object None extends Option[Nothing] {
def isEmpty = true
def get = throw new NoSuchElementException("None.get")
}
When i look for similar implementation in List.scala, i could find case implementation for Nil(case object Nil extends List[Nothing]) which will be called for case Nil pattern match
but i am trying to understand how the statement case h1 :: h2 :: rest is matched with List.scala case implementation
Thanks for taking a look and please add comment if my question is missing requirded information to look into it.

How to explain these pattern matching examples?

I wrote some events in FSM, and discovered something I can not explain when pattern matching. I thought the following was completely legal, that is that I can send this actor either a message which is a vector[A] or vector[B].
when(State) {
case Event(content: Vector[A], _) => {
println("matched A")
stay
}
case Event(content: Vector[B], _) => {
println("matched B")
stay
}
}
However,
if I send the actor a vector[B] message it leads to
java.lang.ClassCastException: B cannot be cast to A
So basically it tries to match the first event eventhough the next would match.
I tried to make an even simpler pattern match example;
object Pattern extends App {
val n = Vector(1,2,3)
val s = Vector("S", "S", "S")
n match{
case e:Vector[String] => println("matched string")
case v:Vector[Int] => println("matched int")
}
}
This is actually not legal;
Error:(8, 12) pattern type is incompatible with expected type;
found : Vector[String]
required: scala.collection.immutable.Vector[Int]
case e:Vector[String] => println("matched string")
However, I am allowed to run my code if I do the following cast;
object Pattern extends App {
val n = Vector(1,2,3).asInstanceOf[Vector[Any]]
val s = Vector("S", "S", "S")
n match{
case e:Vector[String] => println(n(0).getClass)
case v:Vector[Int] => println("matched int")
}
}
The thing I find strange then is that I apparently say that Any could match a String, but the print is java.lang.Integer. So should I think of it as I have an vector[Int] that I say is a Vector[Any], since Vector[Any] could be a Vector[String] it matches that pattern, and again since it really is a vector[Int] I mask as Vector[Any] the print is fine too.
Could someone explain these pattern matching observations?
and how should I set up the messages so my state can handle both messages of Vector[A] and Vector[B]?
Due to type erasure of jvm type information is lost at runtime this kind of pattern matching (pattern matching with higher kinded types) is not supported directly.
Here are the ways to get around this problem
Instead I recommend you to wrap the vector in another container.
sealed trait Vectors
case class VectorString(vs: Vector[String]) extends Vectors
case class VectorInt(vi: Vector[Int]) extends Vectors
def doStuff(v: Vectors) = v match {
case VectorString(vs) => //be sure that vs is Vector[String]
case VectorInt(vi) =>
}
Ways to pattern match generic types in Scala
Using TypeTag
import scala.reflect.runtime.universe._
def handle[A: TypeTag](a: A): Unit =
typeOf[A] match {
case t if t =:= typeOf[List[String]] =>
// list is a string list
val r = a.asInstanceOf[List[String]].map(_.length).sum
println("strings: " + r)
case t if t =:= typeOf[List[Int]] =>
// list is an int list
val r = a.asInstanceOf[List[Int]].sum
println("ints: " + r)
case _ => // ignore rest
}
val ints: List[Int] = Nil
handle(List("hello", "world")) // output: "strings: 10"
handle(List(1, 2, 3)) // output: "ints: 6"
handle(ints) // output: "ints: 0" it works!

How to reproduce case class behaviour with apply/unapply methods?

I tried to replace case class with mundane class and companion object and suddenly get type error.
Code that compiles fine (synthetic example):
trait Elem[A,B] {
def ::[C](other : Elem[C,A]) : Elem[C,B] = other match {
case Chain(head, tail) => Chain(head, tail :: this)
case simple => Chain(simple, this)
}
}
class Simple[A,B] extends Elem[A,B]
final case class Chain[A,B,C](head : Elem[A,B], tail : Elem[B,C]) extends Elem[A,C]
Change the last definition with:
final class Chain[A,B,C](val head : Elem[A,B], val tail : Elem[B,C]) extends Elem[A,C]
object Chain {
def unapply[A,B,C](src : Chain[A,B,C]) : Option[(Elem[A,B], Elem[B,C])] =
Some( (src.head, src.tail) )
def apply[A,B,C](head : Elem[A,B], tail : Elem[B,C]) : Chain[A,B,C] =
new Chain(head, tail)
}
But that seemingly equivalent code make compiler emit errors:
CaseMystery.scala:17: error: type mismatch;
found : test.casemystery.Fail.Elem[A,B] where type B, type A >: C <: C
required: test.casemystery.Fail.Elem[A,Any] where type A >: C <: C
Note: B <: Any, but trait Elem is invariant in type B.
You may wish to define B as +B instead. (SLS 4.5)
case Chain(head, tail) => Chain(head, tail :: this)
^
CaseMystery.scala:17: error: type mismatch;
found : test.casemystery.Fail.Elem[B(in method ::),B(in trait Elem)] where type B(in method ::)
required: test.casemystery.Fail.Elem[Any,B(in trait Elem)]
Note: B <: Any, but trait Elem is invariant in type A.
You may wish to define A as +A instead. (SLS 4.5)
case Chain(head, tail) => Chain(head, tail :: this)
^
two errors found
What is the difference between implicitly created method with the case statement and explicitly written methods for mundane class?
This answer ended up being longer than I expected. If you just want the guts of what is happening with type inference, skip to the end. Otherwise, you get led through the steps of getting to the answer.
The problem is in the case, but not the one in case class
In this case, as much as I hate to admit it, case classes really are magic. In particular, they get special treatment at the type checker level (I think we can agree that your code would work if it got past that phase - you might even be able to throw enough casts at it to make that work).
The problem is, surprisingly enough, not in the class Chain itself, but in the places it is used, specifically in the pattern matching part. For example, consider the case class
case class Clazz(field: Int)
Then, you expect the following to be equivalent:
Clazz(3) match { case Clazz(i) => i }
// vs
val v = Clazz.unapply(Clazz(3))
if (v.isDefined) v.get else throw new Exception("No match")
But, Scala wants to be more clever and optimize this. In particular, this unapply method pretty can pretty much never fail (let's ignore null for now) and is probably used a lot, so Scala wants to avoid it altogether and just extract the fields as it usually would get any member of an object. As my compiler professor is fond of saying, "compilers are the art of cheating without getting caught".
Yet here there is a difference in the type-checker. The problem is in
def ::[Z, X](other : Elem[Z, X]) : Elem[Z, Y] = other match {
case Chain(head, tail) => Chain(head, tail :: this)
case simple => Chain(simple, this)
}
If you compile with -Xprint:typer you'll see what the type checker sees. The case class version has
def ::[C](other: Elem[C,A]): Elem[C,B] = other match {
case (head: Elem[C,Any], tail: Elem[Any,A])Chain[C,Any,A]((head # _), (tail # _)) => Chain.apply[C, Any, B](head, {
<synthetic> <artifact> val x$1: Elem[Any,A] = tail;
this.::[Any](x$1)
})
case (simple # _) => Chain.apply[C, A, B](simple, this)
}
While the regular class has
def ::[C](other: Elem[C,A]): Elem[C,B] = other match {
case Chain.unapply[A, B, C](<unapply-selector>) <unapply> ((head # _), (tail # _)) => Chain.apply[A, Any, B](<head: error>, {
<synthetic> <artifact> val x$1: Elem[_, _ >: A <: A] = tail;
this.::[B](x$1)
})
case (simple # _) => Chain.apply[C, A, B](simple, this)
}
So the type checker actually gets a different (special) case construct.
So what does the match get translated to?
Just for fun, we can check what happens at the next phase -Xprint:patmat which expands out patterns (although here the fact that these are no longer really valid Scala programs really becomes painful). First, the case class has
def ::[C](other: Elem[C,A]): Elem[C,B] = {
case <synthetic> val x1: Elem[C,A] = other;
case5(){
if (x1.isInstanceOf[Chain[C,Any,A]])
{
<synthetic> val x2: Chain[C,Any,A] = (x1.asInstanceOf[Chain[C,Any,A]]: Chain[C,Any,A]);
{
val head: Elem[C,Any] = x2.head;
val tail: Elem[Any,A] = x2.tail;
matchEnd4(Chain.apply[C, Any, B](head, {
<synthetic> <artifact> val x$1: Elem[Any,A] = tail;
this.::[Any](x$1)
}))
}
}
else
case6()
};
case6(){
matchEnd4(Chain.apply[C, A, B](x1, this))
};
matchEnd4(x: Elem[C,B]){
x
}
}
Although a lot of stuff is confusing here, notice that we never use the unapply method! For the non-case class version, I'll use the working code from user1303559:
def ::[Z, XX >: X](other: Elem[Z,XX]): Elem[Z,Y] = {
case <synthetic> val x1: Elem[Z,XX] = other;
case6(){
if (x1.isInstanceOf[Chain[A,B,C]])
{
<synthetic> val x2: Chain[A,B,C] = (x1.asInstanceOf[Chain[A,B,C]]: Chain[A,B,C]);
{
<synthetic> val o8: Option[(Elem[A,B], Elem[B,C])] = Chain.unapply[A, B, C](x2);
if (o8.isEmpty.unary_!)
{
val head: Elem[Z,Any] = o8.get._1;
val tail: Elem[Any,XX] = o8.get._2;
matchEnd5(Chain.apply[Z, Any, Y](head, {
<synthetic> <artifact> val x$1: Elem[Any,XX] = tail;
this.::[Any, XX](x$1)
}))
}
else
case7()
}
}
else
case7()
};
case7(){
matchEnd5(Chain.apply[Z, XX, Y](x1, this))
};
matchEnd5(x: Elem[Z,Y]){
x
}
}
And here, sure enough, the unapply method makes an appearance.
It isn't actually cheating (for the Pros)
Of course, Scala doesn't actually cheat - this behavior is all in the specification. In particular, we see that constructor patterns from which case classes benefit are kind of special, since, amongst other things, they are irrefutable (related to what I was saying above about Scala not wanting to use the unapply method since it "knows" it is just extracting the fields).
The part that really interests us though is 8.3.2 Type parameter inference for constructor patterns. The difference between the regular class and the case class is that Chain pattern is a "constructor pattern" when Chain is a case class, and just a regular pattern otherwise. The constructor pattern
other match {
case Chain(head, tail) => Chain(head, tail :: this)
case simple => Chain(simple, this)
}
ends up getting typed as though it were
other match {
case _: Chain[a1,a2,a3] => ...
}
Then, based on the fact that other: Elem[C,A] from the argument types and the fact that Chain[a1,a2,a3] extends Elem[a1,a3], we get that a1 is C, a3 is A and a2 can by anything, so is Any. Hence why the types in the output of -Xprint:typer for the case class has an Chain[C,Any,A] in it. This does type check.
However, constructor patterns are specific to case classes, so no - there is no way to imitate the case class behavior here.
A constructor pattern is of the form c(p1,…,pn) where n≥0. It
consists of a stable identifier c, followed by element patterns
p1,…,pn. The constructor c is a simple or qualified name which
denotes a case class.
Firstly other is Elem[C, A], but after you had tried to match it as Chain(head, tail) it actually matched to Chain[C, some inner B, A](head: Elem[C, inner B], tail: Elem[inner B, A]). After that you create Chain[C, inner B <: Any, A](head: Elem[C, inner B], (tail :: this): Elem[inner B, B])
But result type must be Elem[C, B], or Chain[C, Any, B]. So compiler trying to cast inner B to Any. But beacause inner B is invariant - you must have exactly Any.
This is actually better rewrite as follows:
trait Elem[X, Y] {
def ::[Z, X](other : Elem[Z, X]) : Elem[Z, Y] = other match {
case Chain(head, tail) => Chain(head, tail :: this)
case simple => Chain(simple, this)
}
}
final class Chain[A, B, C](val head : Elem[A, B], val tail : Elem[B, C]) extends Elem[A, C]
object Chain {
def unapply[A,B,C](src : Chain[A,B,C]) : Option[(Elem[A,B], Elem[B,C])] =
Some( (src.head, src.tail) )
def apply[A,B,C](head : Elem[A,B], tail : Elem[B,C]) : Chain[A,B,C] =
new Chain(head, tail)
}
After this error message becoming much more informative and it is obviously how to repair this.
However I don't know why that works for case classes. Sorry.
Working example is:
trait Elem[+X, +Y] {
def ::[Z, XX >: X](other : Elem[Z, XX]) : Elem[Z, Y] = other match {
case Chain(head, tail) => Chain(head, tail :: this)
case simple => Chain(simple, this)
}
}
final class Chain[A, B, C](val head : Elem[A, B], val tail : Elem[B, C]) extends Elem[A, C]
object Chain {
def unapply[A,B,C](src : Chain[A,B,C]) : Option[(Elem[A,B], Elem[B,C])] =
Some( (src.head, src.tail) )
def apply[A,B,C](head : Elem[A,B], tail : Elem[B,C]) : Chain[A,B,C] =
new Chain(head, tail)
}
EDITED:
Eventually I found that:
case class A[T](a: T)
List(A(1), A("a")).collect { case A(x) => A(x) }
// res0: List[A[_ >: String with Int]] = List(A(1), A(a))
class B[T](val b: T)
object B {
def unapply[T](b: B[T]): Option[T] = Option(b.b)
}
List(new B(1), new B("b")).collect { case B(x) => new B(x) }
// res1: List[B[Any]] = List(B#1ee4afee, B#22eaba0c)
Obvious that it is compiler feature. So I think no way there to reproduce the full case class behavior.

pattern matching with case match in scala

I have a match statement like this:
val x = y match {
case array: Array[Float] => call z
case array: Array[Double] => call z
case array: Array[BigDecimal] => call z
case array: Array[_] => show error
}
How do I simplify this to use only two case statements, since first three case statements do same thing, instead of four.
Type erasure does not really gives you opportunity to understand how array was typed. What you should do instead is to extract head ( first element) of array and check it's type. For example following code works for me:
List(1,2,3) match {
case (a:Int) :: tail => println("yep")
}
This work, although not very nice:
def x(y: Array[_]) = y match {
case a if a.isInstanceOf[Array[Double]] ||
a.isInstanceOf[Array[Float]] ||
a.isInstanceOf[Array[BigDecimal]] => "call z"
case _ => "show error"
}
Would have thought that pattern matching with "|" as below would do the trick. However, this gives pattern type is incompatible with expected type on Array[Float] and Array[BigDecimal]. It might be that matching of generic on this single case where it could work has not been given so much attention:
def x(y: Array[_ <: Any]) = y match {
case a # (_:Array[Double] | _:Array[Float] | _:Array[BigDecimal]) => "call z"
case a: Array[_] => "show error"
}
May be it helps a bit:
import reflect.runtime.universe._
object Tester {
def test[T: TypeTag](y: Array[T]) = y match {
case c: Array[_] if typeOf[T] <:< typeOf[AnyVal] => "hi"
case c: Array[_] => "oh"
}
}
scala> Tester.test(Array(1,2,3))
res0: String = hi
scala> Tester.test(Array(1.0,2.0,3.0))
res1: String = hi
scala> Tester.test(Array("a", "b", "c"))
res2: String = oh
You can obtain the class of array elements as follows (it will be null for non-array types): c.getClass.getComponentType. So you can write:
if (Set(classOf[Float], classOf[Double], classOf[BigDecimal]).contains(c.getClass.getComponentType)) {
// call z
} else {
// show error
}
Not particularly Scala'ish, though; I think #thoredge's answer is the best for that.
You could also check whether the Array is empty first and then if not, just pattern match on Array.head...something like:
def x(y: Array[_]) = {
y.isEmpty match {
case true => "error"
case false => y.head match {
case a:Double | a:BigInt => do whatever
case _ => "error"
}
}
}

How to make Scala's type system catch this MatchError?

I've defined an ordering for Seq[Seq[T]] such that it's a normal lexicographic ordering except all items (sub-sequences) are reversed first (so that C,B,A comes before A,B,C but after A,B,A):
implicit def ReverseListOrdering[T: Ordering] = new Ordering[Seq[T]] {
override def compare(xs1: Seq[T], xs2: Seq[T]) =
doCompare(xs1.reverse, xs2.reverse)
private def doCompare(xs1: Seq[T], xs2: Seq[T]): Int = (xs1, xs2) match {
case (Nil, Nil) => 0
case (x :: _, Nil) => 1
case (Nil, x :: _) => -1
case (x :: xs, y :: ys) =>
val a = implicitly[Ordering[T]].compare(x, y)
if (a != 0) a else doCompare(xs, ys)
}
}
This used to be defined on List[List[T]] at first but I later realized I want it for all Seq[Seq[T]]; this is why I initially left in the Nils in the pattern matching block, while failing to realize Nil never matches e.g. an empty Array.
Later I tried to run this block of code:
// the Seq[String] declarations are needed; otherwise sample` will be Array[Object] for some reason
val sample = List(
List("Estonia"): Seq[String],
Array("Tallinn", "Estonia"): Seq[String],
List("Tallinn", "Harju", "Estonia"): Seq[String])
println(sample.sorted)
This compiles just fine but results in the following error at runtime:
scala.MatchError: (WrappedArray(Estonia, Tallinn),List(Estonia)) (of class scala.Tuple2)
— while I understand the cause of the error perfectly well, what I fail to understand (or at least accept) is, if the Ordering is successfully defined on all Seq[Seq[T]], yet a superficially valid but obviously incompatible (in terms of how doCompare is defined) Seq[Seq[T]] (i.e. Array[List[String] | Array[String]]) attempt to use that ordering results in no static type errors or even warnings whatsoever.
Is this caused by the fact that the pattern matching code is not statically verified to cover all possible "instances" of Seq[Seq[T]] and that it only handles the List case? If yes, what are the currently available workarounds to achieving type safety in such cases? Is Scalaz something to be looked at yet again for a decent solution?
P.S. I'm aware I could easily do away with a solution that works for all Seq[Seq[T]] by not using pattern matching and resorting to head and tail with an if-else block (or case statements if guards, which is only superficially nicer), but I'm keen to learn how to make the most of out Scala's type capabilities (AFAIK F# and Haskell catch these errors for breakfast); not to mention pattern matching is by far more elegant and readable.
This might be closer:
scala> def cmp[A, B[_] <: Seq[_]](xs: B[A], ys: B[A]) = (xs, ys) match { case (Nil, Nil) => true }
<console>:7: error: pattern type is incompatible with expected type;
found : scala.collection.immutable.Nil.type
required: B[?A1] where type ?A1 (this is a GADT skolem)
def cmp[A, B[_] <: Seq[_]](xs: B[A], ys: B[A]) = (xs, ys) match { case (Nil, Nil) => true }
^
There is a known syndrome when the type parameter is on the class instead of the method.
It has surfaced on the ML recently. Here.
Comparing to:
scala> (null: Seq[_]) match { case _: Nil.type => true }
scala.MatchError: null
... 33 elided
scala> (null: List[_]) match { case _: Nil.type => true }
<console>:8: warning: match may not be exhaustive.
It would fail on the following input: List(_)
(null: List[_]) match { case _: Nil.type => true }
^
scala.MatchError: null
... 33 elided
scala> (null: List[_]) match { case Nil => true }
<console>:8: warning: match may not be exhaustive.
It would fail on the following input: List(_)
(null: List[_]) match { case Nil => true }
^
scala.MatchError: null
... 33 elided
Sorry to be lazy, but off to bed.
Scala compiler produces non-exhaustive match warnings only for sealed types, which Seq isn't. AFAIK, there is no way to force it to check, like there is for tail recursion.
(AFAIK F# and Haskell catch these errors for breakfast)
Haskell doesn't have an equivalent to Seq; the code you used to have with List is the one which is equivalent to Haskell (modulo laziness), and Scala does catch the error in this case. I don't know F# well, but looking at http://msdn.microsoft.com/en-us/library/dd547125.aspx it seems that pattern matching a general IEnumerable<T> isn't supported.
Use code:
implicit def ReverseListOrdering[T: Ordering] = new Ordering[Seq[T]] {
override def compare(xs1: Seq[T], xs2: Seq[T]) =
doCompare(xs1.reverse, xs2.reverse)
private def doCompare(xs1: Seq[T], xs2: Seq[T]): Int = (xs1, xs2) match {
case (Seq(), Seq()) => 0
case (x +: _, Seq()) => 1
case (Seq(), x +: _) => -1
case (x +: xs, y +: ys) =>
val a = implicitly[Ordering[T]].compare(x, y)
if (a != 0) a else doCompare(xs, ys)
}
}
As I mention is comment Nil is not the same type as Seq(). For example WrappedArray is not a List. And when you use x :: xs - it is matched as List. Array("Tallinn", "Estonia") is converted to WrappedArray. Always use +: in pattern matching when you use Seq