real world dsl - making technical and business people speaking the same language
DESCRIPTION
TRANSCRIPT
by Mario FuscoRed Hat – Senior Software [email protected]: @mariofusco
Real world
DSLmaking
technical and business people
speaking the same language
What is aDomain Specific Language?
A computer programming language
of limited expressiveness
focused on a particular domain
computer programming language
limited expressiveness
particular domain
2 principles driving toward DSL development
Why use aDomain Specific Language?
The only purpose of languages,
even programming onesIS COMMUNICATION
Communication is king
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
Internal DSL
External DSL
LanguageWorkbench
DSL Types Comparison+ learning curve+ cost of building+ programmers familiarity+ IDE support (autocompletion …)+ composability
+ flexibility+ readability+ clear separation between business (DSL) and host language+ helpful when business code is written by a separate team (domain experts)
� syntactic noise� needs to be recompiled (if host language is static)
� need to learn of grammars and language parsing� boundary between DSL and host language� easier to grow out of control
+ visual+ rapid development+ IDE support for external DSL
� tools immaturity�� vendor lock-in
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 APIpublic 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 carCar 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);
… but you don't expect a (non-technical) domain expert to read this, don't you?
Quite good …
"Domain users shouldn't be writing code in our DSL but it must be designed for them to understand and validate“
Debasish Ghosh
Object Initializationnew CarImpl() {{ color(Color.WHITE); engine(new EngineImpl {{ type(Engine.Type.FUEL); power(73); cylinder(4); }}); engine(new EngineImpl {{ type(Engine.Type.FUEL); power(60); }}); transmission(new TransmissionImpl {{ type(Transmission.Type.CONTINOUSLY_VARIABLE); }});}};
� syntactial noise
� explicit use of constructors
+ clear hierarchic structure
+ small implementation effort
� unclear separation between Command API and DSL
Functions Sequencecar(); color(Color.WHITE); engine(); type(Engine.Type.FUEL); power(73); cylinder(4); engine(); type(Engine.Type.ELECTRIC); power(60); transmission(); type(Transmission.Type.CONTINOUSLY_VARIABLE);end();
+ works well for defining the items of a (top level) list
� use of global context variable(s)
� hierarchy defined only by identation convention
+ lowest possible syntactic noise
� impossible to enforce right sequence of global function invocation
Methods Chainingcar() .color(Color.WHITE) .engine() .type(Engine.Type.FUEL) .power(73) .cylinder(4) .engine() .type(Engine.Type.ELECTRIC) .power(60) .transmission() .type(Transmission.Type.CONTINOUSLY_VARIABLE).end();
+ method names act as keyword argument
� hierarchy defined only by identation convention
+ object scoping
+ works good with optional parameter
Nested Functionscar( color(Color.WHITE), transmission( type(Transmission.Type.CONTINOUSLY_VARIABLE), ), engine( type(Engine.Type.FUEL), power(73), cylinder(4) ), engine( type(Engine.Type.ELECTRIC), power(60) ));
+ no need for context variables
� higher punctuation noise
+ hierarchic structure is echoed by function nesting
� inverted evaluation order
� arguments defined by position rather than name
� rigid list of arguments or need of methods overloading
Is my DSL good (concise, expressive, readable)
enough?You cooked the meal …
… now eat it!
DSL design is an iterative process
Mixed Strategycar( color(Color.WHITE), transmission() .type(Transmission.Type.CONTINOUSLY_VARIABLE), engine() .type(Engine.Type.FUEL) .power(73) .cylinder(4), engine() .type(Engine.Type.ELECTRIC) .power(60));car( ...);
function sequence for top level lists
method chaining for arguments definition
nested function for top level object creation
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();
jMockTurtle 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)
));