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


Published on

Published in: Technology, Business

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

  1. 1. Real world DSL making technical and business people speakingthe same languageby Mario FuscoRed Hat – Senior Software Engineermario.fusco@gmail.comtwitter: @mariofusco
  2. 2. What is aDomain Specific Language? Acomputer programming language of limited expressiveness focused on a particular domain
  3. 3. Why use aDomain Specific Language?2 principles driving toward DSL development
  4. 4. Communication is king The only purpose of languages, even programming ones IS COMMUNICATION
  5. 5. Written once, read many times Always code as if the person who will maintain yourcode is a maniac serial killer that knows where you live
  6. 6. "Any fool can write codethat a computer canunderstand.Good programmerswrite code that humanscan understand“ Martin Fowler
  7. 7. 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
  8. 8. 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 languages 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
  9. 9. DSL Types Comparison + learning curve ̶ syntactic noise + cost of building ̶ needs to be recompiledInternal DSL + programmers familiarity (if host language is static) + IDE support (autocompletion …) + composability + flexibility ̶ need to learn of grammars + readability and language parsingExternal 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 immaturityWorkbench + rapid development ̶ vendor lock-in + IDE support for external DSL
  10. 10. What is an internal DSL? A (business) internal DSL is a fluent interfacebuilt on top of a clearly defined Command & Query API
  11. 11. 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);}
  12. 12. Lets 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);
  13. 13. Quite good …… but you dont expecta (non-technical)domain expert to readthis, dont you?
  14. 14. "Domain users shouldntbe writing code in our DSLbut it must be designedfor them to understandand validate“ Debasish Ghosh
  15. 15. Object Initializationnew 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
  16. 16. Functions Sequencecar(); 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
  17. 17. Methods Chainingcar() .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
  18. 18. Nested Functionscar( 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
  19. 19. Is my DSL good(concise, expressive, readable) enough? You cooked the meal … … now eat it!DSL design is an iterative process
  20. 20. Mixed Strategycar( 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 listscar( ...);
  21. 21. Advanced DSL examples
  22. 22. Hibernate Criteria QueriesList cats = session .createCriteria(Cat.class) .add("name", "Fritz%") ) .add( Restrictions.between("weight", min, max) ) .addOrder( Order.desc("age") ) .setMaxResults(50) .list();
  23. 23. jMockTurtle turtle = context.mock(Turtle.class);// The turtle will be told to turn 45 degrees once onlyoneOf(turtle).turn(45);// The turtle can be told to flash its LEDs any number// of times or not at allallowing(turtle).flashLEDs();// The turtle can be asked about its pen any number of times// and will always return PEN_DOWNallowing(turtle).queryPen();will(returnValue(PEN_DOWN));// The turtle will be told to stop at least once.atLeast(1).of(turtle).stop();
  24. 24. 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)));
  25. 25. Q AMario Fusco mario.fusco@gmail.comRed Hat – Senior Software Engineer twitter: @mariofusco