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

Mastering Functional JavaScript

Mastering Functional JavaScript

An easy (I hope), but deep (I believe), introduction in JavaScript functions. Several snippets, several live and fine examples, you'll like it.

Links to the examples:
Deferred functions: http://codepen.io/shamansir/pen/HskmE, http://codepen.io/shamansir/pen/kBzJe
Partial functions: http://codepen.io/shamansir/pen/xCrgz
Queued functions: http://codepen.io/shamansir/pen/AaHqy
Composed functions: http://codepen.io/shamansir/pen/Funwt

Lyfe.js: http://bitbucket.org/balpha/lyfe
Article on lyfe.js: http://balpha.de/2011/06/introducing-lyfe-yield-in-javascript

Ulric Wilfred

December 12, 2012
Tweet

More Decks by Ulric Wilfred

Other Decks in Programming

Transcript

  1. shaman.sir, Animatron, 2012 Mastering Functional JavaScript (the super-duper lecture that

    will let you win in the end) (especially if you will be a smart guy)
  2. ? (you are just silent in the absence of thoughts

    and don't know what to ask) (so I'l tell you which way the lecture will go) About me. Structure of a function. Lambdas. Named functions. In-place call. this. call & apply. Common pitfalls and tricks. Deferred functions. Partial applications. Queueing functions. Iterators & Generators. Composing functions. Objects from a functions' view.
  3. Who's that guy? (means me) I am shaman.sir, a.k.a. Kotenko

    Anton, experienced JavaScript web developer. You may find my homesite at http://shamansir.github.com, and my blog is at http://shamansir.github.com/blog; my e-mail is [email protected].
  4. Factually, this example itself is a lambda (λ), an in-place

    function without a name... function(x, y, z) { return ω; }
  5. Factually, this example itself is a lambda (λ), an in-place

    function without a name... function(x, y, z) { return ω; }
  6. Here are several named (instead) variants... var foo = function(x,

    y, z) { return ω; } function foo(x, y, z) { return ω; } bar.foo = function(x, y, z) { return ω; } bar.prototype.foo = function(x, y, z) { return ω; }
  7. Here are several named (instead) variants... var foo = function(x,

    y, z) { return ω; } function foo(x, y, z) { return ω; } bar.foo = function(x, y, z) { return ω; } bar.prototype.foo = function(x, y, z) { return ω; }
  8. ...And how d'ya call them. foo(x, y, z); foo(x, y,

    z); bar.foo(x, y, z); new bar().foo(x, y, z);
  9. ...And how d'ya call them. var a = foo(x, y,

    z); var a = foo(x, y, z); var a = bar.foo(x, y, z); var a = new bar().foo(x, y, z);
  10. Using lambda format, you may pass one function to any

    other function function(x, y, z) { return ω; } function filter(xs, f) { ... f(xs[i]) ... } filter([...], function(x, y, z) { return ω; });
  11. You may call lambda in-place (function(x, y, z) { return

    ω; })(25, 6, 11) with passing actual values
  12. You may call lambda in-place (function(x, y, z) { return

    x+y+z; })(25, 6, 11) with passing actual values ➟ 42
  13. You may return function from lambda and call it then

    (function(z, a, b) { var x = a + b; return function(y) { return x + y + z; }; })(11, 20, 5)(6); ➟ 42
  14. Any function may be called with particular this state var

    foo = function(z) { return this.x+this.y+z; }; foo.call({x: 25, y: 6}, 11); ➟ 42 foo.apply({x: 25, y: 6}, [11]); ➟ 42 Array.prototype.push.call({length: 6}, 'a'); ➟ { length: 7, 6: 'a' }
  15. Any function may be called with particular this state var

    foo = function(z) { return this.x+this.y+z; }; foo.call({x: 25, y: 6}, 11); ➟ 42 foo.apply({x: 25, y: 6}, [11]); ➟ 42 Array.prototype.push.call({length: 6}, 'a'); ➟ { length: 7, 6: 'a' }
  16. New scope level is created only for functions, there is

    no block-scoping So there is no inner-blocks for if-s and for-s and all that stuff.
  17. Common pitfall for (var i = 0; i < 42;

    i++) { other_func(..., function() { console.log(i); }); }); because of its lambda nature, inner function may be called in any time after the fact the loop was performed — it may never be called at all or may be called several times, but it's only other_func knows when since your i variable is just a pointer to a value, independently of the time when lambda was called, it is already a 42
  18. Common pitfall for (var i = 0; i < 42;

    i++) { other_func(..., (function(actual_i) { return function() { console.log(actual_i); }); })(i)); }); Now you create a wrapping lambda that is called immediately and saves the actual value of i to some another var, say actual_i solution
  19. Required to achieve pipe( process('foo.sh'), process('bar.sh'), stream_to('file.text') ); (synchronous, one-by-one,

    for now, but an asynchronous version to come later in the very same presentation)
  20. A way function twice(f) { var chunk = f(); return

    (search(str, pos, chunk) && search(str, pos+chunk.length, chunk); } twice = def(twice);
  21. A tool function def(f) { return function() { return (function(f,

    args) { return function() { return f.apply(null, args); }; })(f, arguments); } }
  22. Required to achieve foo(a, b, c) == foo(a)(b, c) (you

    are right, if you think about native Function.bind — we do reimplement it here)
  23. A way var foo_c = bind(foo), foo_c_ctx = ctx_bind(foo); foo_c(1,

    4, 5)(2, 3, 7) foo_c_ctx(1, 4)(2, 3, 8)({ foo: 'bar' });
  24. A tool function bind(f) { return function() { return (function(args_p1)

    { return function() { return (function(args_p2) { return f.apply(null, as_arr(args_p1).concat(as_arr(args_p2))); })(arguments); }; })(arguments); }; }
  25. A tool function ctx_bind(f) { return function() { return (function(args_p1)

    { return function() { return (function(args_p2) { return function(ctx) { return f.apply(ctx || null, as_arr(args_p1).concat(as_arr(args_p2))); } })(arguments); }; })(arguments); }; }
  26. Required to achieve queue(load_file)( ['file1', 'file2', 'file3'] ); (this one

    looks asynchronous and it is factually able to be asynchronous, every call is such, and they are performed in the given strict order)
  27. A tool function queue(f) { return function(values) { function __next()

    { if (!values.length) return; f(values.shift(), __next); } __next(); } } (we omit error handling to give you clear view towards the process, but you may easily get on_error or on_complete among with f and pass them together with __next)
  28. Required to achieve compose(open_file, read_file, show_file, close_file)('test.file'); (this one gives

    a next-calls-combined callback to every function, so each of them decides by itself if call it after synchronous or asynchronous process)
  29. Required to achieve compose(open_file, read_file, show_file, close_file)('test.file'); (also it passes

    the every previous result to the next function and that's why it's so MIGHTY)
  30. A way function open_file(name, callback) { ... // callback may

    be called asynchronously } function read_file(handle, callback) { ... // callback may be called asynchronously } function show_file(contents, callback) { ... // callback may be called asynchronously } function close_file(handle, callback) { ... // callback may be called asynchronously }
  31. A tool function compose() { var fs = arguments, fl

    = fs.length, fi = fl, stack = []; if (!fi) throw new Error('Please pass some functions'); while (fi--) { stack[fi] = (function(f, cb) { return function(res) { f(res, cb); } })(fs[fi], stack[fi+1] || null); } return function(initial) { stack[0](initial); } }
  32. For generators in JS, (yes, the ones with yield) see

    And if you'll look in the source, you now can make it a few-liner too. Lyfe.js
  33. For generators in JS, (yes, the ones with yield) see

    an article about And if you'll look in the source, you now can make it a few-liner too. Lyfe.js
  34. Most of the functional approaches are few- liners You probably

    don't need special libraries in these cases.
  35. Now when you've mastered functions, you may start to master

    objects. Just treat them as a states or instances and never (please, never) give them methods if you are willing to iterate over them in future.
  36. Object with data is a hash. { val1: 3, val2:

    '17', val3: function... } You may iterate over it. (and it is really easy to mix objects like this one)
  37. Object, created from some prototype is a special instance function

    A() { this.a = smth; } A.prototype.method = function() {} var foo = new A(); There's no meaning in iterating over it.