Книга: Metaprogramming Ruby 2
Назад: The Case for Domain-Specific Languages
Дальше: DSLs and Metaprogramming

Internal and External DSLs

Let’s see an example of a DSL that’s actually a GPL in disguise. Here’s a snippet of Ruby code that uses the Markaby gem to generate HTML:

 
require ​'markaby'
 
 
html = Markaby::Builder.new ​do
 
head { title ​"My wonderful home page"​ }
 
body ​do
 
h1 ​"Welcome to my home page!"
 
b ​"My hobbies:"
 
ul ​do
 
li ​"Juggling"
 
li ​"Knitting"
 
li ​"Metaprogramming"
 
end
 
end
 
end

This code is plain old Ruby, but it looks like a specific language for HTML generation. You can call Markaby an internal DSL, because it lives within a larger, general-purpose language. By contrast, languages that have their own parser, such as make, are often called external DSLs. One example of an external DSL is the Ant build language. Even though the Ant interpreter is written in Java, the Ant language is completely different from Java.

Let’s leave the GPL vs. DSL match behind us and assume that you want to use a DSL. Which DSL should you prefer? An internal DSL or an external DSL?

One advantage of an internal DSL is that you can easily fall back on the underlying GPL whenever you need to do so. However, the syntax of your internal DSL will be constrained by the syntax of the GPL behind it. This is a big problem with some languages. For example, you can write an internal DSL in Java, but the result is probably still going to look pretty much like Java. But with Ruby, you can write an internal DSL that looks more like an ad hoc language tailored to the problem at hand. Thanks to Ruby’s flexible, uncluttered syntax, the Markaby example shown earlier barely looks like Ruby at all.

That’s why Ruby programmers tend to use Ruby where Java programmers would use an external language or an XML file. It’s easier to adapt Ruby to your own needs than it is to adapt Java. As an example, consider build languages. The standard build languages for Java and C (Ant and make, respectively) are external DSLs, while the standard build language for Ruby (Rake) is just a Ruby library—an internal DSL.

Назад: The Case for Domain-Specific Languages
Дальше: DSLs and Metaprogramming