Function compositionhas got to be my favorite part of functional programming. I hope to provide you with a good real world example so not only do you understand function composition, but so you can begin using today!

The Basics

We have to walk before we can run, so let’s start with the boring stuff that you need to know.

Function composition is a mathematical concept that allows you to combine two or more functions into a new function.

You have probably stumbled across this example when googling function composition. If you haven’t yet, I assure you, you will.

const add = x => y => x + y const multiply = x => y => x * y const add2Multiply3 = compose(multiply(3), add(2))

I myself am guilty of using this example and what I failed to realize is that the student is not yet able to see how this can be practically applied in their codebase today . Instead they are comparing that example with something like this:

const value = (x + 2) * 3

It is hard to see why you would prefer the functional example.

A teachers failure to properly provide good real world examples will result in a students failure to understand why.

Hopefully I can do a better job of demonstrating the power of function composition.

Back toBasics

A key to function composition is having functions that are composable. A composable function should have 1 input argument and 1 output value.

You can turn any function into a composable function by currying the function. I’ll expand on currying in another article, but you should still be able to follow along without knowing what currying is.

You might do html stuff, so that’s a good point to start. Let’s make a tag . (I’m going to be working with strings here, but you could also do this with DOM).

const tag = t => contents => `<\${t}>\${contents}</\${t}>` tag('b')('this is bold!') > <b>this is bold!</b>

Because I curried the function (broke up the arguments so they can be sent in one at a time), I can also create new functions like this:

const bold = tag('b') const underline = tag('u')

I also want a function to handle tags with attributes like <div class="title">...</div> . So I’ll add another function to handle this use case.

const encodeAttribute = (x = '') => x.replace(/"/g, '"') const toAttributeString = (x = {}) => Object.keys(x) .map(attr => `\${encodeAttribute(attr)}="\${encodeAttribute(x[attr])}"`) .join(' ') const tagAttributes = x => c => `<\${x.tag}\${x.attr?' ':''}\${toAttributeString(x.attr)}>\${c}</\${x.tag}>`

Sprinkle in a little refactoring to combine it all into these four functions…

const encodeAttribute = (x = '') => x.replace(/"/g, '"') const toAttributeString = (x = {}) => Object.keys(x) .map(attr => `\${encodeAttribute(attr)}="\${encodeAttribute(x[attr])}"`) .join(' ') const tagAttributes = x => (c = '') => `<\${x.tag}\${x.attr?' ':''}\${toAttributeString(x.attr)}>\${c}</\${x.tag}>` const tag = x => typeof x === 'string' ? tagAttributes({ tag: x }) : tagAttributes(x)

Now we can call tag with a string or an object .

tag('b')('this is bold!') > <b>this is bold!</b> tag({ tag: 'div', attr: { 'class': 'title' }})('this is a title!') > <div>this is a title!</div> Making Something Real

Now that you’ve made it through that boring stuff, you deserve some fun code.

Let’s use that fancy new tag function to create something tangible. We can use something easy and something familiar, bootstrap’s list group .

<ul class="list-group"> <li class="list-group-item">Cras justo odio</li> <li class="list-group-item">Dapibus ac facilisis in</li> <li class="list-group-item">Morbi leo risus</li> <li class="list-group-item">Porta ac consectetur ac</li> <li class="list-group-item">Vestibulum at eros</li> </ul>

First, let’s create a function for each of these tags and listGroupItems to support multiple listGroupItem s.

const listGroup = tag({ tag: 'ul', attr: { class: 'list-group' }}) const listGroupItem = tag({ tag: 'li', attr: { class: 'list-group-item' }}) const listGroupItems = items => items.map(listGroupItem) .join('') listGroup() > <ul class="list-group"></ul> listGroupItem('Cras justo') > <li class="list-group-item">Cras justo</li> listGroupItems(['Cras justo', 'Dapibus ac']) > <li class='list-group-item'>Cras justo</li> > <li class='list-group-item'>Dapibus ac</li> listGroup(listGroupItems(['Cras justo', 'Dapibus ac'])) > <ul class='list-group'> > <li class='list-group-item'>Cras justo</li> > <li class='list-group-item'>Dapibus ac</li> > </ul> If we look at the structure of the list-group html we can see that there is one outer element that contains multiple children. Since it will always be created this way, it seems a little verbose to call listGroup(listGroupItems([‘Cras justo’, ‘Dapibus ac’])) to render the list every time. I should just be able to call listGroup([‘Cras justo’, ‘Dapibus ac’]) . The function should know what I want to do. To do this I’ll start by renaming listGroup to listGroupTag . That way I can create a new listGroup function that will encapsulate the call to listGroupTag(listGroupItems([])) . const listGroupTag = tag({ tag: 'ul', attr: { class: 'list-group' }}) const listGroup = items => listGroupTag(listGroupItems(items)) Function Composition

For those of you that skipped the whole article and scrolled all the way down to this section, you might be disappointed. Composing the functions is actually the easiest part of the whole process. After you have created your functions to be composable, they just kind of snap together.

Take a look at the code below. Any time you recognize this pattern, the functions can be easily composed.

const listGroup = items => listGroupTag(listGroupItems(items))

When composed together, the result will look similar to the original, listGroupTag on the left, followed by listGroupItems, and then items on the right.

const listGroup = items => compose(listGroupTag, listGroupItems)(items)

Let’s look at them side-by-side so we can see the similarities and differences.

listGroupTag (listGroupItems (items)) compose(listGroupTag, listGroupItems)(items)

When functions are composed together, they are read from right to left just like regular functions.

Because

1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责；
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性，不作出任何保证或承若；
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。