Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Case classes, pattern matching and curried constructors in Scala

They don't seem to mix that well:

abstract class A
case class B (var a: Int)(var b: String) extends A
case class C extends A

The following will not work:

B(1)("1") match {
  case B(a)(b) => print("B")
  case C() => print("C")
}

The problem is that pattern matching and curried arguments do not seem to work. Is there a work-around for this?

like image 270
Henry Henrinson Avatar asked Sep 01 '11 11:09

Henry Henrinson


People also ask

What is case class and pattern matching in Scala?

Scala's pattern matching statement is most useful for matching on algebraic types expressed via case classes. Scala also allows the definition of patterns independently of case classes, using unapply methods in extractor objects.

What is pattern matching in Scala?

Pattern matching is a way of checking the given sequence of tokens for the presence of the specific pattern. It is the most widely used feature in Scala. It is a technique for checking a value against a pattern. It is similar to the switch statement of Java and C.

What are Scala case classes?

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.

What is the difference between class and case class in Scala?

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).


2 Answers

If you look at the signature of the unapply function created for the class B, you will see that it is: unapply(x$0: Q): Option[Int]. Thus, the unapply function works with the first range of parameter of the case classes.

It is confirmed by the scala specification (§5.3.2):

The formal parameters in the first parameter section of a case class are called elements; they are treated specially. First, the value of such a parameter can be extracted as a field of a constructor pattern.

It claims clearly tha only the first parameter section is available through the extractor.

Several workarounds:

  • uncurry your parameters
  • use a pattern matching with guard if you want to test the 2 values: case x@B(3) if x.b == "bazinga" => ...
  • use a normal class and define your own companion object with your own apply / unapply
like image 139
Nicolas Avatar answered Sep 20 '22 20:09

Nicolas


What's wrong with this?

def m(a: A) = a match {
  case b: B => print("B")
  case c: C => print("C")
}

I'm only asking because you didn't ask for more functionality than this.

EDIT

This could help:

object Dog {
   def apply(name: String)(size: Int) = new Dog(name)(size)
   def unapply(dog: Dog) = Some(dog.name, dog.size)
}
class Dog(val name: String)(var size: Int)

Now you can create dogs either like this:

new Dog("Snoopy")(10) 

or like this:

Dog("Snoopy")(10)

But when you pattern match on dogs the constructor pattern is not curried.

Dog("Snoopy")(10) match {
   case Dog(a, b) => // do sth with a or b
}
like image 42
agilesteel Avatar answered Sep 22 '22 20:09

agilesteel