1

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?

Ben Kovitz
  • 4,920
  • 1
  • 22
  • 50
  • Does this scala S-expr parser (with specialised notions of SNil and SCons) contain what you need: http://matt.might.net/articles/parsing-s-expressions-scala/ – NietzscheanAI Jan 15 '16 at 15:32
  • @user217281728 Yes. Wow! That even addresses what I'm actually doing, which I thought was too much to ask about: implementing a simple Scheme-like language within a larger Scala program. – Ben Kovitz Jan 15 '16 at 21:41
  • As a bonus, I think the site also has M-expressions... – NietzscheanAI Jan 15 '16 at 22:09

3 Answers3

2

This link describes a Scala parser for S-expresions in the following form:

abstract class SExp
case class SInt(val value : Int) extends SExp { ... }
case class SSymbol(val value : String) extends SExp { ...}
case class STrue() extends SExp { ... }
case class SFalse() extends SExp { ... }
case class SCons(val car : SExp, val cdr : SExp) extends SExp { ... }
case class SNil() extends SExp { ... }

As another answer observes, what looks like a list is really a form of binary tree, as shown by virtue of the explicit SNil and SCons which takes arguments of type SExp.

NietzscheanAI
  • 966
  • 6
  • 16
1

Something like this maybe

 trait Sexpr
 case class Atom(text: String) extends Sexpr
 case class SList(list: Iterable[Sexpr]) extends Sexpr

 def matchtest(sexpr: Sexpr): Unit = sexpr match {
    case SList(blah :: Nil) => println(blah)
    case _ => println("no match")
 }
Dima
  • 39,570
  • 6
  • 44
  • 70
  • This isn't really correct. The "list" the definition of an S-expression refers to is _not necessarily_ a linked list like `scala.collection.immutable.List`, it is more general than that. – Michael Zajac Jan 15 '16 at 00:52
  • `Iterable` isn't really any better, in fact, `Iterable` doesn't fit the definition of an s-expression at all. It's not recursive, in general. – Michael Zajac Jan 15 '16 at 02:51
  • @m-z Sure it does fit. I don't know what definition you have in mind, but it definitely fits the definition that OP posted. I think, you are just being difficult. Even `List` was really fine (the concrete details of implementation are irrelevant). – Dima Jan 15 '16 at 02:56
  • I'm not purposefully being difficult. In programming you cannot say "details are irrelevant", because they absolutely are relevant. A linked list is an s-expression (a special one), but not all s-expressions are linked lists. It's not the same structure. – Michael Zajac Jan 15 '16 at 03:14
  • @m-z If there is one phrase you sayo *constantly* in programming, it is "implementation details is irrelevant". You program to the interface, not implementation. The classes and data structures are *models* if the real life objects, not the actual objects. Not all strings are arrays of characters, not all integer numbers are smaller than 2^31. Etc. So, by your logic, Scala's (and Java's and any other language's) definitions of int or String are wrong. Hey are not. Just like my definition of S-Expr isn't. You are being difficult and you know it. – Dima Jan 15 '16 at 11:29
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/100771/discussion-between-m-z-and-dima). – Michael Zajac Jan 15 '16 at 13:58
0

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

  1. an atom, or
  2. 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
Community
  • 1
  • 1
Michael Zajac
  • 55,144
  • 7
  • 113
  • 138
  • This is does not answer the original question: it was about *list*, not pair. – Dima Jan 15 '16 at 02:57
  • 1
    @Dima Did you read the link the OP provided? **List** and **linked list** are **not** the same thing. The OP asked how to define an s-expression in Scala, and he mistakenly believed it would be like a scala `List`, but it isn't. Please read about what an s-expression actually is. – Michael Zajac Jan 15 '16 at 03:06
  • Indeed I'm just looking for a "Scalastic" way to define sexprs, with no restrictions on how. This answer has already been quite valuable just by liberating me from thinking only in terms of doing something with `List`. This answer answers the deeper question, which is how to define a recursive data structure in Scala. (Not coincidentally, `List` is defined with same approach.) Hence the +1. :) – Ben Kovitz Jan 15 '16 at 03:59
  • @m-z `Pair` and `Tuple2` aren't the same thing either. Sure, the _represent_ the same thing, just like `List`, `Iterable` or `Vector` do, but the are not it. So, your agument applies here too: your `Pair` class is an s-expr, but not all s-exprs are `Pairs`. `(SExpr, SExpr)` for example, is an s-expr, but not a `Pair`, etc. Moreover, the wiki page defines sexprs as ordered pairs, but OP's post mentions a list. Your definition can therefore be considered correct by some people (not by you though, for the reasons you explained above), but does not answer the OPs actual question. – Dima Jan 15 '16 at 11:45
  • @BenKovitz, yeah, it's not a bad answer. M-z just is in an argumentative mood today, so he is getting what he asked for. This definition isn't really recursive, BTW, as both classes reference a common trait, but not themselves or each other. If you are looking for an example of a true recursive definition, just look at the classic linked lost node: `case class Node[+A](data: A, next: Option[Node[A]] = None)`. – Dima Jan 15 '16 at 12:16
  • @Dima Maybe I should have said "inductive definition" rather than "recursive definition", as in [this book](https://books.google.com/books?id=-VkNAAAAQBAJ&pg=PA207&dq=%22inductive+definition%22+bnf&hl=en&sa=X&ved=0ahUKEwjghbvEga3KAhUHlx4KHcVyB2cQ6AEIJTAA#v=onepage&q=%22inductive%20definition%22%20bnf&f=false). Regarding "list": in the question, I meant "list" in its ordinary English sense. Scala's `List` is just one way to model the abstract concept. In other words, I'm open to all approaches, as long as they work nicely in Scala. – Ben Kovitz Jan 15 '16 at 23:55
  • 1
    @BenKovitz Scala `List` is one model of english language list, just like m-z's `Pair` is just one model of the notion of an ordered pair, and also, one of the ways to represent a linked list. That's a very prominent feature of modeling things: there is usually more than one way, so arguing that your way is wrong, because I can think of another is ... well, wrong :) – Dima Jan 16 '16 at 00:06