How does Scala "want" me to define s-expr? In English, we define s-expr recursively, like this: "An s-expr is either an atom or a list of s-exprs." How do you say that in Scala?
I'm pretty sure this is wrong:
// Scala 2.11.2
trait Sexpr
case class Atom(text: String) extends Sexpr
type List[Sexpr] = Sexpr // To my amazement, the compiler accepts this!
val blah = Atom("blah")
def matchtest(sexpr: Sexpr): Unit = sexpr match {
case blah :: Nil => println(blah) // But the compiler won't accept this
case _ => println("no match")
}
matchtest(List(Atom("blah")))
Either
is probably not a good fit for this, either, since then I'd have to distinguish between Left
and Right
, which is beside the point.
How do you make a recursive class definition like this so that it works nicely with the rest of Scala?
The problem is type List[Sexpr] = Sexpr
is not what you think it means. List
is a type alias that you have defined with a type parameter, and not the List
class. So when you attempt to pattern match it as a List
(the class), it fails. It would be no different to write type L[Sexpr] = Sexpr
You need to define your own list type. Using scala.collection.immutable.List
isn't correct, though. According to the wikipedia definition:
an s-expression is classically defined[1] inductively as
- an atom, or
- an expression of the form (x . y) where x and y are s-expressions.
In a Scala List
, x
is not a List
(in x :: y
), which does not fit the above definition. An S-expression is more general than a linked list, and is a type of binary tree. A Scala List
is a particular type of S-expression where the first ordinate of each pair is an atom, but not all S-expressions fit into a List
.
It should look more like this:
sealed trait SExpr[+A]
case object NilAtom extends SExpr[Nothing]
case class Atom[+A](a: A) extends SExpr[A]
case class Pair[+A](a1: SExpr[A], a2: SExpr[A]) extends SExpr[A]
def matchTest[A](sexpr: SExpr[A]): Unit = sexpr match {
case Pair(a1, a2) => println("Not an Atom")
case _ => println("Atom")
}
Usage:
scala> matchTest(Pair(Atom("Test"), NilAtom))
Not an Atom
scala> matchTest(Atom("Test"))
Atom