ScaLearning 4 – DSL Exploration & Operator Notation
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.
Why make a DSL?
I’ve heard it said that the real meat of learning Scala is done by people who want to expose a DSL. So far I’ve found learning Scala fairly easy. Sometimes I’m a little unclear on rules, like when I can leave out brackets and when I can’t, but when that happens I can just re-add the brackets and carry on.
The same flexibility isn’t quite so true for people who wish to create a good DSL. A great deal of effort goes into carefully constructing the syntax. Scala is such an incredibly extensible language that, with a little patience, you can create code that doesn’t look at all like “Scala”, and yet still compiles.
In order to get a taste of this learning, I decided I was going to attempt a DSL.
Starting into DSL Exploration
I decided my DSL of choice was going to be LOLCode.
Based on the (priceless) LolCats meme, LolCode presented itself as the perfect project to build a DSL around:
- Some rigidity in sticking close to the community consensus on syntax
- Enough flexibility to allow me to explore a few different approaches
- Different enough from Scala to present a challenge in implementation
- There’s a specific “feel” for the DSL we can aim for
- Unary (prefix) Notation
- Only works for +, -, !, and ~
- Method must be no-args
- Must omit the dot – ie: !.true would not work, but !true would
- Postfix Notation
- Method must be no-args
- May omit the dot and brackets if desired, but not required.
- Infix Notation
- Method must be single-argument
- May omit the dot and brackets if desired, but not required. ie: “a” + “b” or “a”.+(“b”)
Learned Lesson 1: Operator Notation
I decided to start where everyone starts, “Hello World”. System output is one of the most basic pieces of functionality for a language. The spec lists “VISIBLE” as the keyword for println:
VISIBLE "Hello World" // Prints hello world with a newline
VISIBLE "Goodbye"! // Prints goodbye, but with no newline
This seemed easy enough. We’ll start by ignoring the bang-syntax:
scala> def VISIBLE(text : String) = {println(text)}
VISIBLE: (text: String)Unit
scala> VISIBLE "Hello World"
:1: error: ';' expected but string literal found.
VISIBLE "Hello World"
Uh-oh.
Just starting in, we’ve already ran into a lesson on operator syntax. There are three kinds of Operator Notation:
In my case, “VISIBLE” would be a good Unary operator on some string-like object, but it is not a valid unary operator. For it to be an infix operator, I need some other “object” for it to be Infix of. Looks like I’ll have to compromise on this one.
Reading around the site, I discovered that while the spec listed “VISIBLE” as the “println” equivalent, “U SEEZ” was the community’s preference.
U SEEZ "Hello World"
U SEEZ "Hello World"!
This is convenient, as commenters on LolCode pointed out, as “U” can be seen as the I/O similar to Java’s “System.out”, and “SEEZ” is simply a “print” method on any output stream.
So let’s wrap up today’s post with one last crack at the non-bang notation:
scala> class OutputStream {
| def SEEZ(output: String) = println(output);
| }
defined class OutputStream
scala> val U = new OutputStream
U: OutputStream = OutputStream@4be03c55
scala> U SEEZ "Hello World"
Hello World
Worked like a charm. Stay tuned, as I continue to explore the process behind creating a DSL, and what it reveals about the language.