$30 off During Our Annual Pro Sale. View Details »

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.

David Mosher

January 09, 2015
Tweet

More Decks by David Mosher

Other Decks in Technology

Transcript

  1. Building DSLs
    with JavaScript and CoffeeScript
    Dave Mosher
    @dmosher
    [email protected]

    View Slide

  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?

    View Slide

  3. Domain Specific Language
    “Small languages, focused
    around a specific aspect of a
    software system”
    ~ Martin Fowler
    http://martinfowler.com/books/dsl.html

    View Slide

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

    View Slide

  5. Characteristics
    External vs Internal

    View Slide

  6. External
    “language that's parsed
    independently of the host
    general purpose language”
    http://martinfowler.com/books/dsl.html

    View Slide

  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*] ‘)’ ]
    ;

    View Slide

  8. Internal
    “form of API in a host
    general purpose language”
    http://martinfowler.com/books/dsl.html

    View Slide

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

    View Slide

  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);
    }

    View Slide

  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();
    }

    View Slide

  12. In the Browser
    JavaScript

    View Slide

  13. A Question of Value
    “I use to say, ‘I can bend space
    and time with .toString()
    and .eval()…

    View Slide

  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

    View Slide

  15. In the Browser
    Does the host language
    support meta programming?

    View Slide

  16. Meta Programming Support
    define_method
    method_missing
    eval
    assignment**
    Proxies*
    eval
    *ES6 coming soon™
    ** Object.defineProperty in some cases

    View Slide

  17. What’s a realistic approach
    to building a DSL in
    JavaScript?

    View Slide

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

    View Slide

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

    View Slide

  20. Focus Areas
    1. convention over configuration
    2. meaningful semantics
    3. fluent interfaces
    4. extensions to libraries
    5. abstractions

    View Slide

  21. Realistic DSLs in JavaScript
    Convention over
    Configuration

    View Slide

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

    }
    // myView.js
    app = app || {};
    app.views = app.views || {};
    app.views.myView = {};

    View Slide

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

    View Slide

  24. Realistic DSLs in JavaScript
    Meaningful Semantics

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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;

    View Slide

  29. Realistic DSLs in JavaScript
    Fluent Interfaces

    View Slide

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

    View Slide

  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; });
    };

    View Slide

  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);
    }

    View Slide

  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();
    }

    View Slide

  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

    View Slide

  35. Realistic DSLs in JavaScript
    Extensions to Libraries

    View Slide

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

    View Slide

  37. ng - Automatic Template Resolution

    View Slide

  38. Realistic DSLs in JavaScript
    Abstractions

    View Slide

  39. Web Components
    Date:
    $(‘#datepicker’).datepicker();

    View Slide

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

    View Slide

  41. ng - custom element directives

    View Slide

  42. Recap
    1. convention over configuration
    2. meaningful semantics
    3. fluent interfaces
    4. extensions to libraries
    5. abstractions

    View Slide

  43. Values to keep in mind
    1. reduce cognitive load
    2. improve understanding
    3. reveal intent

    View Slide

  44. Final Thoughts
    Be pragmatic
    Be aware of complexity
    Be cost-averse

    View Slide

  45. Questions?

    View Slide

  46. Thanks for coming!
    Dave Mosher
    @dmosher
    [email protected]
    bitly.com/bundles/dmosher/8

    View Slide