Richard Bucker

My requirements for the ideal DSL

Posted at — Jul 21, 2013

I do not want to create a primer or answer the question as to what a DSL is (domain specific language) but I recognize that the line between a successful programming language and a ubiquitous uber programming language is somewhere between the range of problems that a language can solve. For example languages like C, C++, and assembler are excellent for low level programming but one can get tangled at high level. Languages like Go, C#, erlang and Java (CLR, Mono, JVM, Beam/EVM) are better at medium level challenges and most dynamic languages like perl, Ruby, JavasScript and Python are better at high level problems.

Going back to Dave Thomas’s (pragprog) AU Ruby Conf keynote. Paraphrasing: The power of any DSL is that it’s ideally suited for the complexity of the challenge it solves making the programmer productive.

A side note: I have purposefully left out languages like CoffeeScript, Elixir and Clojure because they are more akin to translations than they are standalone; and they simply do not solve any more problems than they inherit from their environments etc…

Up until this point in the argument I have been examining these languages from the level at which the programming interacts or develops application. From this perspective languages are broken into 3 components. (1) syntax; (2) APIs; (3) execution.

syntax; The differences in most languages is most dramatic in the syntax. It’s the place where most novice language designers think the definition of a DSL is. Almost all syntax looks the same. That’s probably because todays language tools like YACC and LEX, BNF etc help the language designer get to market quickly. So with the exception of concepts like Object-Oriented, lambda, and closures (and some other concepts) I think it’s safe to say that ALL modern programming languages are more similar than they are different.

APIs; unless the language provides direct access to the system hardware like registers and memory; everything, if exposed, is going to be provided in the APIs. The difference between the high and low level language APIs is the amount of protection the API provides for the user and the overall efficiency and implementation. For example ‘C’ lets the operating system detect the memory errors while Java and C# perform that task (whether they defer to the OS and intercept or preempt is not important)

execution; There are many variations to this model. From a binary with statically linked in libraries; or dynamic linked libraries; virtual machines like the CLR, Mono, JVM and EVM and more(DartVM, LuaJit); or complete interpretive runtimes like perl, perl6(I think), Ruby, Python, JavaScript. Execution is the biggest barrier to the DSL and the DSL environment because it’s easy to translate a DSL into a language that already has an execution path and it’s hard to get to a clean path. (how many GCC preprocessors do we really need).

One interesting idea is some sort of mutation. The demo I watched was the conversion of the UnReal engine from ‘C’ to JavaScript(and the demo rocks!). They accomplished the task in two ways. (a) they compiled the C code with the LLVM and then converted the code in the LLVM to JavaScript. (b) the optimized JavaScript was then running on asm.js which is a stripped down and performant JavaScript environment.(further optimized for FireFox). Unfortunately this is in the opposite direction.

My requirements for the ideal DSL:

  1. easily digested idiomatic “one solution” syntax
  2. rich APIs partitioned vertically by task and horizontally by complexity
  3. execution as a binary or with an embedded interpreter or JIT
For me; the ideal DSL is one DSL that allow me to write a device driver, implement a dynamic website backend, or generate a PDF report from a CSV.