0
Embedded Typesafe DSLs for Java Jevgeni Kabanov Tech Lead, ZeroTurnaround  jevgeni@zeroturnaround.com
ABOUT ME <ul><li>ZeroTurnaround Tech Lead </li></ul><ul><ul><li>Our flagship product is JavaRebel  - a class reloading JVM...
Based on <ul><li>Research a rticle “Embedded Typesafe Domain Specific Languages for Java” </li></ul><ul><ul><li>by Jevgeni...
OUTLINE <ul><li>DSLs and Fluent Interface </li></ul><ul><li>Case study: Building SQL queries </li></ul><ul><li>Case study:...
Domain Specific Language <ul><li>Small  sub-language  that has very little overhead when expressing domain specic data and...
Why bother? <ul><li>Low overhead </li></ul><ul><ul><li>You need to write less </li></ul></ul><ul><ul><li>It is easier to u...
Fluent Interface <ul><li>customer.newOrder()  .with(6, &quot;TAL&quot;)  .with(5, &quot;HPK&quot;).skippable()  .with(3, &...
Java 5 features and EDSLs <ul><li>Java 5 has  </li></ul><ul><ul><li>Parametric polymorphism (generics) </li></ul></ul><ul>...
BUILDING SQL QUERIES
SQL Example (4 errors) StringBuffer sql =  new   StringBuffer() ; sql.append( &quot;SELECT o.sum,(SELECT first_name,last_n...
Typesafe SQL Example Person p =  new   Person() ;   List<Tuple3<String, Integer, Date>> rows = new   QueryBuilder(datasour...
Tuples <ul><li>R eturn  tuples  that have precisely the selected data with types known ahead </li></ul><ul><li>Tuple types...
Tuple2 public   class   Tuple2<T1, T2>  implements   Tuple { public   final   T1  v1 ; public   final   T2  v2 ; public   ...
Typesafe SQL Example Person p =  new   Person() ;   List<Tuple3<String, Integer, Date>> rows = new   QueryBuilder(datasour...
Typesafe Metadata <ul><li>Metadata used by your DSL should include   compile-time type information. </li></ul><ul><li>We m...
Metadata dictionary public   class   Person   implements   Table   { public   String   getName() {   return   &quot;person...
Typesafe SQL Example Person p =  new   Person() ;   List<Tuple3<String, Integer, Date>> rows = new   QueryBuilder(datasour...
Restricting Syntax <ul><li>At any moment of time the DSL builder should have precisely the methods allowed in the current ...
QueryBuilder class   QueryBuilder   extends   Builder   { ... <T  extends   Table> FromBuilder<T> from(T table); }
FromBuilder class   FromBuilder<T  extends  Table> extends   Builder { ... <C1> SelectBuilder1<T, C1> select(Col<T, C1> c1...
SelectBuilder class   SelectBuilder2<T   extends   Table,C1,C2>  extends   SelectBuilder<T> { ... List<Tuple2<C1,C2>> list...
Typesafe SQL Example Person p =  new   Person() ;   List<Tuple3<String, Integer, Date>> rows = new   QueryBuilder(datasour...
Hierarchical Expressions <ul><li>Use </li></ul><ul><ul><li>method chaining when you need context  </li></ul></ul><ul><ul><...
Expression public   interface   Expression<E> { String getSqlString(); List<Object> getSqlArguments(); Class<E> getType(); }
Expressions class   Expressions { Expr<Bool> and(Expr<Bool>... e) <E> Expr<Bool> eq(Expr<E> e1, Expr<E> e2) Expr<Bool> lik...
Closures interface   Closure   {   void   apply(Builder b); } class   SelectBuilderC2<C1,C2> extends   SelectBuilder { Sel...
Unsafe Assumptions <ul><li>Allow the user to do type unsafe actions, but make sure he has to document his assumptions. </l...
Used Patterns <ul><li>Restricting syntax (builders allow  from ,  where  and  select  to be called only once) </li></ul><u...
Case Study 2: Typesafe Bytecode
Java  Class Definition Modifiers, name, super class, interfaces Enclosing class reference Annotation* Inner class* Name Fi...
Java Execution Model Local variables Operand stack Execution stack Frame Calling a method Throwing an exception
Instruction Example opcode argumen ts apply Operands stack when applying the instruction : Instruction :
Bytecode Engineering <ul><li>ASM – Java bytecode engineering library </li></ul><ul><ul><li>Visitor-based API </li></ul></u...
Hello, World! public   class   HelloWorld { public   static   void   main(String[] args) { System. out .println( &quot;Hel...
Hello, World! in Bytecode public   class   HelloWorld { public   <init>()V ALOAD 0 INVOKESPECIAL  Object.<init>()V RETURN ...
Hello, World! in ASM ClassWriter cw =  new   ClassWriter(0); MethodVisitor  mv; cw.visit( V1_6 ,  ACC_PUBLIC  +  ACC_SUPER...
Hello, World! in DSL
Possible mistakes <ul><li>Not enough stack elements for the instruction </li></ul><ul><li>Stack elements have the wrong ty...
Similar  Patterns <ul><li>Typesafe metadata (class literals) </li></ul><ul><li>Closures for mixing with the control flow <...
Different Patterns <ul><li>Restricting syntax  </li></ul><ul><ul><li>H ide methods that consume more stack slots than avai...
Tracking stack <ul><li>We generate classes MethodBuilderX, where X ranges from 0 to N </li></ul><ul><li>Each represents a ...
Tracking stack
Hello, World! in DSL
Type History <ul><li>You can accumulate type history as a type list and use it to reject actions that do not fit with that ...
Tracking stack types
Tracking stack types
Hello, World! in DSL
Unsafe Assumptions <ul><li>Allow the user to do type unsafe actions, but make sure he has to document his assumptions. </l...
Unsafe Assumptions
Reusable genSayHello() private   static   void   genSayHello(MethodBuilder0 mb) { mb .assumePush(String. class ) .getStati...
Questions
Upcoming SlideShare
Loading in...5
×

Embedded Typesafe Domain Specific Languages for Java

2,389

Published on

Published in: Technology, News & Politics
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
2,389
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
36
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Transcript of "Embedded Typesafe Domain Specific Languages for Java"

  1. 1. Embedded Typesafe DSLs for Java Jevgeni Kabanov Tech Lead, ZeroTurnaround jevgeni@zeroturnaround.com
  2. 2. ABOUT ME <ul><li>ZeroTurnaround Tech Lead </li></ul><ul><ul><li>Our flagship product is JavaRebel - a class reloading JVM plugin </li></ul></ul><ul><li>Double as the R&D lead of Webmedia, Ltd. </li></ul><ul><ul><li>Largest custom software developer in the Baltics </li></ul></ul><ul><li>Co-founder of Aranea Web Framework </li></ul><ul><li>Personal blog at dow.ngra.de </li></ul>
  3. 3. Based on <ul><li>Research a rticle “Embedded Typesafe Domain Specific Languages for Java” </li></ul><ul><ul><li>by Jevgeni Kabanov & Rein Raudjärv </li></ul></ul><ul><ul><li>Published at Principles and Practice of Programming in Java ' 08 </li></ul></ul>
  4. 4. OUTLINE <ul><li>DSLs and Fluent Interface </li></ul><ul><li>Case study: Building SQL queries </li></ul><ul><li>Case study: Java bytecode engineering </li></ul>
  5. 5. Domain Specific Language <ul><li>Small sub-language that has very little overhead when expressing domain specic data and behaviour </li></ul><ul><li>Can be </li></ul><ul><ul><li>A fully implemented language </li></ul></ul><ul><ul><li>A specialised API that looks like a sublanguage but still written using some general-purpose language – embedded DSL </li></ul></ul>
  6. 6. Why bother? <ul><li>Low overhead </li></ul><ul><ul><li>You need to write less </li></ul></ul><ul><ul><li>It is easier to understand </li></ul></ul><ul><ul><li>Domain experts can understand it </li></ul></ul><ul><li>Type safety </li></ul><ul><ul><li>You can be sure that certain errors won’t occur </li></ul></ul><ul><ul><li>You need to write less tests </li></ul></ul><ul><ul><li>You can use type info to add features </li></ul></ul>
  7. 7. Fluent Interface <ul><li>customer.newOrder() .with(6, &quot;TAL&quot;) .with(5, &quot;HPK&quot;).skippable() .with(3, &quot;LGV&quot;) .priorityRush(); </li></ul>
  8. 8. Java 5 features and EDSLs <ul><li>Java 5 has </li></ul><ul><ul><li>Parametric polymorphism (generics) </li></ul></ul><ul><ul><li>Static method import </li></ul></ul><ul><li>Java 5 doesn’t have </li></ul><ul><ul><li>Closures or first-class functions </li></ul></ul><ul><ul><li>Operator overloading </li></ul></ul><ul><ul><li>Variable/parameter type inference </li></ul></ul>
  9. 9. BUILDING SQL QUERIES
  10. 10. SQL Example (4 errors) StringBuffer sql = new StringBuffer() ; sql.append( &quot;SELECT o.sum,(SELECT first_name,last_name&quot; ); sql.append( &quot; FROM person p&quot; ); sql.append( &quot; WHERE o.person_id=p.id) AS client&quot; ); sql.append( &quot; FROM order o&quot; ); sql.append( &quot;WHERE o.id = &quot; +orderId); sql.append( &quot; AND o.status_code IN (?,?)&quot; ); PreparedStatement stmt = conn.prepareStatement(sql.toString()); stmt.setString(1, &quot;PAID&quot; ); //...
  11. 11. Typesafe SQL Example Person p = new Person() ; List<Tuple3<String, Integer, Date>> rows = new QueryBuilder(datasource) .from(p) .where( gt(p. height , 170)) .select(p. name , p. height , p. birthday ) .list(); for (Tuple3<String, Integer, Date> row : rows) { String name = row. v1 ; Integer height = row. v2 ; Date birthday = row. v3 ; System. out .println( name + &quot; &quot; + height + &quot; &quot; + birthday); }
  12. 12. Tuples <ul><li>R eturn tuples that have precisely the selected data with types known ahead </li></ul><ul><li>Tuple types are inferred from the select expression (column) types </li></ul>
  13. 13. Tuple2 public class Tuple2<T1, T2> implements Tuple { public final T1 v1 ; public final T2 v2 ; public Tuple2(T1 v1, T2 v2) { this . v1 = v1; this . v2 = v2; } public T1 v1() { return v1 ; } public T2 v2() { return v2 ; } }
  14. 14. Typesafe SQL Example Person p = new Person() ; List<Tuple3<String, Integer, Date>> rows = new QueryBuilder(datasource) .from(p) .where( gt(p. height , 170)) .select(p. name , p. height , p. birthday ) .list(); for (Tuple3<String, Integer, Date> row : rows) { String name = row. v1 ; Integer height = row. v2 ; Date birthday = row. v3 ; System. out .println( name + &quot; &quot; + height + &quot; &quot; + birthday); }
  15. 15. Typesafe Metadata <ul><li>Metadata used by your DSL should include compile-time type information. </li></ul><ul><li>We make use of pregenerated metadata dictionary that contains type information about tables and columns </li></ul>
  16. 16. Metadata dictionary public class Person implements Table { public String getName() { return &quot;person&quot; ; } ; public Column<Person, String> name = new Column<Person, String>( this , &quot;name&quot; , String . class ) ; public Column<Person, Integer> height = new Column<Person, Integer>( this , &quot;height&quot; , Integer. class ) ; public Column<Person, Date> birthday = new Column<Person, Date>( this , &quot;birthday&quot; , Date. class ) ; }
  17. 17. Typesafe SQL Example Person p = new Person() ; List<Tuple3<String, Integer, Date>> rows = new QueryBuilder(datasource) .from(p) .where( gt(p. height , 170)) .select(p. name , p. height , p. birthday ) .list(); for (Tuple3<String, Integer, Date> row : rows) { String name = row. v1 ; Integer height = row. v2 ; Date birthday = row. v3 ; System. out .println( name + &quot; &quot; + height + &quot; &quot; + birthday); }
  18. 18. Restricting Syntax <ul><li>At any moment of time the DSL builder should have precisely the methods allowed in the current state. </li></ul><ul><li>SQL query builders allow from , where and select to be called </li></ul><ul><ul><li>once and only once </li></ul></ul><ul><ul><li>only in valid order </li></ul></ul>
  19. 19. QueryBuilder class QueryBuilder extends Builder { ... <T extends Table> FromBuilder<T> from(T table); }
  20. 20. FromBuilder class FromBuilder<T extends Table> extends Builder { ... <C1> SelectBuilder1<T, C1> select(Col<T, C1> c1); <C1, C2> SelectBuilder2<T, C1, C2> select(Col<T, C1> c1, Col<T, C2> c2); ... }
  21. 21. SelectBuilder class SelectBuilder2<T extends Table,C1,C2> extends SelectBuilder<T> { ... List<Tuple2<C1,C2>> list(); ... }
  22. 22. Typesafe SQL Example Person p = new Person() ; List<Tuple3<String, Integer, Date>> rows = new QueryBuilder(datasource) .from(p) .where( gt(p. height , 170)) .select(p. name , p. height , p. birthday ) .list(); for (Tuple3<String, Integer, Date> row : rows) { String name = row. v1 ; Integer height = row. v2 ; Date birthday = row. v3 ; System. out .println( name + &quot; &quot; + height + &quot; &quot; + birthday); }
  23. 23. Hierarchical Expressions <ul><li>Use </li></ul><ul><ul><li>method chaining when you need context </li></ul></ul><ul><ul><li>static functions when you need hierarchy and extensibility </li></ul></ul>or( eq(p.name, &quot;Peter&quot;), gt(p.height, 170) )
  24. 24. Expression public interface Expression<E> { String getSqlString(); List<Object> getSqlArguments(); Class<E> getType(); }
  25. 25. Expressions class Expressions { Expr<Bool> and(Expr<Bool>... e) <E> Expr<Bool> eq(Expr<E> e1, Expr<E> e2) Expr<Bool> like(Expr<?> e, Expr<String> pattern) <E> Expr<E> constant(E value) Expr<String> concat(Expr... e) ... }
  26. 26. Closures interface Closure { void apply(Builder b); } class SelectBuilderC2<C1,C2> extends SelectBuilder { SelectBuilderC2<C1,C2> closure(Closure closure) { closure.apply( this ); return this ; } } }
  27. 27. Unsafe Assumptions <ul><li>Allow the user to do type unsafe actions, but make sure he has to document his assumptions. </li></ul>Expression<Integer> count = unchecked(Integer. class , &quot;util.countChildren(id)&quot; );
  28. 28. Used Patterns <ul><li>Restricting syntax (builders allow from , where and select to be called only once) </li></ul><ul><li>Typesafe metadata (data dictionary) </li></ul><ul><li>Hierarchical Expressions (with method chaining for main syntax) </li></ul><ul><li>Unsafe assumptions (unchecked expressions declare the expected type) </li></ul><ul><li>Closures for mixing with the control flow </li></ul>
  29. 29. Case Study 2: Typesafe Bytecode
  30. 30. Java Class Definition Modifiers, name, super class, interfaces Enclosing class reference Annotation* Inner class* Name Field* Modifiers, name, type Annotation* Method* Modifiers, name, return and parameter types Annotation* Compiled code
  31. 31. Java Execution Model Local variables Operand stack Execution stack Frame Calling a method Throwing an exception
  32. 32. Instruction Example opcode argumen ts apply Operands stack when applying the instruction : Instruction :
  33. 33. Bytecode Engineering <ul><li>ASM – Java bytecode engineering library </li></ul><ul><ul><li>Visitor-based API </li></ul></ul><ul><ul><li>Tree-based API </li></ul></ul><ul><li>Completely untyped </li></ul><ul><li>Produced bytecode is only verified at runtime </li></ul>
  34. 34. Hello, World! public class HelloWorld { public static void main(String[] args) { System. out .println( &quot;Hello, World!&quot; ); } }
  35. 35. Hello, World! in Bytecode public class HelloWorld { public <init>()V ALOAD 0 INVOKESPECIAL Object.<init>()V RETURN public static main([LString;)V GETSTATIC System.out : LPrintStream; LDC &quot;Hello, World!&quot; INVOKEVIRTUAL PrintStream.println(LString;)V RETURN }
  36. 36. Hello, World! in ASM ClassWriter cw = new ClassWriter(0); MethodVisitor mv; cw.visit( V1_6 , ACC_PUBLIC + ACC_SUPER , &quot;HelloWorld&quot; , null , &quot;java/lang/Object&quot; , null ); { mv = cw.visitMethod( ACC_PUBLIC + ACC_STATIC , &quot;main&quot; , &quot;([Ljava/lang/String;)V&quot; , null , null ); mv.visitCode(); mv.visitFieldInsn( GETSTATIC , &quot;java/lang/System&quot; , &quot;out&quot; , &quot;Ljava/io/PrintStream;&quot; ); mv.visitLdcInsn( &quot;Hello, World!&quot; ); mv.visitMethodInsn( INVOKEVIRTUAL , &quot;java/io/PrintStream&quot; , &quot;println&quot; , &quot;(Ljava/lang/String;)V&quot; ); mv.visitInsn( RETURN ); mv.visitMaxs(2, 1); mv.visitEnd(); } cw.visitEnd();
  37. 37. Hello, World! in DSL
  38. 38. Possible mistakes <ul><li>Not enough stack elements for the instruction </li></ul><ul><li>Stack elements have the wrong type </li></ul><ul><li>Local variables have the wrong type </li></ul><ul><li>Using illegal modifiers or opcodes </li></ul>
  39. 39. Similar Patterns <ul><li>Typesafe metadata (class literals) </li></ul><ul><li>Closures for mixing with the control flow </li></ul>
  40. 40. Different Patterns <ul><li>Restricting syntax </li></ul><ul><ul><li>H ide methods that consume more stack slots than available </li></ul></ul><ul><li>Unsafe assumptions </li></ul><ul><ul><li>Deprecate methods instead of omitting them </li></ul></ul><ul><ul><li>Allow assuming stack slot and local variable types </li></ul></ul>
  41. 41. Tracking stack <ul><li>We generate classes MethodBuilderX, where X ranges from 0 to N </li></ul><ul><li>Each represents a state with X stack slots initialized </li></ul><ul><li>Stack consuming operations are only available in builders which have enough stack slots initialized </li></ul>
  42. 42. Tracking stack
  43. 43. Hello, World! in DSL
  44. 44. Type History <ul><li>You can accumulate type history as a type list and use it to reject actions that do not fit with that history </li></ul><ul><li>In addition to tracking the current stack size we track each stack slot type using a list of generic type variables </li></ul>
  45. 45. Tracking stack types
  46. 46. Tracking stack types
  47. 47. Hello, World! in DSL
  48. 48. Unsafe Assumptions <ul><li>Allow the user to do type unsafe actions, but make sure he has to document his assumptions. </li></ul>
  49. 49. Unsafe Assumptions
  50. 50. Reusable genSayHello() private static void genSayHello(MethodBuilder0 mb) { mb .assumePush(String. class ) .getStatic(System. class , &quot;out&quot; , PrintStream . class ) .swap() .invoke() .param(String. class ) .virtVoid(PrintStream. class , &quot;println&quot; ); }
  51. 51. Questions
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×