Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Domain Specific Languages -   a  user  view and an  implementation  view does expressiveness imply a compromise on the und...
Debasish Ghosh @debasishg on twitter Code @  http://github.com/debasishg   Blog @  Ruminations of a Programmer http:// deb...
Open Source Footprints <ul><li>Committer in Akka ( http:// akka.io )  </li></ul><ul><li>Owner/Founder of : </li></ul><ul><...
<ul><li>model-view architecture of a DSL </li></ul><ul><li>how abstractions shape a DSL structure </li></ul><ul><li>choosi...
<ul><li>model-view  architecture of a DSL </li></ul><ul><li>how abstractions shape a DSL structure </li></ul><ul><li>choos...
View Or Syntax  new_trade  'T-12435'   for account  'acc-123'   to buy  100  shares of  'IBM' ,  at UnitPrice =  100 ,  Pr...
view / syntax is derived from model
view / syntax is a veneer of abstraction over model
view / syntax is decoupled thru an interpreter from model
view / syntax can be multiple over the same model
<ul><li># create instrument </li></ul><ul><li>instrument = Instrument.new( 'Google' ) </li></ul><ul><li>instrument.quantit...
<ul><li>new_trade  'T-12435'   </li></ul><ul><li>for account  'acc-123'   </li></ul><ul><li>to buy  100  shares of  'IBM' ...
<ul><li>new_trade  'T-12435'   </li></ul><ul><li>for account   'acc-123'   </li></ul><ul><li>to buy   100   shares of   'I...
domain syntax idiomatic Ruby model interpreter isolation layer between the  model and the view of the DSL
<ul><li>def  parse(dsl_string) </li></ul><ul><li>dsl = dsl_string.clone </li></ul><ul><li>dsl.gsub!(/=/,  '=>' ) </li></ul...
<ul><li>Not all transformations can be done using simple regular expression based string processing  </li></ul>
<ul><li>model-view architecture of a DSL </li></ul><ul><li>how  abstractions  shape a DSL structure </li></ul><ul><li>choo...
… … abstractions of  the core domain model layers of  abstractions
… … abstractions of  the core domain model abstractions that glue model elements layers of  abstractions combinators, faça...
… … abstractions of  the core domain model abstractions that glue model elements layers of  abstractions combinators, faça...
… … abstractions of  the core domain model abstractions that glue model elements layers of  abstractions combinators, faça...
layers of  abstractions .. <ul><li>keep the underlying domain model clean </li></ul><ul><li>ensure separation of concerns ...
withAccount(trade) {  account => { settle(trade using account.getClient .getStandingRules .filter(_.account == account) .f...
withAccount( trade ) {  account  => { settle (trade using account.getClient .getStandingRules .filter(_.account == account...
withAccount ( trade ) {  account  => { settle (trade  using account.getClient .getStandingRules .filter(_.account == accou...
<ul><li>We need  abstractions  that generalize over computations .. </li></ul>
Our domain – stocks, trades, bulls, bears .. <ul><li>Security trade </li></ul><ul><ul><li>Exchange of securities and curre...
Our domain – stocks, trades, bulls, bears .. <ul><li>Security trade </li></ul><ul><ul><li>Exchange of securities and curre...
// The Trade model case class   Trade ( account:  Account ,  instrument:  Instrument ,  referenceNo:  String ,  market:  M...
// various tax/fees to be paid when u do a trade sealed trait   TaxFeeId case object   TradeTax   extends   TaxFeeId case ...
// computes value date of a trade val  valueDateProcessor:  Trade  =>  Trade  =  //.. impl // computes tax/fee of a trade ...
val  trades =  //.. list of security trades // get a list of value dates for all trades   val  valueDates = trades map (_....
val trades = //.. list of security trades // get a list of value dates for all trades  val valueDates =   trades   map (_....
val trades = //.. list of security trades // get a list of value dates for all trades  val valueDates = trades   map   (_....
val  trades =  //.. list of security trades // map across the list functor val  valueDates = trades map (_.valueDate) // m...
<ul><li>a  map  is .. </li></ul><ul><li>a combinator </li></ul>
<ul><li>a  map  .. </li></ul><ul><li>pimps abstractions to give us an ad hoc polymorphism implementation on top of OO </li...
<ul><li>a  map  is .. </li></ul><ul><li>fundamentally powered by type classes </li></ul>
sealed trait   MA [ M [_],  A ]  extends   PimpedType [ M [ A ]] { def  map[ B ](f:  A  =>  B )( implicit  t:  Functor [ M...
trait  Functor[ F [ _ ]]  extends   InvariantFunctor [ F ] {    def  fmap[ A ,  B ](r:  F [ A ], f:  A  =>  B ):  F [ B ] ...
a  functor  is .. a type class for all things that can be mapped over
// map across the Function1 functor   val  richTrade = (valueDateProcessor map  taxFeeProcessor map  netAmountProcessor) a...
the idea is to  generalize  abstractions .. .. and by generalizing  map  over functors we get a  combinator that can glue ...
semantic model + combinators = DSL
<ul><li>model-view architecture of a DSL </li></ul><ul><li>how abstractions shape a DSL structure </li></ul><ul><li>choosi...
<ul><li>a  DSL  .. </li></ul><ul><li>evolves from the model non-invasively </li></ul><ul><li>using combinators </li></ul><...
so, a domain model also has to be .. designed for compositionality Image source: Mathematical art of M. C. Escher  ( http:...
domain  rule .. <ul><li>enrichment of trade is done in steps: </li></ul><ul><li>get the tax/fee ids for a trade </li></ul>...
the  DSL  .. val  enrich =  for  { // get the tax/fee ids for a trade    taxFeeIds <- forTrade // calculate tax fee values...
the  DSL  .. val enrich =  for { // get the tax/fee ids for a trade    taxFeeIds <-   forTrade // calculate tax fee values...
reiterating .. a  combinator  acts .. as a glue combining model elements But ..
there is a  prerequisite  .. model elements should be composable we call this the  compositionality  of the model
Q : How to design your model  so that it's  compositional  ? A : choose the right  abstractions  .. depending on the progr...
domain  rule .. <ul><li>net cash value calculation of trade: </li></ul><ul><li>use strategy for specific markets, if avail...
the  DSL  .. // type alias for domain vocabulary type   NetAmount   =   BigDecimal type   CashValueCalculationStrategy   =...
the  DSL  .. val   cashValue :  Trade   =>   CashValueCalculationStrategy  =>  NetAmount  =  {   trade  =>  pf  =>     if ...
<ul><li>PartialFunction  is .. </li></ul><ul><li>the secret sauce </li></ul><ul><li>all functions for specific and default...
val  forHongKong:  CashValueCalculationStrategy  = {    case  HongKong => { trade => //.. } } val  forSingapore:  CashValu...
<ul><li>model-view architecture of a DSL </li></ul><ul><li>how abstractions shape a DSL structure </li></ul><ul><li>choosi...
choice of abstractions depends on the  programming language  chosen  for implementation of your DSL always keep an eye on ...
domain  rule .. A trade needs to be enriched with a set of tax and fees before we calculate its net cash value
the  Java  way   .. // use the decorator pattern new  CommissionDecorator( new  TaxFeeDecorator(new Trade(..))); public cl...
the  Java  way   .. <ul><li>decorators and the decoratee share a common interface </li></ul><ul><li>statically coupled thr...
the  Ruby  way   .. ## decorator pattern Trade . new (..).with  TaxFee ,  Commission class   Trade attr_accessor  .. def  ...
the  Ruby  way   .. ## mixins module   TaxFee ## calculate taxfee def   value super   + .. end end ## mixins module   Comm...
the  Ruby  way   .. <ul><li>more dynamic compared to Java </li></ul><ul><li>uses Ruby idioms of runtime meta-programming <...
if we were to do the same in  Clojure  .. ## decorator pattern (with-tax-fee trade (with-values :tax  12 ) (with-values :c...
the  Clojure  way   .. <ul><li>more functional – uses HOF </li></ul><ul><li>function threading makes the decorator impleme...
<ul><li>model-view architecture of a DSL </li></ul><ul><li>how abstractions shape a DSL structure </li></ul><ul><li>choosi...
if you thought DSLs grow through c omposition of abstractions  ..  .. then the final DSL is the  whole  formed from the co...
…  and if the final DSL is also an abstraction .. .. there's no reason we can compose it with  another abstraction  (aka a...
trait   TradeDsl  { type   T  <:  Trade def  enrich:  PartialFunction [ T ,  T ] //.. other methods } constrained abstract...
trait   FixedIncomeTradeDsl   extends   TradeDsl  { type   T  =  FixedIncomeTrade import   FixedIncomeTradingService ._ ov...
trait   EquityTradeDsl   extends   TradeDsl  { type   T  =  EquityTrade import   EquityTradingService ._ override def  enr...
.. and now a new market rule comes up that needs to be applied to  all  types of trades .. you  don’t want to affect the c...
.. design the DSL for the new rule in such a way that you can  plug in  the semantics of  all types of trade  within it ..
and now the rule itself .. “ Any trade on the New York Stock Exchange of principal value > 10,000 USD must have a discount...
trait   MarketRuleDsl   extends   TradeDsl  { val  semantics:  TradeDsl type   T  = semantics. T override def  enrich:  Pa...
trait MarketRuleDsl extends TradeDsl { val   semantics :  TradeDsl type T = semantics.T override def enrich: PartialFuncti...
object  EquityTradeMarketRuleDsl  extends   MarketRuleDsl  { val  semantics = EquityTradeDsl } object  FixedIncomeTradeMar...
import  FixedIncomeTradeMarketRuleDsl._ withTrade( 200  discount_bonds  IBM for_client  NOMURA on NYSE at  72 .ccy( USD ))...
Summary <ul><li>A DSL has a syntax (for the user) and a model (for the implementer) </li></ul><ul><li>The syntax is the vi...
 
Upcoming SlideShare
Loading in …5
×

of

DSL - expressive syntax on top of a clean semantic model Slide 1 DSL - expressive syntax on top of a clean semantic model Slide 2 DSL - expressive syntax on top of a clean semantic model Slide 3 DSL - expressive syntax on top of a clean semantic model Slide 4 DSL - expressive syntax on top of a clean semantic model Slide 5 DSL - expressive syntax on top of a clean semantic model Slide 6 DSL - expressive syntax on top of a clean semantic model Slide 7 DSL - expressive syntax on top of a clean semantic model Slide 8 DSL - expressive syntax on top of a clean semantic model Slide 9 DSL - expressive syntax on top of a clean semantic model Slide 10 DSL - expressive syntax on top of a clean semantic model Slide 11 DSL - expressive syntax on top of a clean semantic model Slide 12 DSL - expressive syntax on top of a clean semantic model Slide 13 DSL - expressive syntax on top of a clean semantic model Slide 14 DSL - expressive syntax on top of a clean semantic model Slide 15 DSL - expressive syntax on top of a clean semantic model Slide 16 DSL - expressive syntax on top of a clean semantic model Slide 17 DSL - expressive syntax on top of a clean semantic model Slide 18 DSL - expressive syntax on top of a clean semantic model Slide 19 DSL - expressive syntax on top of a clean semantic model Slide 20 DSL - expressive syntax on top of a clean semantic model Slide 21 DSL - expressive syntax on top of a clean semantic model Slide 22 DSL - expressive syntax on top of a clean semantic model Slide 23 DSL - expressive syntax on top of a clean semantic model Slide 24 DSL - expressive syntax on top of a clean semantic model Slide 25 DSL - expressive syntax on top of a clean semantic model Slide 26 DSL - expressive syntax on top of a clean semantic model Slide 27 DSL - expressive syntax on top of a clean semantic model Slide 28 DSL - expressive syntax on top of a clean semantic model Slide 29 DSL - expressive syntax on top of a clean semantic model Slide 30 DSL - expressive syntax on top of a clean semantic model Slide 31 DSL - expressive syntax on top of a clean semantic model Slide 32 DSL - expressive syntax on top of a clean semantic model Slide 33 DSL - expressive syntax on top of a clean semantic model Slide 34 DSL - expressive syntax on top of a clean semantic model Slide 35 DSL - expressive syntax on top of a clean semantic model Slide 36 DSL - expressive syntax on top of a clean semantic model Slide 37 DSL - expressive syntax on top of a clean semantic model Slide 38 DSL - expressive syntax on top of a clean semantic model Slide 39 DSL - expressive syntax on top of a clean semantic model Slide 40 DSL - expressive syntax on top of a clean semantic model Slide 41 DSL - expressive syntax on top of a clean semantic model Slide 42 DSL - expressive syntax on top of a clean semantic model Slide 43 DSL - expressive syntax on top of a clean semantic model Slide 44 DSL - expressive syntax on top of a clean semantic model Slide 45 DSL - expressive syntax on top of a clean semantic model Slide 46 DSL - expressive syntax on top of a clean semantic model Slide 47 DSL - expressive syntax on top of a clean semantic model Slide 48 DSL - expressive syntax on top of a clean semantic model Slide 49 DSL - expressive syntax on top of a clean semantic model Slide 50 DSL - expressive syntax on top of a clean semantic model Slide 51 DSL - expressive syntax on top of a clean semantic model Slide 52 DSL - expressive syntax on top of a clean semantic model Slide 53 DSL - expressive syntax on top of a clean semantic model Slide 54 DSL - expressive syntax on top of a clean semantic model Slide 55 DSL - expressive syntax on top of a clean semantic model Slide 56 DSL - expressive syntax on top of a clean semantic model Slide 57 DSL - expressive syntax on top of a clean semantic model Slide 58 DSL - expressive syntax on top of a clean semantic model Slide 59 DSL - expressive syntax on top of a clean semantic model Slide 60 DSL - expressive syntax on top of a clean semantic model Slide 61 DSL - expressive syntax on top of a clean semantic model Slide 62 DSL - expressive syntax on top of a clean semantic model Slide 63 DSL - expressive syntax on top of a clean semantic model Slide 64 DSL - expressive syntax on top of a clean semantic model Slide 65 DSL - expressive syntax on top of a clean semantic model Slide 66 DSL - expressive syntax on top of a clean semantic model Slide 67 DSL - expressive syntax on top of a clean semantic model Slide 68 DSL - expressive syntax on top of a clean semantic model Slide 69 DSL - expressive syntax on top of a clean semantic model Slide 70 DSL - expressive syntax on top of a clean semantic model Slide 71 DSL - expressive syntax on top of a clean semantic model Slide 72 DSL - expressive syntax on top of a clean semantic model Slide 73 DSL - expressive syntax on top of a clean semantic model Slide 74 DSL - expressive syntax on top of a clean semantic model Slide 75 DSL - expressive syntax on top of a clean semantic model Slide 76 DSL - expressive syntax on top of a clean semantic model Slide 77 DSL - expressive syntax on top of a clean semantic model Slide 78 DSL - expressive syntax on top of a clean semantic model Slide 79 DSL - expressive syntax on top of a clean semantic model Slide 80 DSL - expressive syntax on top of a clean semantic model Slide 81 DSL - expressive syntax on top of a clean semantic model Slide 82 DSL - expressive syntax on top of a clean semantic model Slide 83
Upcoming SlideShare
Simplicity 2.0 - Get the power back
Next
Download to read offline and view in fullscreen.

9 Likes

Share

Download to read offline

DSL - expressive syntax on top of a clean semantic model

Download to read offline

Does a DSL mean compromising the domain model purity for an ultra-expressive syntax. This presentation discusses how to evolve your DSL syntax as a sublanguage of combinators on top of an expressive domain model.

Related Books

Free with a 30 day trial from Scribd

See all

DSL - expressive syntax on top of a clean semantic model

  1. 1. Domain Specific Languages - a user view and an implementation view does expressiveness imply a compromise on the underlying domain model ?
  2. 2. Debasish Ghosh @debasishg on twitter Code @ http://github.com/debasishg Blog @ Ruminations of a Programmer http:// debasishg.blogspot.com
  3. 3. Open Source Footprints <ul><li>Committer in Akka ( http:// akka.io ) </li></ul><ul><li>Owner/Founder of : </li></ul><ul><ul><li>sjson (JSON serialization library for Scala objects – http://github.com/debasishg/sjson ) </li></ul></ul><ul><ul><li>scala-redis (Redis client for Scala – http://github.com/debasishg/scala-redis ) </li></ul></ul><ul><ul><li>scouchdb (Scala driver for Couchdb – http:// github.com/debasishg/scouchdb ) </li></ul></ul>
  4. 4. <ul><li>model-view architecture of a DSL </li></ul><ul><li>how abstractions shape a DSL structure </li></ul><ul><li>choosing proper abstractions for compositionality </li></ul><ul><li>using proper language idioms </li></ul><ul><li>composing DSLs </li></ul>
  5. 5. <ul><li>model-view architecture of a DSL </li></ul><ul><li>how abstractions shape a DSL structure </li></ul><ul><li>choosing proper abstractions for compositionality </li></ul><ul><li>using proper language idioms </li></ul><ul><li>composing DSLs </li></ul>
  6. 6. View Or Syntax new_trade 'T-12435' for account 'acc-123' to buy 100 shares of 'IBM' , at UnitPrice = 100 , Principal = 12000 , Tax = 500 DSL Model … Semantic model of the domain
  7. 7. view / syntax is derived from model
  8. 8. view / syntax is a veneer of abstraction over model
  9. 9. view / syntax is decoupled thru an interpreter from model
  10. 10. view / syntax can be multiple over the same model
  11. 11. <ul><li># create instrument </li></ul><ul><li>instrument = Instrument.new( 'Google' ) </li></ul><ul><li>instrument.quantity = 100 </li></ul><ul><li>#create trade </li></ul><ul><li>Trade.new( 'T-12435' , </li></ul><ul><li>'acc-123' , :buy, instrument, </li></ul><ul><li>'unitprice' => 200 , </li></ul><ul><li>'principal' => 120000 , </li></ul><ul><li>'tax' => 5000 </li></ul><ul><li>) </li></ul>idiomatic Ruby API
  12. 12. <ul><li>new_trade 'T-12435' </li></ul><ul><li>for account 'acc-123' </li></ul><ul><li>to buy 100 shares of 'IBM' , </li></ul><ul><li>at UnitPrice = 100 , </li></ul><ul><li>Principal = 12000 , </li></ul><ul><li>Tax = 500 </li></ul>add domain syntax
  13. 13. <ul><li>new_trade 'T-12435' </li></ul><ul><li>for account 'acc-123' </li></ul><ul><li>to buy 100 shares of 'IBM' , </li></ul><ul><li>at UnitPrice = 100 , </li></ul><ul><li>Principal = 12000 , </li></ul><ul><li>Tax = 500 </li></ul>add domain syntax
  14. 14. domain syntax idiomatic Ruby model interpreter isolation layer between the model and the view of the DSL
  15. 15. <ul><li>def parse(dsl_string) </li></ul><ul><li>dsl = dsl_string.clone </li></ul><ul><li>dsl.gsub!(/=/, '=>' ) </li></ul><ul><li>dsl.sub!(/and /, '' ) </li></ul><ul><li>dsl.sub!(/at /, '' ) </li></ul><ul><li>dsl.sub!(/for account /, ',' ) </li></ul><ul><li>dsl.sub!(/to buy /, ', :buy, ' ) </li></ul><ul><li>dsl.sub!(/(+) shares of ( '.*?' )/, </li></ul><ul><li>'.shares.of()' ) </li></ul><ul><li>puts dsl </li></ul><ul><li>dsl </li></ul><ul><li>end </li></ul>
  16. 16. <ul><li>Not all transformations can be done using simple regular expression based string processing </li></ul>
  17. 17. <ul><li>model-view architecture of a DSL </li></ul><ul><li>how abstractions shape a DSL structure </li></ul><ul><li>choosing proper abstractions for compositionality </li></ul><ul><li>using proper language idioms </li></ul><ul><li>composing DSLs </li></ul>
  18. 18. … … abstractions of the core domain model layers of abstractions
  19. 19. … … abstractions of the core domain model abstractions that glue model elements layers of abstractions combinators, façade objects, bridges & adapters
  20. 20. … … abstractions of the core domain model abstractions that glue model elements layers of abstractions combinators, façade objects, bridges & adapters d omain s pecific e mbedded l anguage
  21. 21. … … abstractions of the core domain model abstractions that glue model elements layers of abstractions combinators, façade objects, bridges & adapters d omain s pecific e mbedded l anguage model view
  22. 22. layers of abstractions .. <ul><li>keep the underlying domain model clean </li></ul><ul><li>ensure separation of concerns between the model and the view of the DSL </li></ul><ul><li>you can also implement the glue layer using a different programming language (polyglot programming) </li></ul>
  23. 23. withAccount(trade) { account => { settle(trade using account.getClient .getStandingRules .filter(_.account == account) .first) andThen journalize } } DSL in Scala over a Java domain model
  24. 24. withAccount( trade ) { account => { settle (trade using account.getClient .getStandingRules .filter(_.account == account) .first) andThen journalize } } DSL in Scala over a Java domain model Java abstractions
  25. 25. withAccount ( trade ) { account => { settle (trade using account.getClient .getStandingRules .filter(_.account == account) .first) andThen journalize } } DSL in Scala over a Java domain model Java abstractions Wired through Scala control structures
  26. 26. <ul><li>We need abstractions that generalize over computations .. </li></ul>
  27. 27. Our domain – stocks, trades, bulls, bears .. <ul><li>Security trade </li></ul><ul><ul><li>Exchange of securities and currencies (also known as Instruments) </li></ul></ul><ul><ul><li>In the stock exchange (market) </li></ul></ul><ul><ul><li>On a specified &quot;trade date&quot; </li></ul></ul><ul><ul><li>Between counter parties </li></ul></ul><ul><ul><li>In defined quantities </li></ul></ul><ul><ul><li>At market rates </li></ul></ul><ul><ul><li>Resulting in a net cash value for the seller </li></ul></ul><ul><ul><li>On the &quot;value date&quot; </li></ul></ul>
  28. 28. Our domain – stocks, trades, bulls, bears .. <ul><li>Security trade </li></ul><ul><ul><li>Exchange of securities and currencies (also known as Instruments ) </li></ul></ul><ul><ul><li>In the stock exchange ( market ) </li></ul></ul><ul><ul><li>On a specified &quot; trade date &quot; </li></ul></ul><ul><ul><li>Between counter parties' accounts </li></ul></ul><ul><ul><li>In defined quantities </li></ul></ul><ul><ul><li>At market rates </li></ul></ul><ul><ul><li>Resulting in a net cash value for the seller </li></ul></ul><ul><ul><li>On the &quot; value date &quot; </li></ul></ul>
  29. 29. // The Trade model case class Trade ( account: Account , instrument: Instrument , referenceNo: String , market: Market , unitPrice: BigDecimal , quantity: BigDecimal , tradeDate: Date = Calendar .getInstance.getTime, valueDate: Option [ Date ] = None , taxFees: Option [ List [( TaxFeeId , BigDecimal )]] = None , netAmount: Option [ BigDecimal ] = None )
  30. 30. // various tax/fees to be paid when u do a trade sealed trait TaxFeeId case object TradeTax extends TaxFeeId case object Commission extends TaxFeeId case object VAT extends TaxFeeId case object Surcharge extends TaxFeeId
  31. 31. // computes value date of a trade val valueDateProcessor: Trade => Trade = //.. impl // computes tax/fee of a trade val taxFeeProcessor: Trade => Trade = //.. Impl // computes the net cash value of a trade val netAmountProcessor: Trade => Trade = //.. impl
  32. 32. val trades = //.. list of security trades // get a list of value dates for all trades val valueDates = trades map (_.valueDate) // enrich a trade passing it thru multiple processors val richTrade = (valueDateProcessor map taxFeeProcessor map netAmountProcessor) apply trade // pass a trade thru one specific processor val t = some(trade) map valueDateProcessor
  33. 33. val trades = //.. list of security trades // get a list of value dates for all trades val valueDates = trades map (_. valueDate ) // enrich a trade passing it thru multiple processors val richTrade = ( valueDateProcessor map taxFeeProcessor map netAmountProcessor ) apply trade // pass a trade thru one specific processor val t = some( trade ) map valueDateProcessor
  34. 34. val trades = //.. list of security trades // get a list of value dates for all trades val valueDates = trades map (_.valueDate) // enrich a trade passing it thru multiple processors val richTrade = (valueDateProcessor map taxFeeProcessor map netAmountProcessor) apply trade // pass a trade thru one specific processor val t = some(trade) map valueDateProcessor
  35. 35. val trades = //.. list of security trades // map across the list functor val valueDates = trades map (_.valueDate) // map across the Function1 functor val richTrade = (valueDateProcessor map taxFeeProcessor map netAmountProcessor) apply trade // map across an Option functor val t = some(trade) map valueDateProcessor
  36. 36. <ul><li>a map is .. </li></ul><ul><li>a combinator </li></ul>
  37. 37. <ul><li>a map .. </li></ul><ul><li>pimps abstractions to give us an ad hoc polymorphism implementation on top of OO </li></ul>
  38. 38. <ul><li>a map is .. </li></ul><ul><li>fundamentally powered by type classes </li></ul>
  39. 39. sealed trait MA [ M [_], A ] extends PimpedType [ M [ A ]] { def map[ B ](f: A => B )( implicit t: Functor [ M ]): M [ B ] = t.fmap(value, f) source: scalaz : https://github.com/scalaz/scalaz a type class a pimp
  40. 40. trait Functor[ F [ _ ]] extends InvariantFunctor [ F ] {    def fmap[ A , B ](r: F [ A ], f: A => B ): F [ B ] } source: scalaz : https://github.com/scalaz/scalaz
  41. 41. a functor is .. a type class for all things that can be mapped over
  42. 42. // map across the Function1 functor val richTrade = (valueDateProcessor map taxFeeProcessor map netAmountProcessor) apply trade Function1[] implicit def Function1Functor[R] = new Functor //..
  43. 43. the idea is to generalize abstractions .. .. and by generalizing map over functors we get a combinator that can glue a large set of our domain abstractions .. and this gives a feeling of uniformity in the DSL syntax
  44. 44. semantic model + combinators = DSL
  45. 45. <ul><li>model-view architecture of a DSL </li></ul><ul><li>how abstractions shape a DSL structure </li></ul><ul><li>choosing proper abstractions for compositionality </li></ul><ul><li>using proper language idioms </li></ul><ul><li>composing DSLs </li></ul>
  46. 46. <ul><li>a DSL .. </li></ul><ul><li>evolves from the model non-invasively </li></ul><ul><li>using combinators </li></ul><ul><li>that compose model elements </li></ul><ul><li>interprets them into computations of the domain </li></ul>
  47. 47. so, a domain model also has to be .. designed for compositionality Image source: Mathematical art of M. C. Escher ( http://im-possible.info/english/articles/escher_math/escher_math.html )
  48. 48. domain rule .. <ul><li>enrichment of trade is done in steps: </li></ul><ul><li>get the tax/fee ids for a trade </li></ul><ul><li>calculate tax/fee values </li></ul><ul><li>enrich trade with net cash amount </li></ul>
  49. 49. the DSL .. val enrich = for { // get the tax/fee ids for a trade    taxFeeIds <- forTrade // calculate tax fee values    taxFeeValues <- taxFeeCalculate // enrich trade with net cash amount    netAmount <- enrichTradeWith } yield ((taxFeeIds map taxFeeValues) map netAmount) // enriching a trade trade map enrich should equal(…)
  50. 50. the DSL .. val enrich = for { // get the tax/fee ids for a trade    taxFeeIds <- forTrade // calculate tax fee values    taxFeeValues <- taxFeeCalculate // enrich trade with net cash amount    netAmount <- enrichTradeWith } yield((taxFeeIds map taxFeeValues) map netAmount) // enriching a trade trade map enrich should equal(…) Model elements that can be composed using the for-comprehension
  51. 51. reiterating .. a combinator acts .. as a glue combining model elements But ..
  52. 52. there is a prerequisite .. model elements should be composable we call this the compositionality of the model
  53. 53. Q : How to design your model so that it's compositional ? A : choose the right abstractions .. depending on the programming paradigm (FP, OO ..) and the implementation language used
  54. 54. domain rule .. <ul><li>net cash value calculation of trade: </li></ul><ul><li>use strategy for specific markets, if available </li></ul><ul><li>otherwise use default strategy </li></ul><ul><li>we can supply specific strategy inline as well </li></ul>
  55. 55. the DSL .. // type alias for domain vocabulary type NetAmount = BigDecimal type CashValueCalculationStrategy = PartialFunction [ Market , Trade => NetAmount ] // declarative business logic val cashValueComputation : Trade => NetAmount = { trade =>    (forHongKong orElse forSingapore orElse forDefault)(trade.market)(trade) }
  56. 56. the DSL .. val cashValue : Trade => CashValueCalculationStrategy => NetAmount = { trade => pf =>    if (pf.isDefinedAt(trade.market)) pf(trade.market)(trade)    else cashValueComputation(trade) }
  57. 57. <ul><li>PartialFunction is .. </li></ul><ul><li>the secret sauce </li></ul><ul><li>all functions for specific and default computation return PartialFunction </li></ul><ul><li>that offer combinators for chaining of abstractions .. </li></ul>
  58. 58. val forHongKong: CashValueCalculationStrategy = {    case HongKong => { trade => //.. } } val forSingapore: CashValueCalculationStrategy = {    case Singapore => { trade => //.. } } val forDefault: CashValueCalculationStrategy = {    case _ => { trade => //.. } }
  59. 59. <ul><li>model-view architecture of a DSL </li></ul><ul><li>how abstractions shape a DSL structure </li></ul><ul><li>choosing proper abstractions for compositionality </li></ul><ul><li>using proper language idioms </li></ul><ul><li>composing DSLs </li></ul>
  60. 60. choice of abstractions depends on the programming language chosen for implementation of your DSL always keep an eye on the paradigms that your implementation language supports and play to the idioms of the language
  61. 61. domain rule .. A trade needs to be enriched with a set of tax and fees before we calculate its net cash value
  62. 62. the Java way .. // use the decorator pattern new CommissionDecorator( new TaxFeeDecorator(new Trade(..))); public class Trade { //.. public class TaxFeeDecorator extends Trade { private Trade trade; //.. } public class CommissionDecorator extends Trade { private Trade trade; //.. }
  63. 63. the Java way .. <ul><li>decorators and the decoratee share a common interface </li></ul><ul><li>statically coupled through inheritance </li></ul><ul><li>the decorator usage syntax is outside-in </li></ul>
  64. 64. the Ruby way .. ## decorator pattern Trade . new (..).with TaxFee , Commission class Trade attr_accessor .. def initialize .. def with(*args) args.inject(self) {|memo, val| memo. extend val} end def value @principal end end
  65. 65. the Ruby way .. ## mixins module TaxFee ## calculate taxfee def value super + .. end end ## mixins module Commission ## calculate commission def value super + .. end end
  66. 66. the Ruby way .. <ul><li>more dynamic compared to Java </li></ul><ul><li>uses Ruby idioms of runtime meta-programming </li></ul><ul><li>decorators don’t statically extend the core abstraction </li></ul>
  67. 67. if we were to do the same in Clojure .. ## decorator pattern (with-tax-fee trade (with-values :tax 12 ) (with-values :commission 23 )) ( defmacro with-tax-fee &quot;wrap a function in one or more decorators&quot; [func & decorators] `(redef ~func (-> ~func ~@decorators)))
  68. 68. the Clojure way .. <ul><li>more functional – uses HOF </li></ul><ul><li>function threading makes the decorator implementation almost trivial </li></ul><ul><li>control structure uses macros, which are compile time meta-programming artifacts (unlike Ruby) – no run time overhead </li></ul>
  69. 69. <ul><li>model-view architecture of a DSL </li></ul><ul><li>how abstractions shape a DSL structure </li></ul><ul><li>choosing proper abstractions for compositionality </li></ul><ul><li>using proper language idioms </li></ul><ul><li>composing DSLs </li></ul>
  70. 70. if you thought DSLs grow through c omposition of abstractions .. .. then the final DSL is the whole formed from the composition of its parts ..
  71. 71. … and if the final DSL is also an abstraction .. .. there's no reason we can compose it with another abstraction (aka another DSL) to form a bigger whole ..
  72. 72. trait TradeDsl { type T <: Trade def enrich: PartialFunction [ T , T ] //.. other methods } constrained abstract type abstract method
  73. 73. trait FixedIncomeTradeDsl extends TradeDsl { type T = FixedIncomeTrade import FixedIncomeTradingService ._ override def enrich: PartialFunction [ T , T ] = { case t => t.cashValue = cashValue(t) t.taxes = taxes(t) t.accruedInterest = accruedInterest(t) t } } object FixedIncomeTradeDsl extends FixedIncomeTradeDsl concrete type for fixed income trade concrete definition for enrichment of fixed income trade
  74. 74. trait EquityTradeDsl extends TradeDsl { type T = EquityTrade import EquityTradingService ._ override def enrich: PartialFunction [ T , T ] = { case t => t.cashValue = cashValue(t) t.taxes = taxes(t) t } } object EquityTradeDsl extends EquityTradeDsl concrete type for equity trade concrete definition for enrichment of equity trade
  75. 75. .. and now a new market rule comes up that needs to be applied to all types of trades .. you don’t want to affect the core logic , since the rule is a temporary and promotional one and is likely to be discontinued after a few days ..
  76. 76. .. design the DSL for the new rule in such a way that you can plug in the semantics of all types of trade within it ..
  77. 77. and now the rule itself .. “ Any trade on the New York Stock Exchange of principal value > 10,000 USD must have a discount of 10% of the principal on the net cash value.”
  78. 78. trait MarketRuleDsl extends TradeDsl { val semantics: TradeDsl type T = semantics. T override def enrich: PartialFunction [ T , T ] = { case t => val tr = semantics.enrich(t) tr match { case x if x.market == NYSE && x.principal > 1000 => tr.cashValue = tr.cashValue - tr.principal * 0.1 tr case x => x } } }
  79. 79. trait MarketRuleDsl extends TradeDsl { val semantics : TradeDsl type T = semantics.T override def enrich: PartialFunction[T, T] = { case t => val tr = semantics.enrich(t) tr match { case x if x.market == NYSE && x.principal > 1000 => tr.cashValue = tr.cashValue - tr.principal * 0.1 tr case x => x } } } polymorphic embedding new business rule that acts on top of the enriched trade
  80. 80. object EquityTradeMarketRuleDsl extends MarketRuleDsl { val semantics = EquityTradeDsl } object FixedIncomeTradeMarketRuleDsl extends MarketRuleDsl { val semantics = FixedIncomeTradeDsl } pluggable composition of DSLs plug-in the concrete semantics
  81. 81. import FixedIncomeTradeMarketRuleDsl._ withTrade( 200 discount_bonds IBM for_client NOMURA on NYSE at 72 .ccy( USD )) {trade => Mailer(user) mail trade Logger log trade } cashValue a sneak peek at the fully composed DSL ..
  82. 82. Summary <ul><li>A DSL has a syntax (for the user) and a model (for the implementer) </li></ul><ul><li>The syntax is the view designed on top of the model </li></ul><ul><li>The model needs to be clean and extensible to support seamless development of domain syntax </li></ul><ul><li>The model needs to be compositional so that various model elements can be glued together using combinators to make an expressive syntax </li></ul>
  • mohanmca

    Apr. 18, 2015
  • gabrieloliveiraf

    Dec. 28, 2014
  • hrxone

    Dec. 14, 2014
  • wolfhesse1

    Nov. 9, 2013
  • flyflee

    Nov. 1, 2011
  • behaghel

    May. 4, 2011
  • mmalohlava

    May. 4, 2011
  • satishsrinivasan

    May. 3, 2011
  • SergeStinckwich

    May. 3, 2011

Does a DSL mean compromising the domain model purity for an ultra-expressive syntax. This presentation discusses how to evolve your DSL syntax as a sublanguage of combinators on top of an expressive domain model.

Views

Total views

5,817

On Slideshare

0

From embeds

0

Number of embeds

30

Actions

Downloads

95

Shares

0

Comments

0

Likes

9

×