Slide 1

Slide 1 text

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)

Slide 2

Slide 2 text

? (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.

Slide 3

Slide 3 text

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].

Slide 4

Slide 4 text

What is a Function? So,..

Slide 5

Slide 5 text

What is a Function? arguments body result value

Slide 6

Slide 6 text

Which way it looks like in JavaScript? function(x, y, z) { return ω; }

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

Using lambda format, you may pass one function to any other function

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

You may return function from lambda and call it then

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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' }

Slide 20

Slide 20 text

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' }

Slide 21

Slide 21 text

And even lambda does! (function(z) { return this.x+this.y+z; }).call({x: 25, y: 6}, 11); ➟ 42

Slide 22

Slide 22 text

This means that value of this always depends on the place where you do a call

Slide 23

Slide 23 text

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.

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

Now, to the interesting part

Slide 27

Slide 27 text

Deferred functions

Slide 28

Slide 28 text

Required to achieve 'foofoobarxbee' parse( twice(str('foo')), str('bar'), any_char(), str('bee') );

Slide 29

Slide 29 text

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)

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

A tool function def(f) { return function() { return (function(f, args) { return function() { return f.apply(null, args); }; })(f, arguments); } }

Slide 32

Slide 32 text

What happens?

Slide 33

Slide 33 text

Parser example http://codepen.io/shamansir/pen/HskmE

Slide 34

Slide 34 text

Another example http://codepen.io/shamansir/pen/kBzJe

Slide 35

Slide 35 text

Partial applications

Slide 36

Slide 36 text

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)

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

What happens?

Slide 41

Slide 41 text

An example http://codepen.io/shamansir/pen/xCrgz

Slide 42

Slide 42 text

Queued functions

Slide 43

Slide 43 text

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)

Slide 44

Slide 44 text

A way function load_file(name, next) { setTimeout(function() { .... next(/* file */); }, 1000); }

Slide 45

Slide 45 text

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)

Slide 46

Slide 46 text

What happens?

Slide 47

Slide 47 text

An example http://codepen.io/shamansir/pen/AaHqy

Slide 48

Slide 48 text

Composed functions

Slide 49

Slide 49 text

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)

Slide 50

Slide 50 text

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)

Slide 51

Slide 51 text

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 }

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

What happens?

Slide 54

Slide 54 text

An example http://codepen.io/shamansir/pen/Funwt

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

Most of the functional approaches are few- liners You probably don't need special libraries in these cases.

Slide 58

Slide 58 text

No content

Slide 59

Slide 59 text

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.

Slide 60

Slide 60 text

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)

Slide 61

Slide 61 text

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.

Slide 62

Slide 62 text

And don't forget 'bout the ducks.

Slide 63

Slide 63 text

Fin. All of the paintings are created by shaman.sir with the help of iOS Paper app.

Slide 64

Slide 64 text

Questions.