Real world
         DSL
 making technical
    and business
  people speaking
the same language
by Mario Fusco
Red Hat – Senior Software Engineer
mario.fusco@gmail.com
twitter: @mariofusco
What is a
Domain Specific Language?
               A
computer programming language
              of
    limited expressiveness
         focused on a
      particular domain
Why use a
Domain Specific Language?

2 principles driving toward
     DSL development
Communication is king




    The only purpose of
        languages,
  even programming ones
   IS COMMUNICATION
Written once, read many times
 Always code as
  if the person
     who will
  maintain your
code is a maniac
 serial killer that
  knows where
      you live
"Any fool can write code
that a computer can
understand.
Good programmers
write code that humans
can understand“
          Martin Fowler
Pros & Cons of DSLs
+ Expressivity  Communicativity
+ Conciseness
+ Readability  Maintainability  Modificability
+ Higher level of abstraction
+ Higher productivity in the specific problem domain

̶ Language design is hard
 ̶ Upfront cost
  ̶ Additional indirection layer  Performance concerns
   ̶ Lack of adeguate tool support
    ̶ Yet-another-language-to-learn syndrome
DSL taxonomy
 External DSL  a language having custom
  syntactical rules separate from the main language of
  the application it works with

 Internal DSL  a particular way of employing a
  general-purpose language, using only a small subset
  of the language's features

 Language workbench  a specialized IDE for
  defining and building DSL, usually in a visual way,
  used both to determine the structure of the DSL and
  to provide an editing environment for people using it
DSL Types Comparison
                + learning curve                     ̶ syntactic noise
                + cost of building                    ̶ needs to be recompiled
Internal DSL    + programmers familiarity               (if host language is static)
                + IDE support (autocompletion …)
                + composability
                + flexibility                        ̶ need to learn of grammars
                + readability                            and language parsing
External DSL    + clear separation between            ̶ boundary between DSL
                  business (DSL) and host language       and host language
                + helpful when business code is        ̶ easier to grow out of
                  written by a separate team             control
                  (domain experts)
Language        + visual                             ̶ tools immaturity
Workbench       + rapid development                   ̶ vendor lock-in
                + IDE support for external DSL
What is an internal DSL?

 A (business) internal DSL is a
        fluent interface
built on top of a clearly defined
    Command & Query API
The Command & Query API
public interface Car {
    void setColor(Color color);
    void addEngine(Engine engine);
    void add Transmission(Transmission transmission);
}

public interface Engine {
    enum Type { FUEL, DIESEL, ELECTRIC, HYDROGEN, METHANE }
    void setType(Type type);
    void setPower(int power);
    void setCylinder(int cylinder);
}

public interface Transmission {
    enum Type { MANUAL, AUTOMATIC, CONTINOUSLY_VARIABLE }
    void setType(Type type);
    void setNumberOfGears(int gears);
}
Let's build a car
Car car = new CarImpl();
Car.setColor(Color.WHITE);

Engine engine1 = new EngineImpl();
engine1.setType(Engine.Type.FUEL);
engine1.setPower(73);
engine1.setCylinder(4);
car.addEngine(engine1);

Engine engine2 = new EngineImpl();
engine2.setType(Engine.Type.ELECTRIC);
engine2.setPower(60);
car.addEngine(engine2);

Transmission transmission = new TransmissionImpl();
transmission.setType(Transmission.Type.CONTINOUSLY_VARIABLE);
car.setTransmission(transmission);
Quite good …

… but you don't expect
a (non-technical)
domain expert to read
this, don't you?
"Domain users shouldn't
be writing code in our DSL
but it must be designed
for them to understand
and validate“
          Debasish Ghosh
Object Initialization
new CarImpl() {{
    color(Color.WHITE);            + clear hierarchic structure
    engine(new EngineImpl {{
        type(Engine.Type.FUEL);
        power(73);                       ̶ syntactial noise
        cylinder(4);
    }});
    engine(new EngineImpl {{           ̶ explicit use of constructors
        type(Engine.Type.FUEL);
        power(60);
    }});
    transmission(new TransmissionImpl {{
        type(Transmission.Type.CONTINOUSLY_VARIABLE);
    }});
}};                                 + small implementation effort
         ̶ unclear separation between Command API and DSL
Functions Sequence
car();
    color(Color.WHITE);
                               + lowest possible syntactic noise
    engine();
        type(Engine.Type.FUEL);
        power(73);               ̶ impossible to enforce right sequence
        cylinder(4);                of global function invocation
    engine();
        type(Engine.Type.ELECTRIC);
        power(60);                      ̶ use of global context variable(s)
    transmission();
        type(Transmission.Type.CONTINOUSLY_VARIABLE);
end();
                         + works well for defining the items of a (top level) list
     ̶ hierarchy defined only by identation convention
Methods Chaining
car()
    .color(Color.WHITE)          + object scoping
    .engine()
        .type(Engine.Type.FUEL)
        .power(73)
        .cylinder(4)       + method names act as keyword argument
    .engine()
        .type(Engine.Type.ELECTRIC)
        .power(60)
    .transmission()
                             + works good with optional parameter
        .type(Transmission.Type.CONTINOUSLY_VARIABLE)
.end();


    ̶ hierarchy defined only by identation convention
Nested Functions
car(
    color(Color.WHITE),              + no need for context variables
    transmission(
        type(Transmission.Type.CONTINOUSLY_VARIABLE),
    ),
    engine(                                ̶ higher punctuation noise
        type(Engine.Type.FUEL),
        power(73),                    ̶ arguments defined by position
        cylinder(4)                     rather than name
    ),
    engine(
        type(Engine.Type.ELECTRIC), ̶ rigid list of arguments or
        power(60)                            need of methods overloading
    )
);                            ̶ inverted evaluation order

        + hierarchic structure is echoed by function nesting
Is my DSL good
(concise, expressive, readable)
            enough?
              You cooked the meal …




    … now eat it!

DSL design is an iterative process
Mixed Strategy
car(                         nested function for top level object creation
    color(Color.WHITE),
    transmission()
        .type(Transmission.Type.CONTINOUSLY_VARIABLE),
    engine()
        .type(Engine.Type.FUEL)
        .power(73)
        .cylinder(4),          method chaining for arguments definition
    engine()
        .type(Engine.Type.ELECTRIC)
        .power(60)
);
                        function sequence for top level lists
car(
    ...
);
Advanced DSL examples
Hibernate Criteria Queries
List cats = session
        .createCriteria(Cat.class)
        .add( Restrictions.like("name", "Fritz%") )
        .add( Restrictions.between("weight", min, max) )
        .addOrder( Order.desc("age") )
        .setMaxResults(50)
        .list();
jMock
Turtle turtle = context.mock(Turtle.class);

// The turtle will be told to turn 45 degrees once only
oneOf(turtle).turn(45);

// The turtle can be told to flash its LEDs any number
// of times or not at all
allowing(turtle).flashLEDs();

// The turtle can be asked about its pen any number of times
// and will always return PEN_DOWN
allowing(turtle).queryPen();
will(returnValue(PEN_DOWN));

// The turtle will be told to stop at least once.
atLeast(1).of(turtle).stop();
lambdaj
  Plain Java version:
List<Car> automaticCars = new ArrayList<Car>();
for (Car car : cars) {
    if (car.getTransmission().getType() ==
                    Transmission.TYPE.AUTOMATIC)
        automaticCars.add(car);
}

  lambdaj version:

List<Sale> salesOfAFerrari = select(cars, having(
             on(Car.class).getTransmission().getType(),
             equalTo(Transmission.TYPE.AUTOMATIC)
));
Q                                                 A
Mario Fusco                          mario.fusco@gmail.com
Red Hat – Senior Software Engineer     twitter: @mariofusco

Real world DSL - making technical and business people speaking the same language

  • 1.
    Real world DSL making technical and business people speaking the same language by Mario Fusco Red Hat – Senior Software Engineer mario.fusco@gmail.com twitter: @mariofusco
  • 2.
    What is a DomainSpecific Language? A computer programming language of limited expressiveness focused on a particular domain
  • 3.
    Why use a DomainSpecific Language? 2 principles driving toward DSL development
  • 4.
    Communication is king The only purpose of languages, even programming ones IS COMMUNICATION
  • 6.
    Written once, readmany times Always code as if the person who will maintain your code is a maniac serial killer that knows where you live
  • 7.
    "Any fool canwrite code that a computer can understand. Good programmers write code that humans can understand“ Martin Fowler
  • 8.
    Pros & Consof DSLs + Expressivity  Communicativity + Conciseness + Readability  Maintainability  Modificability + Higher level of abstraction + Higher productivity in the specific problem domain ̶ Language design is hard ̶ Upfront cost ̶ Additional indirection layer  Performance concerns ̶ Lack of adeguate tool support ̶ Yet-another-language-to-learn syndrome
  • 9.
    DSL taxonomy  ExternalDSL  a language having custom syntactical rules separate from the main language of the application it works with  Internal DSL  a particular way of employing a general-purpose language, using only a small subset of the language's features  Language workbench  a specialized IDE for defining and building DSL, usually in a visual way, used both to determine the structure of the DSL and to provide an editing environment for people using it
  • 10.
    DSL Types Comparison + learning curve ̶ syntactic noise + cost of building ̶ needs to be recompiled Internal DSL + programmers familiarity (if host language is static) + IDE support (autocompletion …) + composability + flexibility ̶ need to learn of grammars + readability and language parsing External DSL + clear separation between ̶ boundary between DSL business (DSL) and host language and host language + helpful when business code is ̶ easier to grow out of written by a separate team control (domain experts) Language + visual ̶ tools immaturity Workbench + rapid development ̶ vendor lock-in + IDE support for external DSL
  • 11.
    What is aninternal DSL? A (business) internal DSL is a fluent interface built on top of a clearly defined Command & Query API
  • 12.
    The Command &Query API public interface Car { void setColor(Color color); void addEngine(Engine engine); void add Transmission(Transmission transmission); } public interface Engine { enum Type { FUEL, DIESEL, ELECTRIC, HYDROGEN, METHANE } void setType(Type type); void setPower(int power); void setCylinder(int cylinder); } public interface Transmission { enum Type { MANUAL, AUTOMATIC, CONTINOUSLY_VARIABLE } void setType(Type type); void setNumberOfGears(int gears); }
  • 13.
    Let's build acar Car car = new CarImpl(); Car.setColor(Color.WHITE); Engine engine1 = new EngineImpl(); engine1.setType(Engine.Type.FUEL); engine1.setPower(73); engine1.setCylinder(4); car.addEngine(engine1); Engine engine2 = new EngineImpl(); engine2.setType(Engine.Type.ELECTRIC); engine2.setPower(60); car.addEngine(engine2); Transmission transmission = new TransmissionImpl(); transmission.setType(Transmission.Type.CONTINOUSLY_VARIABLE); car.setTransmission(transmission);
  • 14.
    Quite good … …but you don't expect a (non-technical) domain expert to read this, don't you?
  • 15.
    "Domain users shouldn't bewriting code in our DSL but it must be designed for them to understand and validate“ Debasish Ghosh
  • 16.
    Object Initialization new CarImpl(){{ color(Color.WHITE); + clear hierarchic structure engine(new EngineImpl {{ type(Engine.Type.FUEL); power(73); ̶ syntactial noise cylinder(4); }}); engine(new EngineImpl {{ ̶ explicit use of constructors type(Engine.Type.FUEL); power(60); }}); transmission(new TransmissionImpl {{ type(Transmission.Type.CONTINOUSLY_VARIABLE); }}); }}; + small implementation effort ̶ unclear separation between Command API and DSL
  • 17.
    Functions Sequence car(); color(Color.WHITE); + lowest possible syntactic noise engine(); type(Engine.Type.FUEL); power(73); ̶ impossible to enforce right sequence cylinder(4); of global function invocation engine(); type(Engine.Type.ELECTRIC); power(60); ̶ use of global context variable(s) transmission(); type(Transmission.Type.CONTINOUSLY_VARIABLE); end(); + works well for defining the items of a (top level) list ̶ hierarchy defined only by identation convention
  • 18.
    Methods Chaining car() .color(Color.WHITE) + object scoping .engine() .type(Engine.Type.FUEL) .power(73) .cylinder(4) + method names act as keyword argument .engine() .type(Engine.Type.ELECTRIC) .power(60) .transmission() + works good with optional parameter .type(Transmission.Type.CONTINOUSLY_VARIABLE) .end(); ̶ hierarchy defined only by identation convention
  • 19.
    Nested Functions car( color(Color.WHITE), + no need for context variables transmission( type(Transmission.Type.CONTINOUSLY_VARIABLE), ), engine( ̶ higher punctuation noise type(Engine.Type.FUEL), power(73), ̶ arguments defined by position cylinder(4) rather than name ), engine( type(Engine.Type.ELECTRIC), ̶ rigid list of arguments or power(60) need of methods overloading ) ); ̶ inverted evaluation order + hierarchic structure is echoed by function nesting
  • 20.
    Is my DSLgood (concise, expressive, readable) enough? You cooked the meal … … now eat it! DSL design is an iterative process
  • 21.
    Mixed Strategy car( nested function for top level object creation color(Color.WHITE), transmission() .type(Transmission.Type.CONTINOUSLY_VARIABLE), engine() .type(Engine.Type.FUEL) .power(73) .cylinder(4), method chaining for arguments definition engine() .type(Engine.Type.ELECTRIC) .power(60) ); function sequence for top level lists car( ... );
  • 22.
  • 23.
    Hibernate Criteria Queries Listcats = session .createCriteria(Cat.class) .add( Restrictions.like("name", "Fritz%") ) .add( Restrictions.between("weight", min, max) ) .addOrder( Order.desc("age") ) .setMaxResults(50) .list();
  • 24.
    jMock Turtle turtle =context.mock(Turtle.class); // The turtle will be told to turn 45 degrees once only oneOf(turtle).turn(45); // The turtle can be told to flash its LEDs any number // of times or not at all allowing(turtle).flashLEDs(); // The turtle can be asked about its pen any number of times // and will always return PEN_DOWN allowing(turtle).queryPen(); will(returnValue(PEN_DOWN)); // The turtle will be told to stop at least once. atLeast(1).of(turtle).stop();
  • 25.
    lambdaj PlainJava version: List<Car> automaticCars = new ArrayList<Car>(); for (Car car : cars) { if (car.getTransmission().getType() == Transmission.TYPE.AUTOMATIC) automaticCars.add(car); } lambdaj version: List<Sale> salesOfAFerrari = select(cars, having( on(Car.class).getTransmission().getType(), equalTo(Transmission.TYPE.AUTOMATIC) ));
  • 26.
    Q A Mario Fusco mario.fusco@gmail.com Red Hat – Senior Software Engineer twitter: @mariofusco