When Scala DSLs Fail

logoThe hot new contender in the space of interal DSLs (domain-specific languages as libraries) is Scala. Scala is, as is implied by its name, a language that is designed to scale from small one-off scripts to large enterprise applications. It is a statically typed language, and in many ways can be seen as a successor to Java. It also compiles to JVM bytecode. Similar to Ruby, Scala has a flexible syntax, which makes it an interesting choice to develop internal DSLs for (like Ruby on Rails).

The most popular web application framework for Scala is Lift. On its website it is described as follows:

Lift is an expressive and elegant framework for writing web applications. Lift stresses the importance of security, maintainability, scalability and performance, while allowing for high levels of developer productivity.

In Lift, more configuration is done in Scala code rather than in XML files like in a framework like Seam. Consequently, simple typing mistakes are typically caught by the compiler. One area where mistakes are still easily made are in its XML-based language for constructing user interfaces. I won't milk this one too much, we know the trick by now, but let me demonstrate what happens when a programmer makes mistakes in templates.

Mistake #1: mistyping a class name

What happens if my satanistic fingers force me to type hellWorld, instead of helloWorld:

<lift:surround with="default" at="content">
   <h2>Welcome to your project!</h2>
   <p><lift:hellWorld.howdy /></p>
</lift:surround>

We get no error at compile time. At runtime, however something odd happens.

lift-fail-1

It does display the "Welcome to your project!" message, and the list item with "Home" on it is part of our global template. But where did our howdy message go?

In the console we see the following;

lift-fail-1b

In a way this is an acceptable message, but shouldn't this be an error?

Mistake #2: mistyping an opening tag

This is a nice one. Instead of typing "surround" in our template, we misspell it as "surrond". A honest mistake. How does Lift deal with this?

<lift:surrond with="default" at="content">
  <h2>Welcome to your project!</h2>
  <p><lift:hellWorld.howdy /></p>
</lift:surround>

This is what we see when we load the page:

lift-fail-2

line 4 does not exist? I'm pretty sure that it does... Looking at the stacktrace, I see it has to do something with XML parsing, but other than that, the message is kind of unexpected.

Alright, one more, then it's been enough.

Mistake #3: mistyping a template block name

Let's now misspell some more, we're getting good at it.

<lift:surround with="default" at="conent">
   <h2>Welcome to your project!</h2>
   <p><lift:hellWorld.howdy /></p>
</lift:surround>

Again, no compilation errors. When we load the page we see the following:

lift-fail-3

No error message on screen, or in our console. What happened here? We only see the global template and nothing from the content we just defined. This, of course, has to do with the fact that we're defining a template that's never actually called. It's subtle, but can be tricky to find.

Enough already, we get it!

Ok, ok. I know. You get my point. Statically typed framework fail to detect errors that are encoded in strings and XML files. So what can we do to fix this (other than IDE support)? Well, the obvious thing is stop abusing strings and XML files in this way. If we find an acceptable way to encode these things in Scala we can check a lot more. Lift takes its first steps towards this by doing configuration in its Boot class. There are a few Scala DSLs that make database queries statically verifiable. Still, the user interface part breaks. 

To fix this I attempted to build a simple version of WebDSL as an internal DSL in Scala. What follows is a piece of code that defines a user interface using this DSL:

scala-internal-fail-1

This piece of code defines an entries template. The template renders a header saying "All entries". Underneath we see a list of all Entry objects (Entry.all). For each entry there is a list item with a form. The entry's name and text are displayed and there's a button to delete the entry from the database. Simple and elegant (in my opinion).

The nice thing is that when I misspell one of the UI constructs, I get an error:

scala-internal-fail-2

So the error is found at compile time, which is nice. However, are these errors very helpful?

scala-internal-fail-2b

value buton? Is a buton supposed to be a value? In our domain-speak we're attempting to define a page element here, not a value. The good thing about error messages as you compile is that you see that something is wrong early, and typically can see clearly where the error is. The error messages, however, are not always very helpful, and hardly ever domain specific ("No such page element: buton" would have been a great error message).

But there's another error in the code I just showed and the compiler didn't catch it at all. The thing is that listitems cannot appear just anywhere, they have to appear within lists! The list construct was left out. That's easily fixed:

scala-internal-fail-1-fixed

What this example shows is that although we can catch many errors by creating an embedded DSL in Scala, there are definitely limits. Additionally, there is no way to give domain specific error messages at compile time (unless we extend the compiler), error messages typically expose the underlying implementation. Yes I admit it, the code to construct a button and define its logic is actually a method call.

So although internal DSLs in a statically typed language help you to find certain types of error faster, the error reporting is far from perfect. This is one of the strong points of external DSLs as we will see in a future post. Update: This post, to be exact.