Notes on Composing software by Eric Eliott

Summing up Eric Eliott composing software
profile photo
Julien Zolli
Composing Software: The Book
"Composing Software", the hit blog post series on functional programming and software composition in JavaScript is now a best selling book on Leanpub. Also available in print. On February 18th, 2017...
Composing Software: The Book
Il semble que la perfection soit atteinte non quand il n'y a plus rien à ajouter, mais quand il n'y a plus rien à retrancher. Saint-Exupéry

Curry

javascript
const pipe = (...fns) => x => fns.reduce((y, f) => f(y), x); const trace = label => value => { console.log(`${ label }: ${ value }`); return value; }; const g = n => n + 1; const f = n => n * 2;/* Now the function application order runs top-to-bottom: */ const h = pipe( g, trace('after g'), f, trace('after f'), );h(20); /* after g: 21 after f: 42 */
This trace function is beautiful.
At first you don't understand it well and then, everything is clear.
This curried (one argument at a time) with data last (the value) enables the "point free" call to this function that we see in the pipe.
Here is what it would have looked like with multiple arguments
javascript
const pipe = (...fns) => x => fns.reduce((y, f) => f(y), x); const trace = (label, value) => { console.log(`${ label }: ${ value }`); return value; }; const g = n => n + 1; const f = n => n * 2; const h = pipe( g, // the trace() calls are no longer point-free, // introducing the intermediary variable, `x`. x => trace('after g', x), f, x => trace('after f', x), ); h(20);
Function with multiple fat arrows seem difficult to grasp when they are actually extremely easy to read.
In our case of two fat arrows, if you apply a first argument (here the label), it returns a function, if you give this function a new argument it executes its definition
"Functions in a pipeline must expect exactly one argument."
javascript
// Tiny, recursive autocurry const curry = ( f, arr = [] ) => (...args) => ( a => a.length === f.length ? f(...a) : curry(f, a) )([...arr, ...args]);

Functional mixins

javascript
const flying = o => { let isFlying = false; return Object.assign({}, o, { fly () { isFlying = true; return this; }, isFlying: () => isFlying, land () { isFlying = false; return this; } }); }; const bird = flying({}); console.log( bird.isFlying() ); // false console.log( bird.fly().isFlying() ); // true const quacking = quack => o => Object.assign({}, o, { quack: () => quack }); const pipe = (...fns) => x => fns.reduce((y, f) => f(y), x); const createDuck = quack => pipe( flying, quacking(quack) )({}); const duck = createDuck('Quack!'); console.log(duck.fly().quack());
I use and recommend HOCs (Higher Order Components) with function composition to compose UI components.
Use the simplest practical implementation. Start on the left and move to the right only as needed: pure functions > factories > functional mixins > classes.

Factory functions in ES6

javascript
const withConstructor = constructor => o => ({ // create the delegate [[Prototype]] __proto__: { // add the constructor prop to the new [[Prototype]] constructor }, // mix all o's props into the new object ...o }); const pipe = (...fns) => x => fns.reduce((y, f) => f(y), x); // or `import pipe from 'lodash/fp/flow';` // Set up some functional mixins const withFlying = o => { let isFlying = false; return { ...o, fly () { isFlying = true; return this; }, land () { isFlying = false; return this; }, isFlying: () => isFlying } }; const withBattery = ({ capacity }) => o => { let percentCharged = 100; return { ...o, draw (percent) { const remaining = percentCharged - percent; percentCharged = remaining > 0 ? remaining : 0; return this; }, getCharge: () => percentCharged, getCapacity: () => capacity }; }; const createDrone = ({ capacity = '3000mAh' }) => pipe( withFlying, withBattery({ capacity }), withConstructor(createDrone) )({}); const myDrone = createDrone({ capacity: '5500mAh' }); console.log(` can fly: ${ myDrone.fly().isFlying() === true } can land: ${ myDrone.land().isFlying() === false } battery capacity: ${ myDrone.getCapacity() } battery status: ${ myDrone.draw(50).getCharge() }% battery drained: ${ myDrone.draw(75).getCharge() }% remaining `); console.log(` constructor linked: ${ myDrone.constructor === createDrone } `);

Problems with class

Compare the class:
javascript
class User { constructor ({userName, avatar}) { this.userName = userName; this.avatar = avatar; } }const currentUser = new User({ userName: 'Foo', avatar: 'foo.png' });
Vs the equivalent factory…
javascript
const createUser = ({ userName, avatar }) => ({ userName, avatar }); const currentUser = createUser({ userName: 'Foo', avatar: 'foo.png' });

Composable data types

javascript
const t = value => { const add = n => t(value + n); return Object.assign(add, { toString: () => `t(${ value })`, valueOf: () => value }); };

Algebraic Data Types

Image without caption

Functor and categories

functor data type is something you can map over. It’s a container which has an interface which can be used to apply a function to the values inside it. When you see a functor, you should think “mappable”. Functor types are typically represented as an object with a .map() method that maps from inputs to outputs while preserving structure. In practice, “preserving structure” means that the return value is the same type of functor (though values inside the container may be a different type).
Image without caption
Related posts
post image
Functional programming
Start thinking declaratively
Why move from imperative to declarative code
post image
Creating a new query language specialized in event processing
post image
Core concepts of Potions Reactive framework
Powered by Notaku