It also shows how you can iterate from the ugliest, most powerful, most dangerous eval method by pulling out the functionality you need into less-powerful, safer manipulations. It's all Ruby code until it's actually executed, so it can all be manipulated at runtime.
It focuses on modern Rubies, so it talks about Module#prepend and such. Nice to keep moving forwards.
It's a vague enough question to lead to nothing but religious wars, but I really want to push people who are interested in (e)DSLs to consider their implementation in typed languages. Haskell is, of course, a favorite of mine, but you can achieve similar things in Scala and the ML family. You can even go much further using something like Idris, mostly by using its stronger dependently typed system.
And typing is not a big deal, once you have a proper metaprogramming, you can easily add typing on top of your underlying language.
You can introduce your own type system, of course, but it's really nice to have most of that infrastructure pre-built for you (otherwise why embed?).
Both ways are not as convenient as with the more metaprogramming-oriented languages, plus the lack of reflection limits the potential for integrating DSL into the host language and the other DSLs. Yet, TH is really nice. I cannot stand languages without any metaprogramming capabilities at all, and TH just ticks the box for me.
Compiled DSL implementations are much more declarative - they simply define how a DSL is mapped to the other DSLs or a host language.
Compiled DSLs are more efficient - you don't want an interpreted regexp engine, don't you?
Compiled DSLs are IDE- and static-analysis-friendly, at no additional cost.
Compiled DSLs are more modular and reusable - by sharing the same runtime environment, they can be easily mixed together, their fine grained properties can be picked individually and mixed into new DSLs.
Having said that, I see no single advantage of the interpreted DSLs.
I did a lot of research in tracing JITs, runtime partial specialisation and all the related stuff, but so far there is no practical and useful implementation of any of such techniques which could beat a mere straightforward static translation.
I'm not talking about fully standalone compiled DSLs (totally agree they're usually unmaintainable) - I was referring to the metaprogramming-based compiled eDSLs, implemented as macros on top of a host language (or a range of host languages). This way it's really easy to mix traits into your DSL design, including various type systems, not even necessarily directly compatible with the host language type system.
I found it very productive to have Prolog (or anything comparable) as one of the host languages, it's trivial then to map most of the practical type systems to it.
Along the same lines, here's the ever-entertaining Rich Kilmer talking about DSL experiences on some government projects:
Programmer with a couple of decades' experience here, but no ruby. I have no idea what half the tokens in the example mean, or what changes would be valid beyond editing the four obvious values.
Also, capturing blocks and instance_eval are notoriously slow.
0 - https://www.youtube.com/watch?v=yuh9COzp5vo
1 - https://speakerdeck.com/sferik/writing-fast-ruby
...and, in this case, are evaluated once, at startup. It's so fast that you couldn't possibly notice a difference.
I often think Ruby is unsurpassed for writing readable, English-like DSLs - perhaps because of the combination of the convenient lassitude of Ruby's syntax (not requiring brackets - in many cases - or semi-colons) and the dangerous power of its metaprogramming abilities. The only other language that perhaps comes close and immediately springs to mind, before I've had my first coffee of the day, is (appropriately) CoffeeScript. Any other takers?
Symbolic Manipulation: http://norvig.com/paip/macsymar.lisp
Generating HTML: http://allegroserve.sourceforge.net/aserve-dist/doc/htmlgen....
I personally love how easy it is to write lucid & robust dialects in Rebol. Some examples of Rebol dialects...
* List comprehension - http://blog.revolucent.net/2009/04/dirt-simple-dsl-in-rebol....
* Cron/scheduler - http://softinnov.org/rebol/scheduler.shtml
* Excel - http://www.robertmuench.ch/development/projects/excel/dialec...
* PDF builder - http://www.colellachiara.com/soft/Misc/pdf-maker-doc.pdf
* GUI example (written in Rebol 2 VID) - https://news.ycombinator.com/item?id=7070349
* And a small ditty that I left on Reddit comment just to show what can be done - http://www.reddit.com/r/programming/comments/1tw17i/rust_is_...
Well, obviously on the type safe DSL front nothing compares to Scala or Haskell.
Groovy's MOP was a bit buggy/lacking when I was using it (this is as of 3 years ago mind you, not sure how Groovy MOP has evolved since then) but I recall Gradle and Spock being impressive DSLs, very natural/readable.
The Spock syntax isn't a DSL, it hacks the ossified restrictions of the Antlr 2 based Groovy syntax to pull off tricks like overloading the | operator to give the appearance of tables, and the break/continue labels via an AST plugin to replace function calls with sections, as in this oft-quoted example from their website:
Math.max(a, b) == c
a | b | c
1 | 3 | 3
7 | 4 | 4
0 | 0 | 0
IMHO absence of static analysis is a huge downside of using an interpreted language. Having type safety in an embedded DSL can only be dreampt of in a language like ruby.
And it's not a problem to build typed eDSLs on top of an untyped core language - see Typed Racket or Shen for example.
Clojure being a lisp is extremely powerful in this regard as well, but if you are programming in any lisp I would expect a fair amount of meta programming.
The best way of implementing eDSLs is to extend the syntax of your core language, which is very easy for the languages based on PEG. See https://github.com/combinatorylogic/mbase and https://github.com/combinatorylogic/clike for example of this approach.
I wrote it because I used an invoice app, that went bust and I needed something really simple to send out invoices and I was in complete control of.
Also I didn't want to use a database so just used Ruby DSL meta files that described my invoice.
Admittedly it probably needs a bit more work... but it was fun to do.
If it were included in the containing class it would only be available to that class.
Company or Employee specific attribute-creation methods could be placed on CompanyScope or EmployeeScope.