Static Verification: An External DSL Advantage
By Zef Hemel
- 6 minutes read - 1068 wordsLast week I wrote a number of posts about web development ālanguagesā developed as internal DSLs. An internal DSL is a set of libraries written on top of a host language that, through the use of meta programming facilities, looks a lot like a domain-specific language. I looked at Ruby on Rails as a prime example of an internal DSL built on a dynamic language, at JBoss Seam as a framework built on Java, and then at Lift and my own little page language as examples of internal DSLs in Scala.
This order was on purpose. I started with the most dynamic, catch-any-error-at-runtime language and ended with the most strongly typed we-try-to-find-any-mistake-at-compile-time solution. Iāve been accused of bashing these languages just for the heck of it, but there was a point to this, and itās more than promoting WebDSL.
But letās talk a little about WebDSL anyway. WebDSL is an external DSL for developing data-rich web applications. āExternal DSLā means that it is not built on top of an existing language like Ruby or Java, but that it has its own syntax and compiler. Developing such an external DSL is typically more work than developing an internal DSL, which is a disadvantage, but external DSLs have their advantages too.
Whenever we present WebDSL to people that have done some web development before, we get asked āWhy is this better than Rails? Why not an internal DSL?ā Although we always have problems answering this question, because none of us are very experience Rails developers, our answer comes down to a few general things that potentially apply to any external DSL:
- We have a nicer syntax, as an external DSL weāre not limited to encoding things in a Ruby or Scala way
- Being platform independent, we can generate Java, PHP, Python and C# code if we want to
- An external DSL compiler can statically analyze your application to find common programmer mistakes
Thereās also the runtime performance issue, but I donāt want to get into that.
DSLs built on modern languages like Ruby and Scala look pretty natural. But from time to time you gotta wonder, why do I have to put a colon in front of this word, why do I have to use quotes here? Because you are building on top of a host language, you have to obey that languageās syntax and evaluation rules, whether you like it or not. External DSLs do not have this restriction, they define their own syntax and interpretation of that syntax.
The second advantage is clear, our language does not rely on any platform in particular and we could therefore generate code for any web platform. With Ruby on Rails youāre limited to, well, Ruby. Admittedly, there are Ruby implementations for the JVM andĀ .NET now, but what about my little brother who can only afford cheap PHP hosting, should he just use⦠PHP?
We felt that the current state of the art in web frameworks is not very resilient to programmer mistakes. Typos lead to enormous stack traces and are often difficult to track down, right? Thatās the thing I tried to verify last week.
It turned out we were pretty right. Although not everybody agrees.
Static verification of external DSLs
WebDSL is a statically typed language. It consists of a number of sub-languages. Currently we have a language for defining user interfaces, data models, business logic, access control, data validation and workflows.
To get a flavor of it, hereās an example entity definition. It defines a task entity:
entity Task {
Ā nameĀ :: String
Ā descriptionĀ :: Text
Ā doneĀ :: Bool
Ā archivedĀ :: Bool
Ā user -> User (inverse=User.tasks)
}
and a page definition:
define page tasks(userĀ : User) {
section {
header{āTasks for ā output(user.username) }
table{
for(taskĀ : Task in user.todo) {
row{
output(task.done)
output(task)
}
}
}
}
}
and an access control rule that matches any page whose name starts with edit.
rule page edit*(*) {
Ā loggedIn
}
The nice thing is that we can detect all kinds of mistakes in WebDSL code at compile time. For instance, what if the inverse property of user in the Task entity does not exist?
$ webdsl build
Loading application settings (application.ini)ā¦
[ webdslc | info ] stage 1: parsing webtasks.app
[ webdslc | info ] stage 2: importing modules
[ webdslc | info ] stage 3: typechecking
- webtasks.app:21/25: error: The field User.tasks does not exist
inverse = User.tasks
It points to exactly the right file, line number and column number. Or letās say we misspell a property name in a page definition:
for(taskĀ : Task in user.todo) {
row{
output(task.don)
output(task)
}
}
Result:
- webtasks.app:119/15: error: No property don defined for Task
task.don
Or, mistype a template name:
for(taskĀ : Task in user.todo) {
row{
outputt(task.done)
output(task)
}
}
The error we get at compile time is as follows:
- webtasks.app:39/14: error: Template with this signature not definedĀ
outputt(task.done)
Note the use of domain-speak. This template is not defined, not value or method, but template.
What if we define an access control rule for a page, or set of pages that does not exist:
Access control warning: unused rules:Ā
Ā rule page edit*(*)
We are currently working to also add more thorough checks for page element compositions. For instance, are rows defined within tables, listitems within lists and so on. Weāre also working on IDE integration, so that your WebDSL programs are checked as you type.
No matter if you like the WebDSL language and its syntax or not, love or hate the formatting and wording of its error messages, abstract from all of that and the opportunity is clear:
An external DSL compiler can detect mistakes early and its error messages can be very clear and domain-specific.
Because weāre building our own syntax, compilers and typecheckers it is quite easy to add checks like these. Detecting mistakes in this manner is either not feasible, or at the very least more difficult in internal DSLs. In dynamically typed languages itās extremely hard because of the languageās dynamic nature, in statically typed languages like Scala it may require building a compiler extension, which is also a non-trivial exercise.
Although you may say you would never make mistakes that are caught by checks like these, they have turned out to be extremely useful to us, as WebDSL users ourselves. But then again, that may be because weāre just all a bunch of NNPPs.