How is most \"pure\" way to manipulate DOM in script written in \"functional\" way.
For example if I simply need to change element width should I use typical syntax lik
Every meaningful program must eventually carry out some effects, whether it is in functional or imperative style. The idea of functional programming is to shift theses effects to the edge of a program, so that a large portion remains pure. This makes it much easier to reason about it.
But how can this be put to practice?
Javascript functions are usually defined as multi-argument functions. Hence we can't defer their evaluation at the calling side:
// pure
const add = (x, y) => x + y;
const log = x => console.log(x);
// impure
log(add(2, 3));
It's the same with the imperative style:
let x = 2, y = 3;
// impure
console.log(x + y);
The consequence is that the pure computation (add
) can not be separated from the effect (log
). This isn't a big deal for this sketch, but as soon as your program grows bigger, these scattered effects impair its readability and comprehensibility.
To prevent this behavior functions must become composable, i.e. their last argument must be partially applicable:
// pure
const comp = (f, g) => x => f(g(x));
const add = x => y => x + y;
const log = x => console.log(x);
const addAndLog = comp(log, add(2));
// impure
addAndLog(3);
You might want to look into currying for more information.
Using thunks we can move the effects even further to the edges. A thunk is just a function that expects no arguments and thus represents a deferred computation:
// pure
const comp = (f, g) => x => f(g(x));
const add = x => y => x + y;
const log = x => () => console.log(x);
const addAndLog = comp(log, add(2));
const eventuallyLog = addAndLog(3); // still pure
// impure (release the effect)
eventuallyLog(); // invoke the thunk
You might want to look into IO monads for more information.
// pure
const on = (type, element) => f => {
element.addEventListener(type, f, true);
return () => element.removeEventListener(type, f, true);
}
const compose = (...fs) => x => fs.reduce((acc, f) => f(acc), x);
const countFrom = x => () => (x++, x);
const log = x => console.log(x);
const filter = pred => f => x => pred(x) ? f(x) : x;
const even = x => x % 2 === 0;
const concat = y => x => x + y;
const filterEven = filter(even);
const clickStream = on("click", document);
const computation =
compose(countFrom(0), filterEven(compose(concat("!"), log)));
// impure (release the effects)
console.log("click on the section above");
clickStream(computation);
As a side effect (pun intended)
compose(countFrom(0), filterEven(compose(concat("!"), log)))
reads like plain English. I was recently told that this is not a desirable property. Well, I disagree.