Functional programming and DOM manipulation

前端 未结 1 512
北荒
北荒 2021-02-06 01:29

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

相关标签:
1条回答
  • 2021-02-06 01:46

    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?

    Make functions composable

    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.

    Wrap effects in thunks

    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.

    An (almost) real world example

    // 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.

    0 讨论(0)
提交回复
热议问题