Johdanto yhdistelmiin
Kombinaattorit ovat korkeamman tason funktioita, joiden avulla voit yhdistää funktioita, muuttujia tai muita kombinaattoreita. Yleensä ne eivät sisällä omien muuttujiensa tai liiketoimintalogiikan julistuksia. Ne ovat ainoat, jotka huuhtelevat ohjausta funktio-ohjelmassa.
Tässä artikkelissa yritän käsitellä muutamia niistä:
- Napauta
- Currying
- Putki/sommittele
- Haarukka
- Vuorottelu
- Jakso
Napauta
Kombinaattori on erittäin hyödyllinen funktioissa, jotka eivät palauta mitään. Se ottaa funktion, johon parametri menee, ja sitten se palautetaan.
Ilmoitus
const tap = (fn) => (value) => {
fn(arvo);
return value;
};
Imperatiivinen esimerkki
const [items, setItems] = useState(() => [])
axios
.get('http://localhost')
.then({ data } => {
setItems(data)
console.log(data)
onLoadData(data)
}).then(....) // palaa määrittelemättömänä - lupauksen tietoja on muutettu.
Deklaratiivinen esimerkki
const [items, setItems] = useState(() => [])
axios
.get('http://localhost')
.then(({ data }) => data)
.then(tap(setItems)) // (data) => { setItems(data); return data }
.then(tap(console.log)) // yksi then = yksi funktio = yksi vastuu
.then(tap(onLoadData))
.then(....) // edelleen pääsy alkuperäisiin tietoihin.
// helppo ylläpitää open/close-periaatetta
Currying
Se jakaa funktion argumentit ja mahdollistaa niiden kutsumisen peräkkäin.
Ilmoitus
const curry = (fn) => {
const curried = (...args) => {
if (fn.length !== args.length){
return curried.bind(null, ...args)
}
return fn(...args);
};
return curried
};
Esimerkki
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)
/*
mahdolliset kutsut
currySum(a)(b)(c),
currySum(a)(b,c),
currySum(a,b)(c),
currySum(a,b,c)
*/
currySum(1) // (b, c) => 1 + a + b tai (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) // palauttaa 5.
const multiplyByFive = multiplyBy(5)
multiplyByFive(10) // palauttaa 50 %.
Putki/sommittele
Komposition avulla on mahdollista siirtää tietoja funktiosta toiseen. On tärkeää, että funktiot ottavat saman määrän argumentteja. Piipun ja koosteen ero on siinä, että ensin mainittu suorittaa funktiot ensimmäisestä viimeiseen, ja kooste kutsuu niitä lopusta alkaen.
Ilmoitus
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),
arvo);
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),
arvo);
Imperatiivinen esimerkki
const sine = (val) => Math.sin(val * Math.PI / 180) // ei luettavissa.
sine(90) // palauttaa 1
Deklaratiivinen esimerkki
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) // palauttaa 1
Haarukka
Haarukkayhdistelijä on hyödyllinen tilanteissa, joissa haluat käsitellä arvoa kahdella tavalla ja yhdistää tuloksen.
Ilmoitus
const fork = (join, fn1, fn2) => (arvo) => join(fn1(arvo), fn2(arvo));
Esimerkki
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]) // palauttaa 4.
Vuorottelu
Tämä yhdistelmä ottaa kaksi funktiota ja palauttaa ensimmäisen tuloksen, jos se on tosi. Muussa tapauksessa se palauttaa toisen funktion tuloksen.
Ilmoitus
const alt = (fn, orFn) => (arvo) => fn(arvo) || orFn(arvo)
Esimerkki
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() // luo uusi uuid })
const findOrCreate = alt(findUser, newUser)
findOrCreate({ uuid: '123e4567-e89b-12d3-a456-426655440000' }) // palauttaa William-olion.
findOrCreate({ name: 'John' }) // palauttaa John-olion uudella uuidilla.
Jakso
Se hyväksyy useita itsenäisiä funktioita ja välittää jokaiselle niistä saman parametrin. Tyypillisesti nämä funktiot eivät palauta mitään arvoa.
Ilmoitus
const seq = (...fns) => (val) => fns.forEach(fn => fn(val))
Esimerkki
const appendUser = (id) => ({ name }) => {
document.getElementById(id).innerHTML = nimi
}
const printUserContact = pipe(
findOrCreate, // palauttaa käyttäjän
seq(
appendUser('#contact'), // user => void
console.log, // käyttäjä => void
onContactUpdate // user => void
)
)
printUserContact(userData)
