O funkcionálním programovaní se říká (jako i o spoustě jiných věcí), že vám musí takzvaně docvaknout, ale jakmile vám docvakne, tak to začne dávat smysl a vy budete v programování mnohem lepší. Myslím si, že mi dnes něco z funkcionálního programovaní docvaklo.
Mějme následující jednoduchý úkol: Vytvořte funkci která:
- Z pole na vstupu, např.:
[1,2,3,4]
- Vytvoří pole objektů
[{a: 1, b: 2}, {a: 3, b: 4}]
- Tedy vezme dvojice prvků z pole a vytvoří z nich objekt
{a, b}
Jak na to? Imperativní způsob by vypadal takto:
function transform(arr) {
var temp = []
for (var i = 0; i < arr.length / 2; i++) {
temp.push({ a: arr[i * 2], b: arr[i * 2 + 1] })
}
return temp
}
transform([1, 2, 3, 4])
To není tak špatné, že? Co se na tom dá zlepšit? Problém je, že to moc
nevyjadřuje větu "vezme dvojice prvků z pole a vytvoří z nich objekt {a, b}
".
K přeložení části "vezme dvojice prvků" lze s výhodou použít funkce chunk z knihovny lodash. A k části "vytvoří z nich objekt" se dá použít funkce map (ať už lodash nebo standardní verze). Používám FP verzi knihovny lodash.
function transform(arr) {
return _.chunk(2, arr).map(function(el) {
return {a: el[0], b[1]}
})
}
transform([1,2,3,4])
Tento kód už je lepší co se čitelnosti týče. Čte se takto "rozsekej pole arr na
podpole velikosti 2 (funkce _.chunk
) a potom každou tuto část nahraď polem
{a,b}
(funkce .map
)"
Ale co by se stalo pokud bychom chtěli použít funkci map z knihovny lodash? To by poté vypadalo následovně:
function transform(arr) {
return _.map(function(el) {
return {a: el[0], b[1]}
}, _.chunk(2, arr))
}
transform([1,2,3,4])
Neboli "namapuj to co vytvoří funkce _.chunk(2, arr)
". Tomuto se říká kompozice
funkcí (function composition). Neboli jedna funkce dostane to, co druhá funkce
vrací. Vytváříme tím takové pomyslné potrubí. vstup -> chunk -> map -> výstup
.
K tomuto se také dá použít funkce _.flow
var transform = _.flow(
_.chunk(2),
_.map((el) => {
return {
a: el[0],
b: el[1],
}
}),
)
transform([1, 2, 3, 4])
Neboli přesně vstup -> chunk -> map ([a,b] => {a,b}) -> výstup
. Jak to ale funguje?
Aby toto mohlo fungovat tak knihovna lodash (v FP verzi) implementuje koncept, kterému se říká 'currying'. Ten funguje tak, že pokud funkce přijímá několik argumentů a my některý vynecháme, tak místo aby provedla danou akci, tak nám vrátí funkci.
Důsledek tohoto je, že chunk(2, arr)
provede to samé jako chunk(2)(arr)
a to
je to samé jako var chunk2 = chunk(2); chunk(arr);
. Jednoduchá implementace
funkce flow by vypadala takto:
function flow(a, b) {
return function (args) {
b(a(args))
}
}
To je vše! Ukázali jsme si, jak implementovat stejnou funkci čtyřmi různými způsoby, jak funguje currying a co je to kompozice funkcí.