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