ScaLearning 6 – DSL Exploration & Operator Interpretation


Like many developers who make the journey from Java to Scala, I often find myself amazed at how much easier it is to do some things, or how much easier it is to express myself in Scala.

“ScaLearning” will be a series of short blog-posts just documenting little tidbits I find interesting, confusing, amusing, or otherwise worthy of talking about.

DSL Exploration: Our Tale So Far

For those interested in the whole DSL Exploration “mini-series”, we began with DSL Exploration & Operator Notation.

To quickly summarize our tale so far, I decided to explore the creation of a DSL to expose some of Scala’s nuances. My project of choice was codifying the LOLCode language as a Scala DSL. So far we have created a syntax for outputting data to the screen.

Working syntax so far:

U SEEZ "Hello World" !
U SEEZ "Hello World" !!


Learned Lesson 3: Operation Interpretation

So far we’ve been doing all of our work in the Scala REPL. While extremely powerful, the REPL does have a few known limitations – it sometimes interprets a line differently than it would have been interpreted from a source file. Today we’ll discover one such situation, and explore the reason.

Let us start with a Scala source-file which will act as our basis:

class Printable(output:String) {
  var bang = false;
  def ! = { print(output); }
  def !! = { println(output); }
class OutputStream {
  def SEEZ(output: Printable) : Unit = println(output.toString)
val U = new OutputStream

U SEEZ "Hello"!
U SEEZ " World"!!
U SEEZ "Goodbye"!
U SEEZ " World"!!

The code above is equivalent to what we were writing into the REPL in previous posts, but let’s store that in a file ScaLol.scala and run it:

tcalder:bin tcalder $ scala ScaLol.scala
/.../ScaLol.scala:10: error: value ! is not a member of java.lang.String
U SEEZ "Hello"!

Clearly not what the REPL was telling us. At first glance, it now appears as if it’s completely changed its operational precedence, and that’s true… but only sort of.

After trying various combinations of the code (none of which helped), I eventually gave up and started reading the Scala Reference again.

Let’s review what we’ve said so far:

  • Methods with one argument can be used as an infix operator
  • Methods with no arguments can be used as a postfix operator
  • Unary operators for !, +, -, and ~ can be used if equivalent “unary_*” methods are defined
  • Postfix operators always have lower precedence than infix operators
  • Infix operators have an order of precedence which includes letters being lower than special characters

Reasoning briefly about our code, the REPL clearly evaluates the “!” as a postfix operator, which gives it the lowest precedence and results in:


Yet in ScaLol.scala we appear to have attempted to run “!” on String. Since Postfix have low precedence, we just be attempting to evaluate “!” as an Infix operator. This would make sense since special characters have a higher operational precedence:

U.SEEZ( "Hello World".!(.....?) )

Now if you’re like me, you’re wondering what on earth it could possibly be trying to fill in as the argument to this strange new Infix “!”. The answer is, it’s trying to use the next line as a continuation on the current:

U.SEEZ("Hello".!(U)).SEEZ("World"... // Or some rough equivalent

From the ScalaReference:

Scala is a line-oriented language where statements may be terminated by semicolons or newlines. A newline in a Scala source text is treated as the special token
“nl” if the three following criteria are satisfied:

  1. The token immediately preceding the newline can terminate a statement.
  2. The token immediately following the newline can begin a statement.
  3. The token appears in a region where newlines are enabled.

“U” can start a statement, and “!” can terminate one, since the REPL clearly didn’t get upset at either of these. Newlines are enabled in our code – there’s a lot of logic in the Reference that explains why if you’d like to explore it.

Based on this, we can conclude that our “\n” after the “!” is, in fact, a “[nl]”.

The second piece of the puzzle that we need is the following (summarized) syntactic definitions, expressed in the Scala Ref using Extended Backus–Naur Form:

PostfixExpr ::= InfixExpr [id [nl]]
InfixExpr ::= PrefixExpr
                     | InfixExpr id [nl] InfixExpr
PrefixExpr ::= [‘-’ | ‘+’ | ‘~’ | ‘!’] SimpleExpr

In specific, draw your attention to the similarity between the definition of “PostfixExpr” and the second definition of “InfixExpr”, and pay special attention to our friend “nl”.

The following is a (rough) breakdown of what we’ve actually attempted:

U SEEZ "Hello World" !
U SEEZ "Goodbye World" !

SimpleExpr id SimpleExpr id nl SimpleExpr id SimpleExpr id

PrefixExpr id PrefixExpr id nl PrefixExpr id PrefixExpr id

InfixExpr id InfixExpr id nl InfixExpr id InfixExpr id

Bingo, there’s our culprit. The second “id” in the expression is our “!” operator. Based on the formal specification, “InfixExpr id nl InfixExpr” can be simplified as a single “InfixExpr”, and since “!” has higher precedence than “SEEZ”, it will clearly be evaluated first.

As a final proof, try saving the following code as “ScaLol2.scala” and running it. Note how we use two new-lines in-between “SEEZ” calls. This results in two “nl” entities instead of one, which prevents the compiler from interpreting an InfixExpr where we didn’t intend one:

class Printable(output:String) {
  def ! = { print(output); }
  def !! = { println(output); }
class OutputStream {
  def SEEZ(output: String) : Printable = new Printable(output)
val U = new OutputStream

U SEEZ "Hello"!

U SEEZ " World"!!

U SEEZ "Goodbye"!

U SEEZ " World"!!

The result on my machine:

tcalder:bin tcalder $ scala ScaLol2.scala
Hello World
Goodbye World

That’s all for this week! I hope today’s post proved interesting, it was far deeper than I ever expected to go, which was exactly what I was hoping for.



Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s