Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Building DSLs with JavaScript and CoffeeScript

Building DSLs with JavaScript and CoffeeScript

Web developers are integration specialists; tying plugins, scripts and frameworks together into a web application that works. Thinking in terms of abstractions, by condensing many low-level ideas into fewer high-level ideas, allows us to simplify our code and reason about it with less cognitive overhead. In this session we will examine a few techniques for building abstractions on top of popular JavaScript frameworks by building a Domain Specific Language and bringing some convention to our code.

Ead076bf445f9b50e3c094300e4690e9?s=128

David Mosher

January 09, 2015
Tweet

Transcript

  1. Building DSLs with JavaScript and CoffeeScript Dave Mosher @dmosher dave@testdouble.com

  2. Goals: Theory + Practical Examples What is a DSL? As

    a web developer, why should I care? What practical ways can I apply this knowledge?
  3. Domain Specific Language “Small languages, focused around a specific aspect

    of a software system” ~ Martin Fowler http://martinfowler.com/books/dsl.html
  4. Characteristics “You can't build a whole program with a DSL”

    ~ Martin Fowler http://martinfowler.com/books/dsl.html
  5. Characteristics External vs Internal

  6. External “language that's parsed independently of the host general purpose

    language” http://martinfowler.com/books/dsl.html
  7. External - CSS ruleset : selector [ ‘,’ S* selector

    ]* ‘{‘ S* declaration [ ‘;’ S*declaration ]* ‘}’ S* ; selector : simple_selector [ combinator selector | S+ [ combinator? selector ]? ]? ; simple_selector : element_name [ HASH | class | attrib | pseudo ]* | [ HASH | class | attrib | pseudo ]+ ; class : ‘.’ IDENT ; element_name : IDENT | ‘*’ ; attrib : ‘[‘ S* IDENT S* [ [ ‘=’ | INCLUDES | DASHMATCH ] S* [ IDENT | STRING ] S* ‘]’ ; pseudo : ‘:’ [ IDENT | FUNCTION S* [IDENT S*] ‘)’ ] ;
  8. Internal “form of API in a host general purpose language”

    http://martinfowler.com/books/dsl.html
  9. Internal “fluent interface” http://martinfowler.com/books/dsl.html

  10. Internal - Procedural Code http://martinfowler.com/bliki/FluentInterface.html function makeNormal(Customer customer) { Order

    o1 = new Order(); customer.addOrder(o1); OrderLine line1 = new OrderLine(1, Product.find("widget")); o1.addLine(line1); OrderLine line2 = new OrderLine(2, Product.find("pants")); o1.addLine(line2); OrderLine line3 = new OrderLine(3, Product.find("cows")); o1.addLine(line3); line2.setSkippable(true); o1.setRush(true); }
  11. Internal - Fluent Interface http://martinfowler.com/bliki/FluentInterface.html function makeFluent(Customer customer) { customer.newOrder()

    .with(1, "widget") .with(2, "pants").skippable() .with(3, "cows") .priorityRush(); }
  12. In the Browser JavaScript

  13. A Question of Value “I use to say, ‘I can

    bend space and time with .toString() and .eval()…
  14. A Question of Value “I’ve come to realize that it’s

    generally better to not bend space and time in JavaScript.” ~ Marak Squires
  15. In the Browser Does the host language support meta programming?

  16. Meta Programming Support define_method method_missing eval assignment** Proxies* eval *ES6

    coming soon™ ** Object.defineProperty in some cases
  17. What’s a realistic approach to building a DSL in JavaScript?

  18. Realistic DSLs in JavaScript Focus on the benefits not the

    terminology
  19. Realistic DSLs in JavaScript Reduce cognitive load Improve understanding Reveal

    intent
  20. Focus Areas 1. convention over configuration 2. meaningful semantics 3.

    fluent interfaces 4. extensions to libraries 5. abstractions
  21. Realistic DSLs in JavaScript Convention over Configuration

  22. Namespacing window.app = { views: { config: {}, }, …

    } // myView.js app = app || {}; app.views = app.views || {}; app.views.myView = {};
  23. Extend.js + Namespacing extend(‘app.views.config’, {…});

  24. Realistic DSLs in JavaScript Meaningful Semantics

  25. Meaningful Semantics window.alert; adjective noun verb idiom

  26. Meaningful Semantics window.notify = window.alert; notify(‘semantics improved’); verb

  27. Meaningful Semantics extend ‘app.views.home’, class HomeView extends Backbone.View overloaded semantics

  28. Meaningful Semantics def(‘app.views.home’, Backbone.View.extend({…}); def ‘app.views.home’, class HomeView extends Backbone.View

    improved semantics window.def = window.extend;
  29. Realistic DSLs in JavaScript Fluent Interfaces

  30. Fluent Interfaces (Mocha) (5).should.be.exactly(5).and.be.a.Number;

  31. Method Chaining: Not Fluency // underscore var childrenOf = function(rootId)

    { return _(navItems).chain().where({parentId: rootId, hidden:false}).map(function(child) { return { title: child.label, url: child.url, ordinal: child.sequence, state: child.name }; }).sortBy(function(child) { return child.ordinal; }).value(); }; // lodash var childrenOf = function(rootId) { return _(navItems).where({parentId: rootId, hidden:false}).map(function(child) { return { title: child.label, url: child.url, ordinal: child.sequence, state: child.name }; }).sortBy(function(child) { return child.ordinal; }); };
  32. Procedural Code http://martinfowler.com/bliki/FluentInterface.html function makeNormal(Customer customer) { Order o1 =

    new Order(); customer.addOrder(o1); OrderLine line1 = new OrderLine(1, Product.find("widget")); o1.addLine(line1); OrderLine line2 = new OrderLine(2, Product.find("pants")); o1.addLine(line2); OrderLine line3 = new OrderLine(3, Product.find("cows")); o1.addLine(line3); line2.setSkippable(true); o1.setRush(true); }
  33. Fluent Interface http://martinfowler.com/bliki/FluentInterface.html function makeFluent(Customer customer) { customer.newOrder() .with(1, "widget")

    .with(2, "pants").skippable() .with(3, "cows") .priorityRush(); }
  34. Cost of Implementation “The price of this fluency is more

    effort, both in thinking and in the API construction itself. Coming up with a nice fluent API requires a good bit of thought.” ~ Martin Fowler http://martinfowler.com/bliki/FluentInterface.html
  35. Realistic DSLs in JavaScript Extensions to Libraries

  36. Backbone + Fixins.SuperView class MyView extends Backbone.Fixins.SuperView renderJQueryAccordion: -> https://github.com/testdouble/backbone-fixins

  37. ng - Automatic Template Resolution

  38. Realistic DSLs in JavaScript Abstractions

  39. Web Components <p>Date: <input type=“text” id=“datepicker”/></p> $(‘#datepicker’).datepicker(); <datepicker/>

  40. jQuery + CSS Selector Paradigm $(‘#header > nav li:nth-child(2)’); document.querySelectorAll(…);

  41. ng - custom element directives

  42. Recap 1. convention over configuration 2. meaningful semantics 3. fluent

    interfaces 4. extensions to libraries 5. abstractions
  43. Values to keep in mind 1. reduce cognitive load 2.

    improve understanding 3. reveal intent
  44. Final Thoughts Be pragmatic Be aware of complexity Be cost-averse

  45. Questions?

  46. Thanks for coming! Dave Mosher @dmosher dave@testdouble.com bitly.com/bundles/dmosher/8