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

Functional programming in JavaScript ecosystem

Functional programming in JavaScript ecosystem

JavaScript keeps getting more and more popular, but it's a pain for
folks that want to write code functional style. Are there any solutions?

paulmillr

July 21, 2012
Tweet

More Decks by paulmillr

Other Decks in Programming

Transcript

  1. Functional
    programming
    in JavaScript
    ecosystem

    View Slide

  2. @paulmillr

    View Slide

  3. JS is a functional
    language

    View Slide

  4. JS is a functional
    language
    Sort of...

    View Slide

  5. What do we
    have today

    View Slide

  6. What do we
    have today
    Proper anonymous
    functions (λ)
    Closures
    ES5 array
    extras
    (map, filter, reduce...)

    View Slide

  7. Is it enough?

    View Slide

  8. Is it enough?
    Yep.

    View Slide

  9. Is it comfy?

    View Slide

  10. Nope.
    Is it comfy?

    View Slide

  11. What’s wrong?

    View Slide

  12. What’s wrong?
    keywords
    are too long

    View Slide

  13. What’s wrong?
    braces
    everywhere
    keywords
    are too long

    View Slide

  14. What’s wrong?
    braces
    everywhere
    keywords
    are too long
    no static
    types

    View Slide

  15. What’s wrong?
    braces
    everywhere
    keywords
    are too long
    no static
    types
    no proper
    tail calls

    View Slide

  16. What’s wrong?
    keywords
    are too long
    braces
    everywhere
    this scoping problem
    s
    no static
    types
    no proper
    tail calls

    View Slide

  17. What’s wrong?
    keywords
    are too long
    braces
    everywhere
    this scoping problem
    s
    no constants
    no static
    types
    no proper
    tail calls

    View Slide

  18. What’s wrong?
    ES5 array extras
    work alright in chaining
    But
    Prototype-based
    == awful modularity
    == collisions
    == low performance

    View Slide

  19. Solutions?
    I want to write functionally
    in JS ecosystem simply.
    What are my options?

    View Slide

  20. Solutions?
    haskell-to-js ClojureScript

    View Slide

  21. Solutions?
    haskell-to-js ClojureScript
    Compile to
    very long files
    hard to debug
    Terrible
    interoperability

    View Slide

  22. Solutions?
    Readable / reasonable
    JS output?
    Good interoperability?
    Simple to debug?

    View Slide

  23. CoffeeScript
    coffeescript.org

    View Slide

  24. CoffeeScript
    Great small language
    Compiles down to JS
    #11 most used on GitHub
    Used in 1000s of popular
    projects

    View Slide

  25. CoffeeScript
    Better for functional
    programming
    Heals JS quirks

    View Slide

  26. CoffeeScript
    Implicit return
    Short λ declaration
    (a, b, c) -> a * b / c
    function(a, b, c) {
    return a * b / c;
    }
    vs
    Whitespace-significant
    syntax

    View Slide

  27. CoffeeScript
    No curly braces
    times 2, sum 1, 2, 3 # => 12
    Round braces are optional
    times(2, sum(1, 2, 3)) # => 12

    View Slide

  28. CoffeeScript
    List comprehensions
    (a * 2 for a in [10, 20, 40])

    View Slide

  29. CoffeeScript
    this fixes via bound
    functions
    current = this
    fn = =>
    log current == this
    $(‘body’).on ‘click’, fn
    # Will log true
    var current = this;
    var fn = function() {
    log current == this;
    };
    $(‘body’).on ‘click’, fn
    # Will log false

    View Slide

  30. CoffeeScript
    Doesn’t heal all quirks
    Brings own ones

    View Slide

  31. CoffeeScript
    Chaining is a lot readable
    with short λs, but still terrible
    # Doesn’t work on Array-like objects
    document.querySelectorAll(‘.user’)
    .map((x) -> x + 5)
    .maximum()
    # Defining methods on prototypes? No, thanks.

    View Slide

  32. CoffeeScript
    Must create λs even
    for simple stuff
    array
    .map((a) => a + 2)
    .filter((a) => a != 10)
    .reduce((a, b) => Math.min(a, b))
    the only
    real work

    View Slide

  33. CoffeeScript
    List comprehensions
    aren’t real
    Basically an infix for loop
    (a * b for a in [1, 2, 3] for b in [10, 20, 40])
    # non flattened result, order is wrong
    # => [ [ 10, 20, 30 ], [ 20, 40, 60 ], [ 40, 80, 120 ] ]

    View Slide

  34. CoffeeScript
    variable = 1
    fn = ->
    variable = 2
    fn()
    console.log variable # => 2
    Terrible variable
    scoping

    View Slide

  35. Roy
    roy.brianmckenna.org

    View Slide

  36. Roy
    Type inference
    Algebraic data types
    Pattern matching
    Monadic syntax

    View Slide

  37. Roy
    Not ready yet
    Still a lot of stuff
    it doesn’t have

    View Slide

  38. LiveScript
    gkz.github.com/
    LiveScript/

    View Slide

  39. LiveScript
    + =
    + =
    + =
    Coco
    Coco

    View Slide

  40. LiveScript
    Easy transition from Coffee
    Improved readability
    Perfect piping operators
    |> (F#)
    <| (F#) ($ in Haskell)

    View Slide

  41. LiveScript
    Standard library
    (prelude.ls)
    gkz.github.com/
    prelude-ls/
    Inspired by prelude.hs

    View Slide

  42. LiveScript
    Partially applied operators
    and member access
    array
    |> map (+ 2)
    |> filter (!= 10)
    |> maximum

    View Slide

  43. LiveScript
    Compile-time consants
    Also, compiler flag that
    make all vars consts
    const string = ‘hello’
    string = 5710
    # => Error

    View Slide

  44. LiveScript
    Improved var scoping
    a = 1
    do ->
    a = 2
    a # => still 1

    View Slide

  45. LiveScript
    Improved operators
    associativity
    unique pulls .length
    unique node or not empty node
    (unique pulls).length
    (unique node) or not (empty node)
    instead of coffee’s

    View Slide

  46. LiveScript
    Real list comprehensions
    [x ** y for x in [10, 20] for y in [2, 3]]
    # => [100, 1000, 400, 800]

    View Slide

  47. LiveScript
    Pattern matching
    take(n, [x, ...xs]:list) =
    | n <= 0 => []
    | empty list => []
    | otherwise => [x] +++ take n - 1, xs

    View Slide

  48. LiveScript
    Simple currying
    times = (x, y) --> x * y
    times 2, 3 # => 6
    double = times 2
    double 5 # => 10

    View Slide

  49. LiveScript
    Async callback flattening
    syntax
    error <- fs.write-file path, data

    View Slide

  50. LiveScript
    Is it ready to use today?

    View Slide

  51. LiveScript
    Is it ready to use today?
    Yep!
    1.0.0 will be released
    later this week.

    View Slide

  52. LiveScript
    Relatively simple
    Debugging
    Will be super simple with
    source maps (2012)

    View Slide

  53. LiveScript
    Sure!
    HTML5 apps
    Including builders that
    auto-compile your apps
    without headache
    (Brunch.io).

    View Slide

  54. LiveScript
    Yep.
    Node.js
    Just add pre-publish
    hook to `package.json`

    View Slide

  55. Compare

    View Slide

  56. Compare
    users
    |> map (.age)
    |> filter (> 10)
    |> maximum
    users
    .map((u) -> u.age)
    .filter((a) -> a > 10)
    .reduce (a, b) ->
    Math.max a, b
    users
    .map(function(u) {return u.age})
    .filter(function(a) {return a > 10})
    .reduce(function(a, b) {
    return Math.max(a, b)
    });
    JS
    Coffee
    LiveScript
    (w/prelude)

    View Slide

  57. Compare
    Coffee
    LiveScript
    elems = document.query-selector-all '.listing .meta a:nth-child(3)'
    pulls = elems |> map (.inner-text)
    text = "Total #{pulls.length} pull requests in #{unique pulls .length} repos."
    elems = [].slice.call document.querySelectorAll '.listing .meta a:nth-child(3)'
    pulls = elems.map (elem) -> elem.innerText
    unique = elems.reduce (a, b) ->
    a.push(b) if b not in a
    a
    text = "Total #{pulls.length} pull requests in #{(unique pulls).length} repos."
    JS → 14 LOC

    View Slide

  58. Compare
    LiveScript
    quick-sort = ([x, ...xs]:list) ->
    | empty list => []
    | otherwise =>
    [left, right] = partition (<= x), xs
    (quick-sort left) +++ [x] +++ (quick-sort right)
    gist.github.com/
    3074009

    View Slide

  59. Compare
    JS
    Coffee
    LiveScript
    quick-sort = ([x, ...xs]:list) ->
    | empty list => []
    | otherwise =>
    [left, right] = partition (<= x), xs
    (quick-sort left) +++ [x] +++ (quick-sort right)
    gist.github.com/
    3074009

    View Slide

  60. Future
    ECMAScript 6
    CoffeeScript 2.0
    LiveScript.next

    View Slide

  61. Future: ECMAScript 6
    let block-scoped vars
    const value checking
    Short arrow functions
    Tail call optimization
    Real list comprehensions
    New javascript standard

    View Slide

  62. Future: ECMAScript 6
    Still a lot of syntax garbage
    ((a, b) => {a + b})(2, 5));
    (+) 2, 5
    vs

    View Slide

  63. Future: CoffeeScript 2
    Same feature set
    Proper compiler
    design principles
    github.com/michaelficarra/
    CoffeeScriptRedux

    View Slide

  64. Future: CoffeeScript 2
    github.com/michaelficarra/
    coffee-of-my-dreams
    When it will be ready,
    author will create
    a functional fork

    View Slide

  65. Future: LiveScript
    Type inference
    Pure annotations
    Tail call optimization

    View Slide

  66. So?
    1. Use LiveScript
    2. Wait for fork of Coffee 2.0
    3. Wait for Roy
    I want to write functionally
    in JS ecosystem simply.
    What are my options?

    View Slide

  67. Thanks!
    Paul Miller
    paulmillr.com
    @paulmillr

    View Slide