Published on The Scala Programming Language (http://www.scala-lang.org)


By admin
Created 2008-07-05, 20:42

A Tour of Scala: Case Classes

Scala supports the notion of case classes. Case classes are regular classes which export their constructor parameters and which provide a recursive decomposition mechanism via pattern matching [1].

Here is an example for a class hierarchy which consists of an abstract super class Term and three concrete case classes Var, Fun, and App.

abstract class Term
case class Var(name: String) extends Term
case class Fun(arg: String, body: Term) extends Term
case class App(f: Term, v: Term) extends Term

This class hierarchy can be used to represent terms of the untyped lambda calculus [2]. To facilitate the construction of case class instances, Scala does not require that the new primitive is used. One can simply use the class name as a function.

Here is an example:

Fun("x", Fun("y", App(Var("x"), Var("y"))))

The constructor parameters of case classes are treated as public values and can be accessed directly.

val x = Var("x")
Console.println(x.name)

For every case class the Scala compiler generates equals method which implements structural equality and atoString method. For instance:

val x1 = Var("x")
val x2 = Var("x")
val y1 = Var("y")
println("" + x1 + " == " + x2 + " => " + (x1 == x2))
println("" + x1 + " == " + y1 + " => " + (x1 == y1))

will print

Var(x) == Var(x) => true
Var(x) == Var(y) => false

It makes only sense to define case classes if pattern matching is used to decompose data structures. The following object defines a pretty printer function for our lambda calculus representation:

object TermTest extends Application {
  def printTerm(term: Term) {
    term match {
      case Var(n) =>
        print(n)
      case Fun(x, b) =>
        print("^" + x + ".")
        printTerm(b)
      case App(f, v) =>
        Console.print("(")
        printTerm(f)
        print(" ")
        printTerm(v)
        print(")")
    }
  }
  def isIdentityFun(term: Term): Boolean = term match {
    case Fun(x, Var(y)) if x == y => true
    case _ => false
  }
  val id = Fun("x", Var("x"))
  val t = Fun("x", Fun("y", App(Var("x"), Var("y"))))
  printTerm(t)
  println
  println(isIdentityFun(id))
  println(isIdentityFun(t))
}

In our example, the function print is expressed as a pattern matching statement starting with the match keyword and consisting of sequences of case Pattern => Body clauses.

The program above also defines a function isIdentityFun which checks if a given term corresponds to a simple identity function. This example uses deep patterns and guards. After matching a pattern with a given value, the guard (defined after the keyword if) is evaluated. If it returns true, the match succeeds; otherwise, it fails and the next pattern will be tried.


Source URL (retrieved on 2013-06-18, 07:44): http://www.scala-lang.org/node/107

Links:
[1] http://www.scala-lang.org/node/120
[2] http://www.ezresult.com/article/Lambda_calculus