Creating DSLs in Java, Part 1: What is a domain-specific language?

Learn DSL concepts and where they're used in real-world programming

1 2 3 4 Page 3
Page 3 of 4

Examples of DSLs in everyday programming

As I noted at the beginning of this discussion, DSLs are very common. Chances are good that you've already used quite a few of them as both a programmer and a user. One nearly omnipresent example of a DSL is Cascading Style Sheets (CSS), which allows you to add style to Web pages and documents. Listing 1 is an excerpt from a CSS file. It specifies the style for a hyperlink (the <A> tag) -- namely how its color should change when you mouse over it.

Listing 1. CSS is a DSL

A:hover { color: #FF0000; text-decoration: none; }

Another example of an external DSL is the stuff you write in a makefile for the make utility. The domain in this case is build -- the source code that you want to compile and build into a library or executable, or the files that you want to process for generating documentation or the like. Listing 2 contains an example makefile.

Listing 2. So is this makefile

GCC=cxx -I.
all : clean compile
compile : myprog
myprog: Util.o
    $(GCC) -o myprog Util.o
Util.o : Util.h
    $(GCC) -c
clean :
    /bin/rm -f myprog Util.o

The makefile expresses a set of dependencies, and the commands (the indented statements) are executed based on the dependencies. For example, is compiled into Util.o if Util.o does not exist, or if Util.h or is modified after Util.o was created. Once you learn a few rules (and idiosyncrasies), the makefile is a pretty lightweight way to express build dependencies.

The Java equivalent of make is the popular Ant build file, an example of which you can see in Listing 3.

Listing 3. An Ant build file

<project name="AnExampleProject" default="jarit" basedir=".">
<property name="src" location="src"/>
<property name="build" location="build"/>
<property name="distrib" location="distrib"/>
<target name="compile" description="compile your Java code from src into build" >
<javac srcdir="${src}" destdir="${build}"/>
<target name="jarit" depends="compile" description="jar it up" >
<jar jarfile="${distrib}/AnExampleProject.jar" basedir="${build}"/>

Both makefiles and Ant build files are external DSLs. In the case of Ant, the XML representation is processed by the ant utility using an XML parser. Ant's vocabulary contains various terms, such as target and properties, that are valid in the domain and context of compiling and bundling code.

In the Ruby community, the equivalent of make and Ant is Rake. Rake is also an example of a DSL; however, it is written using Ruby itself, so it is an internal or embedded DSL. Listing 4 contains an example of a Rake file.

Listing 4. An example Rake file

ORIGINAL = 'input.dat'
BACKUP = 'input.dat.bak'
task :default => BACKUP
file BACKUP => ORIGINAL do |task|
cp task.prerequisites[0],

In this simple example, the file input.dat is copied (or backed up) to input.dat.bak only if the timestamp of the dat file is later than the bak file, or if the bak file does not exist. Rake skillfully takes advantage of the flexibility of Ruby to provide an elegant syntax for expressing tasks and their dependencies. For instance, in the context of Rake task is simply a method that takes a hash as a parameter.

Gant is an internal DSL that is similar to Rake; it is written using Groovy, and serves as a wrapper around Ant. If you use Gant, you don't have to endure XML coding, and can use a lightweight syntax to express your builds. Listing 5 is an example.

Listing 5. A sample Gant file

#slightly modified version of example from
includeTargets << gant.targets.Clean
cleanPattern << [ '**/*~' , '**/*.bak' ]
cleanDirectory << 'build'
target (stuff : 'A target to do some stuff') {
println 'Stuff'
depends clean
echo message : 'A default message from Ant'
target (otherStuff : 'A target to do some other stuff') {
println 'OtherStuff'
echo message : 'Another message from Ant'
setDefaultTarget stuff
1 2 3 4 Page 3
Page 3 of 4