Sissejuhatus kombinaatoritesse

Kombinaatorid on kõrgema taseme funktsioonid, mis võimaldavad kombineerida funktsioone, muutujaid või teisi kombinaatoreid. Tavaliselt ei sisalda need oma muutujate või äriloogika deklaratsioone. Nad on ainsad, mis loputavad kontrolli funktsiooniprogrammis.

Käesolevas artiklis püüan käsitleda neist mõningaid:

Tap

Kombinaator on väga kasulik funktsioonide puhul, mis ei tagasta midagi. See võtab funktsiooni, kuhu parameeter läheb, ja seejärel tagastatakse see.

Deklaratsioon

const tap = (fn) => (value) => {
fn(value);
return value;
};

Imperatiivne näide

const [items, setItems] = useState(() => [])

axios
.get('http://localhost')
.then({ data } => {
setItems(data)
console.log(data)
onLoadData(data)
}).then(....) // tagastab undefined - andmed lubaduses on muudetud.

Deklaratiivne näide

const [items, setItems] = useState(() => [])

axios
.get('http://localhost')
.then(({ data }) => data)
.then(tap(setItems)) // (data) => { setItems(data); return data }
.then(tap(console.log)) // üks then = üks funktsioon = üks vastutus
.then(tap(onLoadData))
.then(....) // ikkagi juurdepääs algsetele andmetele
// lihtne säilitada avatud/suletud põhimõtet

Currying

See jagab funktsiooni argumendid ja võimaldab neid järjestikku välja kutsuda.

Deklaratsioon

const curry = (fn) => {
const curried = (...args) => {
if (fn.length !== args.length){
return curried.bind(null, ...args)
}
return fn(...args);
};

return curried

};


Näide

const curry = (fn) => {
const curried = (...args) => {
if (fn.length !== args.length){
return curried.bind(null, ...args)
}
return fn(...args);
};

return curried

};
const sum = (a, b, c) => a + b + c

const currySum = curry(sum)
/*
võimalikud kutsed
currySum(a)(b)(c),
currySum(a)(b,c),
currySum(a,b)(c),
currySum(a,b,c)
*/

currySum(1) // (b, c) => 1 + a + b või (b) => (c) => 1 + a + b
currySum(5)(10) // (c) => 5 + 10 + b
currySum(5, 10) // (c) => 5 + 10 + b
currySum(5)(10)(20) // 35
currySum(5, 10)(20) // 35
currySum(5)(10, 20) // 35

const divideBy = curry((a, b) => b / a)
const multiplyBy = curry((a, b) => a * b)

const divideByTwo = divideBy(2)
divideByTwo(10) // tagastab 5

const multiplyByFive = multiplyBy(5)
multiplyByFive(10) // tagastab 50

Toru/koosseis

Kompositsiooni abil on võimalik andmeid ühest funktsioonist teise edastada. Oluline on, et funktsioonid võtaksid sama arvu argumente. Pipe ja compose erinevus seisneb selles, et esimene täidab funktsiooni esimesest kuni viimaseni ja compose kutsub neid lõpust alates.

Deklaratsioon

const pipe = (...fns) => (value, ...args) =>
fns.reduce((v, f, i) =>
i === 0
? f(v, ...args)
: f(v),
value);

const compose = (...fns) => (value, ...args) =>
fns.reduceRight((v, f, i) =>
i === (fns.length - 1)
? f(v, ...args)
: f(v),
value);
const pipe = (...fns) => (value, ...args) =>
fns.reduce((v, f, i) =>
i === 0
? f(v, ...args)
: f(v),
value);

const compose = (...fns) => (value, ...args) =>
fns.reduceRight((v, f, i) =>
i === (fns.length - 1)
? f(v, ...args)
: f(v),
value);

Imperatiivne näide

const sine = (val) => Math.sin(val * Math.PI / 180) // ei ole loetav
 sine(90) // tagastab 1

Deklaratiivne näide

const sine = pipe(
multiplyBy(Math.PI) // ↓ val * Math.PI
divideBy(180), // ↓ val * Math.PI / 180
Math.sin, // ↓ Math.sin(val * Math.PI / 180)
)

const sine = compose(
Math.sin, // ↑ Math.sin(val * Math.PI / 180)
divideBy(180), // ↑ val * Math.PI / 180
multiplyBy(Math.PI) // ↑ val * Math.PI
)

sine(90) // tagastab 1

Kahvel

Kahvlikombinaator on kasulik olukordades, kus soovite töödelda väärtust kahel viisil ja kombineerida tulemust.

Deklaratsioon

const fork = (join, fn1, fn2) => (value) => join(fn1(value), fn2(value));

Näide

const length = (array) => array.length
const sum = (array) => array.reduce((a, b) => a + b, 0)
const divide = (a, b) => a / b

const arithmeticAverage = fork(divide, sum, length)

arithmeticAverage([5, 3, 2, 8, 4, 2]) // tagastab 4

Vaheldumine

See kombinaator võtab kaks funktsiooni ja tagastab esimese tulemuse, kui see on tõene. Vastasel juhul tagastab ta teise funktsiooni tulemuse.

Deklaratsioon

const alt = (fn, orFn) => (value) => fn(value) || orFn(value)

Näide

const users = [{
uuid: '123e4567-e89b-12d3-a456-426655440000',
name: 'William'
}]

const findUser = ({ uuid: searchesUuid }) =>
users.find(({ uuid }) => uuid === searchesUuid)

const newUser = data => ({ ...data, uuid: uuid() // luua uus uuid })

const findOrCreate = alt(findUser, newUser)

findOrCreate({ uuid: '123e4567-e89b-12d3-a456-426655440000' }) // tagastab William-objekti
findOrCreate({ name: 'John' }) // tagastab John objekti uue uuidiga.

Järjestus

See võtab vastu mitu sõltumatut funktsiooni ja annab igale neist sama parameetri. Tavaliselt ei tagasta need funktsioonid mingit väärtust.

Deklaratsioon

const seq = (...fns) => (val) => fns.forEach(fn => fn(val))

Näide

const appendUser = (id) => ({ name }) => {
document.getElementById(id).innerHTML = nimi
}

const printUserContact = pipe(
findOrCreate, // tagastab kasutaja
seq(
appendUser('#contact'), // user => void
console.log, // kasutaja => void
onContactUpdate // user => void
)
)

printUserContact(userData)
koostööbänner
etEstonian