I have two questions regarding the '::' case class.
:: can be used as
case head :: tail => ...
How does it work? Meaning, what is exactly the flow that Scala uses to match a List instance with the :: case class? Given that I have a class MyClass, with operator op, can I create a case class named op that I can use as:
case foo op bar => ....
?
Scala case classes are just regular classes which are immutable by default and decomposable through pattern matching. It uses equal method to compare instance structurally. It does not use new keyword to instantiate object. All the parameters listed in the case class are public and immutable by default.
You can also serialize / deserialize Scala case classes with other formats, like the MessagePack binary format.
A class can extend another class, whereas a case class can not extend another case class (because it would not be possible to correctly implement their equality).
A Scala Case Class is like a regular class, except it is good for modeling immutable data. It also serves useful in pattern matching, such a class has a default apply() method which handles object construction. A scala case class also has all vals, which means they are immutable.
scala> abstract class Stack {
| def push(n :Int):Stack
| }
defined class Stack
scala> final case class push(st :Stack,hd :Int) extends Stack {
| override def push(n :Int):Stack = new push(this,n)
| }
defined class push
scala> object NullStack extends Stack {
| override def push(n :Int):Stack = new push(null,n)
| }
defined module NullStack
scala> val s = NullStack.push(1).push(2)
s: Stack = push(push(null,1),2)
scala> def test(s :Stack) = s match { case st push i => println(st +"push " + i) }
test: (Stack)Unit
scala> test(s)
push(null,1)push 2
It's detailed in page 301 of Programming in Scala, About pattern matching on List
s.
The "cons" pattern
x :: xs
is a special case of an infix operation pattern. You know already that, when seen as an expression, an infix operation is equivalent to a method call. For patterns, the rules are different: When seen as a pattern, an infix operation such asp op q
is equivalent toop(p, q)
. That is, the infix operatorop
is treated as a constructor pattern. In particular, a cons pattern such asx :: xs
is treated as::(x, xs)
. This hints that there should be a class named::
that correspond to the pattern constructor. Indeed there is such a class. It is namedscala.::
and is exactly the class that builds non-empty lists.
Actually, the fact that :: is a case class is only half of the answer. The reason this works in pattern matching is that there is an extractor for object ::, which is generated automatically when a case class is defined. Conveniently, ::.unapply returns a List, because :: extends List. If you want to use the same trick for Lists, though, you won't be able to extend List, because it's final. What you can do is define an object with the appropriate unapply method, which has the expected return signature. For instance, to match the last element of a list, you can do:
object ::> {def unapply[A] (l: List[A]) = Some( (l.init, l.last) )}
List(1, 2, 3) match {
case _ ::> last => println(last)
}
(1 to 9).toList match {
case List(1, 2, 3, 4, 5, 6, 7, 8) ::> 9 => "woah!"
}
(1 to 9).toList match {
case List(1, 2, 3, 4, 5, 6, 7) ::> 8 ::> 9 => "w00t!"
}
The extractor must return an Option, which contains a tuple of the two deconstructed elements.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With