Inleiding tot combinatoren

Combinatoren zijn functies op een hoger niveau waarmee je functies, variabelen of andere combinatoren kunt combineren. Meestal bevatten ze geen declaraties van hun eigen variabelen of bedrijfslogica. Ze zijn de enige die de besturing doorspoelen in een functieprogramma.

In dit artikel zal ik proberen er een paar te behandelen:

Tik op

Een combinator is erg handig voor functies die niets teruggeven. Het neemt de functie waar de parameter naartoe gaat en geeft deze terug.

Verklaring

const tap = (fn) => (value) => {
fn(waarde);
waarde retourneren;
};

Imperatief voorbeeld

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

axios
.get('http://localhost')
.then({data } => {
setItems(data)
console.log(gegevens)
onLoadData(data)
}).then(...) // geeft ongedefinieerd terug - de gegevens in de belofte zijn gewijzigd

Declaratief voorbeeld

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

axios
.get('http://localhost')
.then(({data }) => data)
.then(tap(setItems)) // (data) => { setItems(data); return data }
.then(tap(console.log)) // een dan = een functie = een verantwoordelijkheid
.then(tap(onLoadData))
.then(...) // nog steeds toegang tot oorspronkelijke gegevens
// makkelijk te onderhouden open/close principe

Curry

Het splitst de argumenten van een functie en maakt het mogelijk om ze opeenvolgend aan te roepen.

Verklaring

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

return curied

};


Voorbeeld

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

return curied

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

const currySum = curry(som)
/*
mogelijke oproepen
currySum(a)(b)(c),
currySum(a)(b,c),
currySum(a,b)(c),
currySum(a,b,c)
*/

currySum(1) // (b, c) => 1 + a + b of (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 delerBy = curry((a, b) => b / a)
const multiplyBy = curry((a, b) => a * b)

const divideByTwo = delenDoor(2)
divideByTwo(10) // geeft 5 terug

const multiplyByFive = multiplyBy(5)
multiplyByFive(10) // geeft 50 terug

Pijpen/Componeren

Door compositie is het mogelijk om gegevens door te geven van de ene functie naar de andere. Het is belangrijk dat de functies hetzelfde aantal argumenten aannemen. Het verschil tussen pipe en compose is dat de eerste de functie van de eerste tot de laatste uitvoert, en compose ze vanaf het einde aanroept.

Verklaring

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

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

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

Imperatief voorbeeld

const sinus = (val) => Math.sin(val * Math.PI / 180) // niet leesbaar
 sinus(90) // geeft 1 terug

Declaratief voorbeeld

const sinus = pijp(
vermenigvuldig(Math.PI) // ↓ waarde * Math.PI
delenDoor(180), // ↓ waarde * Math.PI / 180
Math.sin, // ↓ Math.sin(val * Math.PI / 180)
)

const sinus = samenstellen(
Math.sin, // ↑ Math.sin(val * Math.PI / 180)
delenDoor(180), // ↑ waarde * Math.PI / 180
multiplyBy(Math.PI) // ↑ waarde * Math.PI
)

sinus(90) // geeft 1 terug

Vork

De fork combiner is handig in situaties waarin je een waarde op twee manieren wilt verwerken en het resultaat wilt combineren.

Verklaring

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

Voorbeeld

const lengte = (array) => array.lengte
const som = (array) => array.reduce((a, b) => a + b, 0)
const delen = (a, b) => a / b

const rekenkundig gemiddelde = fork(delen, som, lengte)

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

Afwisseling

Deze combinator neemt twee functies en geeft het resultaat van de eerste terug als deze waar is. Anders geeft hij het resultaat van de tweede functie terug.

Verklaring

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

Voorbeeld

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

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

const newUser = data => ({ ...data, uuid: uuid() // nieuwe uuid maken })

const findOrCreate = alt(findUser, newUser)

findOrCreate({ uuid: '123e4567-e89b-12d3-a456-426655440000' }) // retourneert William-object
findOrCreate({naam: 'John' }) // retourneert John object met nieuwe uuid

Volgorde

Het accepteert veel onafhankelijke functies en geeft dezelfde parameter door aan elk van hen. Meestal geven deze functies geen waarde terug.

Verklaring

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

Voorbeeld

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

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

printUserContact(userData)
vaandel samenwerking
nl_NLDutch