May 15 2014, Verona
Massimiliano Mantione
(Hyperfair CTO)
Honestly, I fear will not see
a lot of new things in this talk
However, I hope to inspire you new ideas
on how code can be written
This talk is mostly about async programming
(the other half is about metaprogramming)
We all know async is hard in "pure" Javascript
Do we?
Let's see...
The easiest way make an API asynchronous
is to get a callback as argument
But this matters nothing!
We think that the problem is the nesting of callbacks...
But it is the nesting of callbacks!
That also synchronous code can suffer from nesting?
writeFiles(
resizeImages(
readFiles(
listFiles(directoryName))));
Nested expressions are unreadable
A nested expression is a complex one
A complex expression is a deep tree
Usually what we want is a single path in the tree!
Parenthesis must be nested
Representing tree levels with parenthesis produces nesting
A single tree path is like a linked chain
Binary, left-associative infix operators can be chained
The "." operator links very versatile entities
(objects and properties)
Remember: the "." operator links very versatile entities
(objects and properties)
Let's build objects that represent the steps in the chain!
Every object will have a method that returns the next step in the chain...
listFiles(directoryName)
.readFiles()
.resizeImages()
.writeFiles();
This usually works well, but...
Let's see how the language
can help us
"straightening" trees
It is: write the wrong macro and your code will become an unreadable mess
With great power comes great responsibility
Macros give you superpowers
Would you really give up superpowers
because you don't want the responsibility?
Let's revisit callback hell
Does anybody understand monads?
No?
Neither do I!
Please watch the talk
"Monads and Gonads"
by Douglas Crockford
Suppose you want to compute result r from value v applying functions f1, f2 and f3
r = f3 (f2 (f1 (v)));
(note the nesting)
You could represent the intermediate values explicitly:
v1 and v2
v1 = f1 (v);
v2 = f2 (v1);
r = f3 (v2);
(note: no nesting!)
But lots of clutter...
You could build a flowing API...
r = v.f1().f2().f3();
...or you could have a "monadic sequencing" notation:
r = on v do
f1
f2
f3
(note: this is not Javascript)
Monads are objecs that encapsulate a function
Actually, they represent the (future?) result of applying that function
They can be linked to other monads, which represent the input and result of the function...
Does this ring a bell?
Remember that each step in the chain represents an intermediate value
listFiles(directoryName)
.then(readFiles)
.then(resizeImages)
.then(writeFiles);
Let's add something to our previous example
listFiles(directoryName)
.then(readFiles)
.then(resizeImages)
.then(writeFiles)
.fail(handleError)
What does the final fail invocation add?
The expression is a list,
but what about the flow of values?
This is the graph of intermediate values:
// directoryName --------\
// | |
// listFiles ------------+
// | |
// readFiles ------------+
// | |
// resizeImages ---------+
// | |
// writeFiles handleError
As you can see, this is not a simple chain of values!
However, it can be represented as a list
because of a "try-catch" trick
An observable can do one of three things:
// observable -----+----------\
// | | |
// filter -------+----------+
// | | |
// throttle ------+----------+
// | | |
// map ---------+----------+
// | | |
// onNext onCompleted onError
Like a monad, with three functions...
It adds some real value over promises
Promises frameworks lack the comprehensive set of operations on flowing values that Rx.Js has
Moreover, promises can fire only once, while observables are more like node.js streams
Of course, ES6 and ES7 will support Promises and not Rx.Js
Let's look at some code!
The problem is the nesting and not the callbacks
(a verbose syntax is annoying anyway)
Promises give you linear code, but they require a global API conversion
A smarter language can give you linear code with any API
(Promises, Rx.Js, or plain callbacks)
Rx.Js adds real value, but will it ever be supported by Javascript?
(think async in ES7)
Don't forget generators and coroutines!
But... when will we have
migratable coroutines?
code, docs and slides are on github
twitter: @M_a_s_s_i, #metascript
group: metascript@googlegroups.com