ScaLearning 5 – DSL Exploration & Operation Precedence

facebookdiggdzonestumbleuponredditdelicious


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"

 
 

Learned Lesson 2: Operation Precedence

Now that we’ve successfully defined “U SEEZ”, the LOLCode spec allows us to add a bang at the end of a sentence to “cancel” the newline. While this feels a little backwards, we can attempt it.

Our goal:

U SEEZ "Hello World" // Includes newline
U SEEZ "Goodbye"! // No newline

My first plan of attack was to make “SEEZ” return an object that has the “!” method, which would do a “print” instead of a “println”. Fortunately, before attempting this solution I realized that if I were to do so there would be no method call to trigger the non-bang “println”.

Attempt two, maybe we can allow SEEZ to accept a different object. That object can have a “!” method. Here’s what I’m aiming for:

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

 
We’ll also need an implicit to turn Strings into this special new object. Here goes.

scala> class Printable(val text : String, val bang : Boolean) {
     | override def toString() = if (bang) {text} else {text + "\n"}
     | def ! = new Printable(text, !bang)                           
     | }                                                            
defined class Printable

scala> class OutputStream {
     | def SEEZ(p : Printable) = print(p.toString())
     | }
defined class OutputStream

scala> val U = new OutputStream
U: OutputStream = OutputStream@e208506

scala> implicit def stringToPrintable(s : String) : Printable = new Printable(s, false)
stringToPrintable: (s: String)Printable

scala> U SEEZ "Hello World"
Hello World

That’s good news, we’ve managed to maintain the old behaviour, so let’s give our new “!” method a try:

scala> U SEEZ "Hello World"!
:10: error: value ! is not a member of Unit

Foiled again! So what happened here? As “println” returns type “Unit”, which is the Scala equivalent to “void”, we can venture a guess that the “!” method was actually run on the result of “println”:

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

 
As I’m sure you’ve guessed, the issue at hand is operator precedence. Let’s experiment a little:

scala> class MyObj(val v : String) {                                                  
     | def in(m : MyObj) = {println("[" + v + ".in(" + m.v + ")]"); new MyObj(v + m.v) }
     | def post() = {println("[" + v + ".post()]"); this}
     | def unary_+ = {println("[" + v + ".pre()]"); this}
     | } 
defined class MyObj
   
scala> implicit def stringToMyObj(s : String) : MyObj = new MyObj(s);
stringToMyObj: (s: String)MyObj

scala> +"a"
[a.pre()]
res2: MyObj = MyObj@7987b796

scala> + "a" in "b"
[a.pre()]
[a.in(b)]
res3: MyObj = MyObj@3cd41115

scala> + "a" in "b" post
[a.pre()]
[a.in(b)]
[ab.post()]
res4: MyObj = MyObj@6dcc55fb

scala> "a" in "b" in "c"
[a.in(b)]
[ab.in(c)]
res5: MyObj = MyObj@69cad977

scala> "a" in "b" post
[a.in(b)]
[ab.post()]
res6: MyObj = MyObj@37b24706

scala> "a" post
[a.post()]
res7: MyObj = MyObj@7ea4b9da

scala> "a".post in "b"
[a.post()]
[a.in(b)]
res8: MyObj = MyObj@7e28388b

scala> "a" post in "b"
:1: error: ';' expected but string literal found.
       "a" post in "b"
                   ^

 
 
We seem to be allowed to omit all the dots and brackets when we chain infix operators, but we’re unable to chain an infix operator to the result of a postfix operator unless the postfix used a dot to call. The postfix call at the end of a chain also seems to apply to the result of the previous infix call.

Note: By using the ‘dot’ in [“a”.post in “b”] we are no longer using an ‘operator’. Explicit method calls are referred to as “function applications” in the reference

According to the Scala Reference:

  • If there are several infix operations, operators with higher precedence bind more closely than operators with lower precedence
  • If there are consecutive infix operations with operators of the same precedence, all operators must have the same associativity
  • Postfix operators always have lower precedence than infix operators

While prefix operators aren’t mentioned, they appear to have a higher precedence than infix operators via experimentation:

scala> class MyObj(val v : String) {
     | def +(m : MyObj)  = {println("[" + v + ".in(" + m.v + ")]"); new MyObj(v + m.v) }
     | def unary_+ = {println("[" + v + ".pre()]"); this}
     | }
defined class MyObj
   
scala> implicit def stringToMyObj(s : String) : MyObj = new MyObj(s);
stringToMyObj: (s: String)MyObj

scala> +"a" + +"b"
[a.pre()]
[b.pre()]
[a.in(b)]
res21: MyObj = MyObj@144f1ada

The end result of this discussion is another compromise. After experimenting with various notations, I gave up and returned to my original plan of a “!” method on an object returned by “SEEZ”:

scala> class Printable(output:String) {
     | def ! = { print(output); }
     | def !! = { println(output); }
     | }
defined class Printable

scala> class OutputStream {
     | def SEEZ(output: String) : Printable = new Printable(output)
     | }
defined class Printable

scala> val U = new OutputStream
U: OutputStream = OutputStream@608d116e

scala> U SEEZ "Hello World"!
Hello World
scala> U SEEZ "Hello World"!!
Hello World

scala>

Stay tuned, next time we’ll attempt to put the pieces together and run HelloLol.scala


facebookdiggdzonestumbleuponredditdelicious

Advertisements

Leave a Reply

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

WordPress.com Logo

You are commenting using your WordPress.com 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