Download - DSL - Domain Specific Languages, Chapter 4, Internal DSL

Transcript
Page 1: DSL - Domain Specific Languages,  Chapter 4, Internal DSL

Domain Specific Languages

Hiro Yoshioka March 15th, 2013 Rakuten Tower 2, Shinagawa, Tokyo, Japan

Page 2: DSL - Domain Specific Languages,  Chapter 4, Internal DSL

2

Chapter 4, Internal DSL

•  Unlike  external  DSLs,  you  don’t  need  to  learn  about  grammars  and  language  parsing.  

•  Constrain  –  host  language  expression  •  Ruby  •  Lisp  

•  fluent  interface  vs  API  

Page 3: DSL - Domain Specific Languages,  Chapter 4, Internal DSL

3

4.1 Fluent and Command-Query APIs

•  Method  Chaining      Processor  p  =  new  Processor(2,  2500,  Processor.Type.i386);  Disk  d1  =  new  Disk(150,  Disk.UNKNOWN_SPEED,  null);  Disk  d2  =  new  Disk(75,  7200,  Disk.Interface.SATA);  return  new  Computer(p,  d1,  d2);      Method  Chaining  computer()    .processor()    .cores(2)    .speed(2500)    .i386()    .disk()    .size(150)    .disk()    .size(75)    .speed(7200)    .sata()    .end();      

Page 4: DSL - Domain Specific Languages,  Chapter 4, Internal DSL

4

4.1 Fluent and Command-Query APIs

•  Func]on  Sequence  computer()  processor();    cores(2);    speed(2500);    i386();    disk();    size(150);    disk();    size(75);    speed(7200);    sata();      

Page 5: DSL - Domain Specific Languages,  Chapter 4, Internal DSL

5

4.1 Fluent and Command-Query APIs

Page 6: DSL - Domain Specific Languages,  Chapter 4, Internal DSL

6

4.1 Fluent and Command-Query APIs

•  Command-­‐query  separa]on  •  Command:  may  change  state,  but  not  

return  value  •  Query:  does  not  change  state  •  Name  –  without  context  

•  Fluent:  Name  –  context  is  important  

Page 7: DSL - Domain Specific Languages,  Chapter 4, Internal DSL

7

4.2 The Need for a Parsing Layer

•  Expression  Builder  •  input:  fluent  interface  •  output:  a  sequence  of  command-­‐query  API  

•  Seman]c  model  •  Separa]ng  the  Seman]c  model  from  

Expression  Builders  •  You  can  test  them  independently.  

Page 8: DSL - Domain Specific Languages,  Chapter 4, Internal DSL

8

4.3 Using Functions

•  func]on  –  most  successful  packaging,  (also  called  subrou]ne,  procedure,  method)  

computer();    processor();      cores(2);      speed(2500);      i386();    disk();    size(150);    disk();    size(75);    speed(7200);    sata();      

Page 9: DSL - Domain Specific Languages,  Chapter 4, Internal DSL

9

4.3 Using Functions •  Method  Chaining  and  Func]on  Sequence  

•  Scope  of  the  func]ons  •  Func]on  Sequence:  ensure  the  func]ons  

resolve  properly  •  global  func]on  –  complica]ng  

namespace  and  introducing  global  variables  for  parsing  data.  •  Context  Variables  

•  Method  Chaining  –  avoids  global  •  object  scoping  –  avoid  globalness,  

extensibility  •  nested  func]on  –  avoid  context  

variables  

Page 10: DSL - Domain Specific Languages,  Chapter 4, Internal DSL

10

4.3 Using Functions

•  nested  func]on  •  combines  func]ons  by  making  func]on  

calls  arguments  in  higher  level  func]on  calls.  

•  reflects  the  logical  syntax  tree  of  the  DSL  •  change  in  evalua]on  order  •  “(“,  “)”,  “,”  are  explicit.  noise  –  Lisp  

•  (third(second(first)))  **  I  found  typo  

computer(    processor(          cores(2),          speed(2500),          i386  ),    disk(          size(150)    ),  disk(          size(75),          speed(7200),        SATA      )  );  

Page 11: DSL - Domain Specific Languages,  Chapter 4, Internal DSL

11

4.3 Using Functions

•  nested  func]on  •  the  hierarchic  structure  of  the  

configura]on  is  echoed  by  the  language  constructs  themselves    

•  reflects  the  logical  syntax  tree  of  the  DSL  •  evalua]on  order  •  arguments  are  iden]fied  by  posi]on  rather  

than  name  

Page 12: DSL - Domain Specific Languages,  Chapter 4, Internal DSL

12

4.3 Using Functions

•  hybrid  •  It  uses  Func]on  Sequence,  each  computer  

func]on  uses  Nested  Func]on,  each  processor  and  disk  is  using  Method  Chaining.  

•  Advantages:    •  Func]on  Sequence:  each  computer  

defini]on  well  separated.  •  Nested  Func]on:  eliminates  a  Context  

Variable.  •  Method  Chaining:  mul]ple  op]onal  

arguments  

computer(      processor()          .cores(2)          .speed(2500)          .type(i386),      disk()          .size(150),      disk()          .size(75)          .speed(7200)          .iface(SATA)  );  computer(      processor()            .cores(4)  );  

Page 13: DSL - Domain Specific Languages,  Chapter 4, Internal DSL

13

4.3 Using Functions

•  hybrid  •  problems  –  punctua]onal  confusion    

•  comma,  periods,  semicolons,  …  

Page 14: DSL - Domain Specific Languages,  Chapter 4, Internal DSL

14

4.4 Literal Collections

•  literal  list  

-­‐-­‐  Java  or  C#  computer(    processor  (...),    disk(...),    disk(...)    );      -­‐-­‐  Ruby  computer  [    processor(...),    disk(...),    disk(...)    ]      

Page 15: DSL - Domain Specific Languages,  Chapter 4, Internal DSL

15

4.4 Literal Collections

•  literal  map  •  named  parameters  •  symbol  

•  immutable,  symbol  lookup  •  :symbol  (ruby  syntax)  

-­‐-­‐  Ruby  computer(processor(:cores  =>  2,  :type  =>  :i386),                                        disk(:size  =>  150),                                        disk(:size  =>  75,  :speed  =>  7200,  :interface  =>  :sata))    -­‐-­‐  Ruby  2.0  has  a  keyword  arguments      

Page 16: DSL - Domain Specific Languages,  Chapter 4, Internal DSL

16

4.4 Literal Collections

•  symbol  

-­‐-­‐  Ruby  computer(processor(:cores  =>  2,  :type  =>  :i386),                                        disk(:size  =>  150),                                        disk(:size  =>  75,  :speed  =>  7200,  :interface  =>  :sata))      

Page 17: DSL - Domain Specific Languages,  Chapter 4, Internal DSL

17

4.5 Using Grammars to Choose Internal Elements

Structure   BNF   Consider  

Mandatory  list   parent  ::=  first  second  third     Nested  Func+on  (357)    

Op]onal  list       parent  ::=  first  maybeSecond?  maybeThird?    

Method  Chaining  (373),  Literal  Map  (419)    

Homogenous  bag   parent  ::=  child*     Literal  List  (417),    Func+on  Sequence  (351)    

Hetrogenous  bag     parent  ::=  (this  |  that  |  theOther)*     Method  Chaining    

Set     n/a     Literal  Map    

Page 18: DSL - Domain Specific Languages,  Chapter 4, Internal DSL

18

4.6 Closures

•  lambdas,  blocks,  anonymous  func]ons  

#ruby...  ComputerBuilder.build  do  |c|      c.processor  do  |p|          p.cores  2          p.i386          p.speed  2.2      end      c.disk  do  |d|          d.size  150        end      c.disk  do  |d|        d.size  75        d.speed  7200        d.sata      end  end  

Page 19: DSL - Domain Specific Languages,  Chapter 4, Internal DSL

19

4.6 Closures

•  Nested  Closures  •  inline  nes]ng  •  deferred  evalua]on  •  limited-­‐scope  variables  

#ruby...  ComputerBuilder.build  do  |c|      c.processor  do  |p|          p.cores  2          p.i386          p.speed  2.2      end      c.disk  do  |d|          d.size  150        end      c.disk  do  |d|        d.size  75        d.speed  7200        d.sata      end  end  

Page 20: DSL - Domain Specific Languages,  Chapter 4, Internal DSL

20

4.7 Parse Tree Manipulation

•  parse  tree  manipula]on  

ptg9438845

BinaryExpression

MemberAccess

Left

ConstantExpression

Right

Age

Member

aPerson

Expression

18

Value

Figure 4.2 A parse tree representation of aPerson.Age > 18

query in another query language, such as SQL. This is essentially what .NET’sLinq language does. Linq allows you to express many SQL queries in C#, whichmany programmers prefer.

The strength of Parse Tree Manipulation is in allowing you to write expressionsin the host language that can then be converted into different expressions thatpopulate the Semantic Model (159) in ways that are beyond just storing the closureitself.

In C#’s case above, this manipulation is done with an object model representa-tion of the parse tree. In Lisp’s case, this manipulation is done by macro transfor-mations on Lisp source code. Lisp is well suited to this because the structure ofits source code is very close to that of a syntax tree. Parse Tree Manipulation ismore widely used in Lisp for DSL work—so much so that Lispers often wail atthe lack of macros in other languages. My view is that manipulating an objectmodel of the parse tree in C# style is a more effective way of doing Parse TreeManipulation than Lisp macros—although this may be due to my lack of practicewith Lisp’s macro processing.

Whatever mechanism you use, the next question is how important Parse TreeManipulation is as a technique for DSLs. One very prominent use is in Linq—aMicrosoft technology that allows you to express query conditions in C# and turnthem into different query languages for various target data structures. In thisway, one C# query can be turned into SQL for relational databases and XPathfor XML structures, or kept in C# for in-memory C# structures. It’s essentiallya mechanism that allows application code to do runtime code translation,generating arbitrary code from C# expressions.

Parse Tree Manipulation is a powerful, but somewhat complex, technique thathasn’t been supported much by languages in the past, but these days has beengetting a lot more attention due to its support in C# 3 and Ruby. Since it’s rela-tively new (at least outside the Lisp world), it’s hard to evaluate how truly usefulit is. My current perception is that it’s a marginal technique—something that israrely needed but very handy on the occasions when that need arises. Translating

4.7 Parse Tree Manipulation 83

4: Implementingan Internal DSL

From the Library of Hiro Yoshioka

Page 21: DSL - Domain Specific Languages,  Chapter 4, Internal DSL

21

4.8 Annotation

•  Auribute  in  C#  

class  Person  

       validates_length_of  :last_name,  :maximum  =>  30  

Page 22: DSL - Domain Specific Languages,  Chapter 4, Internal DSL

22

4.9 Literal Extension

•  Method  Chaining  •  chain  begins  on  a  literal  integer,    

e.g.  5.days.ago  •  add  methods  to  external  library  classes  •  Cons:  methods  globally  

Page 23: DSL - Domain Specific Languages,  Chapter 4, Internal DSL

23

4.10 Reducing the Syntactic Noise

•  Textual  Polishing  

Page 24: DSL - Domain Specific Languages,  Chapter 4, Internal DSL

24

4.11 Dynamic Reception

•  handle  an  unexpected  call  by  run]me  to  a  special  method.  programmers  can  override  the  method  to  do  other  things.  •  method_missing  in  Ruby,  •  doesNotUnderstand  in  Smalltalk  

•  e.g.,  Rails  Ac]ve  record’s  dynamic  finders  

Page 25: DSL - Domain Specific Languages,  Chapter 4, Internal DSL

25

4.12 Providing Some Type Checking

•  sta]c  type  checking  or  not  •  type  checking  at  compile  ]me  or  run  ]me  •  modern  IDEs  provide  excellent  support  based  

on  sta]c  typing  

Page 26: DSL - Domain Specific Languages,  Chapter 4, Internal DSL

26

Yoshioka