|
// Funkcionální programování v JavaScriptu |
|
|
|
/* demonstrace ukazkou: |
|
* |
|
* - klasicky: |
|
* var input= 20; |
|
* if(typeof input!=="undefined"&&input!==null){ |
|
* input+= 1; |
|
* input*= 2; |
|
* } |
|
* console.log(input); // =42 |
|
* |
|
* - funkcionalne: |
|
* $F(20).chain( |
|
* $F.is_defined, |
|
* prictiJednicku, |
|
* vynasobDvemi |
|
* ) |
|
* .then(console.log); // =42 |
|
* |
|
* - Proc funkcionalne: |
|
* * tzv. self-commented, tedy nazvy funkci slouzi zaroven k vysvetleni, co delaji - v komplikovanejsich pripadech vyrazne zjednodusuji pochopeni kodu |
|
* * nuti programovat po mensich kouscich, ale za to vice obecneji (mene je nekdy vice) |
|
* * tyto male kousky (tzv. pure functions) dokaze srozumitelne sretezit a vytvaret tak funkce komplikovanejsi, ale stale srozumitelne |
|
* * tahle moje implementace take sjednocuje syntaxy s asynchronnimi funkcemi: asyncFCE(parametry).then(fce, ktera se zavola po vykonani asyncFCE).catch(fce, ktera se zavola pokud asyncFCE selze) |
|
* |
|
* - Proc ne: |
|
* * v praxi casto nelze takto specificky ulohu rozkouskovat, coz nevadi primo, ale ztraci se "self-commented" vyhoda |
|
* * v praxi muze byt projekt preplnen mraky malych fci |
|
* * muze vest k situaci, kdy v konkretnim pripade se vola fakticky vice prikazu, nez kdyby se programovalo "namiru" |
|
* */ |
|
|
|
// definice Functoru - technicka cast, lze preskocit na radek 82 |
|
const $F= value=>({ |
|
chain: function(...args){ // zajistuje volani fci v retizku |
|
const value_bak= value; |
|
for(let i= 0, i_length= args.length; i < i_length; i++){ |
|
value= args[i](value); |
|
if(value===$F.cancel){ |
|
value= value_bak; |
|
this.fail= i; |
|
break; |
|
} |
|
} |
|
let out= $F(value); |
|
out.fail= this.fail; |
|
return out; |
|
}, |
|
fail: -1, // paramater pro oznaceni zda a kde v retizku Functor spadl |
|
then: function(f){if(!~this.fail)f(value);return this;}, // fce, ktera se provede po uspesnem splneni retizku |
|
catch: function(f){if(!!~this.fail)f(this.fail);return this;}, // fce, ktera se provede po neuspesnem splneni retizku |
|
valueOf: ()=>value, // metoda, pro ziskani hodnoty z Functoru |
|
toString: ()=>`Functor(${value})`, // metoda, pro zprovozneni nativniho chovani JS (typicky insatnceof, ...) |
|
[Symbol.iterator]: ()=>{ // metoda, pro zprovozneni nativniho chovani JS (typicky oparator ...) |
|
let first= true; |
|
return ({ |
|
next: ()=>{ |
|
if (first) { |
|
first= false; |
|
return ({ |
|
done: false, |
|
value |
|
}); |
|
} |
|
return ({ |
|
done: true |
|
}); |
|
} |
|
}); |
|
}, |
|
|
|
constructor: $F // handler pro operator new |
|
}); |
|
Object.assign($F, { |
|
cancel: Symbol("$F.cancel"), // definice symbolu pro preruseni retizku |
|
trace: x=>{console.log(x);return x;}, // funkce pro zobrazeni hodnoty v retizku aniz by byl prerusen |
|
is_defined: x=>{if(typeof x!=="undefined"&&x!==null) return x; else return $F.cancel;}, // funkce zkontroluje zda je input definovan, pokud ne prerusi retizek a zavola catch |
|
toString: ()=>'Functor', // metoda pro zprovozneni nativniho chovani JS (typicky insatnceof, ...) |
|
is: x=>typeof x.chain === 'function' // metoda pro zjisteni zda je input Functor |
|
}); |
|
|
|
// vlastni pouziti |
|
const prictiJednicku= n=>n + 1; |
|
const vynasobDvemi= n=>n * 2; |
|
|
|
var ukazka= input=>$F(input).chain( |
|
$F.trace, // vypise aktualni hodnotu |
|
$F.is_defined, // zkontroluje, zda je aktualni hodnota definovana |
|
$F.trace, // vypise aktualni hodnotu |
|
prictiJednicku, // k aktualni hodnote pricte jednicku |
|
vynasobDvemi // aktualni hodnotu vyasobi dvemi |
|
).then( |
|
console.log // vypise pokud uspech |
|
).catch( |
|
console.error // vypise pokud neuspech |
|
); |
|
|
|
console.log("ukazka(20) ... klasicka operace s cisly a zavola se then"); |
|
ukazka(20); |
|
console.log("ukazka(undefined) ... neprovedese vse za $F.is_defined a zavola se catch s indexem fce u ktere prikaz selhal"); |
|
ukazka(); |
|
console.log("ukazka('') ... pretypuje se na cislo a zavola se then"); |
|
ukazka(""); |