Nepovažuji se za odborníka na funkcionální programování, ale mám pocit, že koncept closure se nijak nebije s immutable state. Ani Tomáš Petříček to ve své knize nikde nezmiňuje.
8.2.2 Capturing state using closures in F#
In this section we're going to talk about closures, which is an important concept in functional programming. Closures are very common and most of the time they aren't used with mutable state. However, working with mutable state is sometimes needed for the sake of pragmatism and closures give us an excellent way to limit the scope of the mutable state.
(Functional Programming for the Real World)
Docela mě potěšilo, že Tomáš zmiňuje vlastně to samé, co jsem zmiňoval v úvodu svého seriálu. OOP a funkcionální programování nejdou proti sobě. Kdy je tedy správné použít closure? Prostě kdykoliv se mi hodí, aby funkce viděla do scope vyšší úrovně. Dejme tomu, že máme funkci, která využívá jQuery. To, že funkce vidí jQuery z globálního scope je přeci úplně intuitivní, ne? Dále, funkce pracuje s nějakými daty, které jsou definované vně funkce. To, že na ně vidí, je opět velmi praktické. Ale ty chceš příklady, ne kecy, rozumím :)
Tak třeba ten each, přepsaný do Javascriptu:
var multiply = function(number, array) {
return array.map(function(item) {
return item * number; // na number je nutné vidět :)
});
};
alert(multiply(2, [1, 2, 3, 4]));
Lze si představit mnoho obměn, typicky array.sort třeba. Další příklady jsou variantou techniky známé jako currying. V javascriptu ji používáme hodně, tak často, že si to už ani uvědomujeme ;) Většina příkladů na webu jsou totiž podivně nepraktické příklady typu: chci mít funkci, která mi vytvoří funkci, která bude přičítat nějaké číslo. Třeba takto:
var makeAddFn = function(number) {
return function(value) {
return value + number;
};
};
var add2 = makeAddFn(2);
alert(add2(1));
K čemu je takový zápis dobrý? K ničemu! ;) Skládat algoritmus z předpřipravených generovaných funkcí je (až na výjimky) cesta do pekla. Tohle je práce pro třídy, dědičnost, agregaci atd.
Nutnost, nebo spíše krásu closure si uvědomíme v okamžiku, kdy píšeme asynchronní kód. Typicky volání serveru, animace, opožděné zobrazení něčeho atd.. díky closure můžeme provázat vyvolání akce s asynchronním obsloužením výsledku, aniž bychom museli vytvářet nějakou novou datovou strukturu, do které bychom vazbu explicitně ukládali.
var save = function(id, data) {
server.call('user.save', data, function() {
User.lookUp(id).update(data);
});
};
Díky closure si nemusíme ukládat id někde bokem, případně si jej vracet ze serveru.