PageSourceSearch

http://www.guyzissman.com/posts/pipe-reducers/

html guyzissman.com collected 2026-09-25 22:32:07 UTC 30,125 bytes, 118 lines download raw bytes

1<!doctype html><html lang="en"><head><title data-react-helmet="true">Using Currying to Compose Reducers</title><meta charset="utf-8"/><meta http-equiv="X-UA-Compatible" content="IE=edge"/><meta name="viewport" content="width=device-width, initial-scale=1"/><meta data-react-helmet="true" name="generator" content="Phenomic 0.21.0"/><meta data-react-helmet="true" property="og:site_name" content="gzps"/><meta data-react-helmet="true" name="twitter:site" content="@Vall3y"/><meta data-react-helmet="true" name="viewport" content="width=device-width, initial-scale=1"/><meta data-react-helmet="true" property="og:type" content="article"/><meta data-react-helmet="true" property="og:title" content="Using Currying to Compose Reducers"/><meta data-react-helmet="true" property="og:url" content="http://guyzissman.com/posts/pipe-reducers/"/><meta data-react-helmet="true" property="og:image" content="http://guyzissman.com/"/><meta data-react-helmet="true" property="og:description" content="A use case demonstration of using currying for a very day-to-day need - composition of (flux) reducers"/><meta data-react-helmet="true" name="twitter:card" content="summary"/><meta data-react-helmet="true" name="twitter:title" content="Using Currying to Compose Reducers"/><meta data-react-helmet="true" name="twitter:creator" content="@Vall3y"/><meta data-react-helmet="true" name="twitter:description" content="A use case demonstration of using currying for a very day-to-day need - composition of (flux) reducers"/><meta data-react-helmet="true" name="twitter:image" content="http://guyzissman.com/"/><meta data-react-helmet="true" name="description" content="A use case demonstration of using currying for a very day-to-day need - composition of (flux) reducers"/><link data-react-helmet="true" href="https://fonts.googleapis.com/css?family=Roboto:400,300,500" rel="stylesheet" type="text/css"/><link data-react-helmet="true" rel="shortcut icon" type="image/png" href="/_/src/favicon.1031412f6e689280c78804873c5a4a61.ico"/><link rel="stylesheet" href="/phenomic.browser.e2de501d4838e0643688.css"/>
1<script data-react-helmet="true" src="http://cdn.polyfill.io/v2/polyfill.min.js?features=es6&amp;flags=gated"></script>
1<script data-react-helmet="true" async="" src="http://platform.twitter.com/widgets.js" charset="utf-8"></script>
1</head><body><div id="phenomic"><div class="_2Mf6m" data-reactroot="" data-reactid="1" data-react-checksum="1218777834"><div hidden="" data-reactid="2"><!-- react-empty: 3 --><!-- react-empty: 4 --><style data-reactid="5">@-ms-viewport { width: device-width; }</style></div><header class="_2gGRo" data-reactid="6"><div class="_1b8Fz" data-reactid="7"><a class="_163zS" href="/" data-reactid="8"><div class="qOYUx" style="color:#212121;background-color:#FFFFFF;transition:all 450ms cubic-bezier(0.23, 1, 0.32, 1) 0ms;box-sizing:border-box;font-family:Roboto, sans-serif;-webkit-tap-highlight-color:rgba(0,0,0,0);box-shadow:0 3px 10px rgba(0, 0, 0, 0.16),
2         0 3px 10px rgba(0, 0, 0, 0.23);border-radius:50%;-webkit-transition:all 450ms cubic-bezier(0.23, 1, 0.32, 1) 0ms;-moz-transition:all 450ms cubic-bezier(0.23, 1, 0.32, 1) 0ms;-moz-box-sizing:border-box;" data-reactid="9"><img class="URRrM" src="/_/src/components/Header/profile.d3c9ff63c2546005d7d9504f39f7b1c9.jpg" data-reactid="10"/></div><h1 data-reactid="11">Guy Zissman</h1><h2 data-reactid="12"><span data-reactid="13">Web Developer, Cat </span><div class="fsSMX" data-reactid="14"><span class="XDTEY" data-reactid="15">Aficionado</span></div><span data-reactid="16">, Ninja</span></h2></a><div class="spq9b" data-reactid="17"><a class="_3Dwlz" href="https://twitter.com/Vall3y" data-reactid="18"><span class="SVGInline" data-reactid="19"><svg class="SVGInline-svg" style="width: 24px;height: 24px;" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M24 4.557a9.83 9.83 0 0 1-2.828.775 4.932 4.932 0 0 0 2.165-2.724 9.864 9.864 0 0 1-3.127 1.195 4.916 4.916 0 0 0-3.594-1.555c-3.18 0-5.515 2.966-4.797 6.045A13.978 13.978 0 0 1 1.67 3.15a4.93 4.93 0 0 0 1.524 6.573 4.903 4.903 0 0 1-2.23-.616c-.053 2.28 1.582 4.415 3.95 4.89a4.935 4.935 0 0 1-2.224.084 4.928 4.928 0 0 0 4.6 3.42A9.9 9.9 0 0 1 0 19.54a13.94 13.94 0 0 0 7.548 2.212c9.142 0 14.307-7.72 13.995-14.646A10.025 10.025 0 0 0 24 4.556z"/></svg></span></a><a class="_3Dwlz" href="https://github.com/Vall3y" data-reactid="20"><span class="SVGInline" data-reactid="21"><svg class="SVGInline-svg" style="width: 24px;height: 24px;" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M12 0C5.374 0 0 5.373 0 12c0 5.302 3.438 9.8 8.207 11.387.6.11.793-.26.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.09-.745.083-.73.083-.73 1.205.085 1.84 1.238 1.84 1.238 1.07 1.834 2.806 1.304 3.49.997.108-.776.42-1.306.763-1.605-2.665-.305-5.467-1.334-5.467-5.93 0-1.312.47-2.382 1.236-3.222-.125-.303-.536-1.524.116-3.176 0 0 1.008-.322 3.3 1.23A11.51 11.51 0 0 1 12 5.803c1.02.005 2.047.138 3.006.404 2.29-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.236 1.91 1.236 3.22 0 4.61-2.807 5.625-5.48 5.922.43.372.824 1.102.824 2.222v3.293c0 .32.192.694.8.576C20.567 21.796 24 17.3 24 12c0-6.627-5.373-12-12-12z"/></svg></span></a><a class="_3Dwlz" href="https://stackoverflow.com/users/1030494/vall3y" data-reactid="22"><span class="SVGInline" data-reactid="23"><?xml version="1.0" encoding="utf-8"?>
3<!-- Generator: Adobe Illustrator 19.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
4<svg class="SVGInline-svg" style="width: 24px;height: 24px;" version="1.1"
5	 id="svg4231" inkscape:version="0.47 r22583" sodipodi:docname="New document 26" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg"
6	 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 45.2 57.6"
7	 style="enable-background:new 0 0 45.2 57.6;" xml:space="preserve">
8<style type="text/css">
9	.st0{fill:#FFFFFF;}
10</style>
11<defs>
12	
13	
14		<inkscape:perspective  id="perspective4239" inkscape:persp3d-origin="32 : 21.333333 : 1" inkscape:vp_x="0 : 32 : 1" inkscape:vp_y="0 : 1000 : 0" inkscape:vp_z="64 : 32 : 1" sodipodi:type="inkscape:persp3d">
15		</inkscape:perspective>
16	
17		<inkscape:perspective  id="perspective4219" inkscape:persp3d-origin="0.5 : 0.33333333 : 1" inkscape:vp_x="0 : 0.5 : 1" inkscape:vp_y="0 : 1000 : 0" inkscape:vp_z="1 : 0.5 : 1" sodipodi:type="inkscape:persp3d">
18		</inkscape:perspective>
19</defs>
20<sodipodi:namedview  bordercolor="#666666" borderopacity="1.0" id="base" inkscape:current-layer="layer1" inkscape:cx="-15.758389" inkscape:cy="39.724597" inkscape:document-units="px" inkscape:grid-bbox="true" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:window-height="674" inkscape:window-maximized="1" inkscape:window-width="1280" inkscape:window-x="0" inkscape:window-y="24" inkscape:zoom="3.8890873" pagecolor="#ffffff" showgrid="true">
21	</sodipodi:namedview>
22<g id="layer1" inkscape:groupmode="layer" inkscape:label="Layer 1">
23	<path id="path2830" class="st0" d="M0,33.6l4.3,0L4.2,52.9H33V33.7h4.6v23.9H0C0,57.6,0,33.6,0,33.6z"/>
24	<rect id="rect3604" x="7.1" y="45" class="st0" width="22.6" height="4.8"/>
25	
26		<rect id="rect3606" x="16.2" y="28.5" transform="matrix(9.606666e-02 -0.9954 0.9954 9.606666e-02 -22.7975 54.6425)" class="st0" width="4.9" height="22.7"/>
27	
28		<rect id="rect3606-1" x="17.5" y="20.6" transform="matrix(0.2716 -0.9624 0.9624 0.2716 -16.2691 42.5075)" class="st0" width="4.9" height="22.7"/>
29	
30		<rect id="rect3606-1-3" x="21" y="12.1" transform="matrix(0.517 -0.856 0.856 0.517 -8.7719 31.3939)" class="st0" width="4.9" height="22.7"/>
31	
32		<rect id="rect3606-1-3-7" x="28.2" y="4.3" transform="matrix(0.8129 -0.5824 0.5824 0.8129 -3.3993 20.7899)" class="st0" width="4.9" height="22.7"/>
33	
34		<rect id="rect3606-1-3-7-6" x="38.4" y="0.3" transform="matrix(0.9863 -0.1648 0.1648 0.9863 -1.3543 6.8916)" class="st0" width="4.9" height="22.7"/>
35</g>
36</svg></span></a></div></div></header><div class="_116Tm" data-reactid="24"><div class="sFWsg" data-reactid="25"><!-- react-empty: 26 --><div class="hOK0m _1z1n5" data-reactid="27"><h2 class="_1Tkeg" data-reactid="28">Using Currying to Compose Reducers</h2><div data-reactid="29"><header class="_2BJ2-" data-reactid="30"><time data-reactid="31">Wed May 17 2017</time></header></div><div class="_30RKo" data-reactid="32"><div class="phenomic-BodyContainer" data-reactid="33"><p>I have learned about currying not too long ago, but haven't been able to see uses for it in my work.
37Functional Programming, especially techniques such as currying and partial application, can be a bit unintuitive,
38and thus can take time before it sinks in your brain and comes natural.</p>
39<p>The usecase I am writing about this time is not a brilliant answer for a rare and complex problem,
40but instead a good answer for a day-to-day problem, commonly encountered when writing reducers (specifically for Flux/Redux).
41This answer is powerful but requires, in my opinion, unintuitive tools, and for that reason I hope this post can be of value to some.</p>
42<p>The problem I am talking about is combining redcuers. In this post I will be using Redux but the concepts here apply in different environments as well.</p>
43<p>A Reducer in Redux is a pure function that accepts a state and an action, and returns a new, transformed, state.</p>
44<p>For example I may have the following reducers that handle actions in a modal with an alternating view:</p>
45<pre><code class="hljs language-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">changeViewReducer</span>(<span class="hljs-params">state, action</span>
45) </span>{
46  <span class="hljs-keyword">const</span> { viewName, index, count } = action.result;
47
48  <span class="hljs-keyword">return</span> <span class="hljs-built_in">Object</span>.assign({}, state, {
49    index,
50    count,
51    <span class="hljs-attr">currentView</span>: viewName,
52  });
53}
54
55<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">startFlowReducer</span>(<span class="hljs-params">state, action</span>) </span>{
56  <span class="hljs-keyword">const</span> { viewName, index, count } = action.result;
57
58  <span class="hljs-keyword">return</span> <span class="hljs-built_in">Object</span>.assign({}, state, {
59    index,
60    count,
61    <span class="hljs-attr">currentView</span>: viewName,
62    <span class="hljs-attr">isModalOn</span>: <span class="hljs-literal">true</span>,
63  });
64}</code></pre>
65<p><code>changeViewReducer</code> would handle an action of view change inside the modal, assigning index, count (used for a widget that shows the user which view is he viewing and how many are ahead) and currentView. <code>startFlowReducer</code> would handle an action that starts the flow, which means setting the same values as the former reducer, but in addition he has to set <code>isModalOn</code>: as true.</p>
66<p>This is a small case of code repetition, but it is not a far fetched scenario that with many more reducers, our code can get out of hand. Maintanence becomes hard when action data needs to be handled differently, or state properties get renamed or moved. In order to avoid the need to track a property in all of the reducers it is used and update it accordingly, some sort of code reuse is desired.</p>
67<p>If we break down our reducers to smaller buildings blocks, we recognize two roles that are fulfilled:</p>
68<ol>
69<li>Assigning <code>{ index, count, currentView: action.viewName }</code> to the state  - which is what <code>changeViewReducer</code> does.</li>
70<li>Assigning <code>{ isModalOn: true }</code> to the state - which is what <code>startFlowReducer</code> does in addition to what the former does. Let's call it <code>showModalReducer</code>.</li>
71</ol>
72<p><a href="http://www.guyzissman.com/posts/fp-usecase/">In a former post I wrote</a> I demonstrate how by piping together small, pure, "building block" functions, we can compose functions for our complex operations. A good solution for our problem can be to compose <code>startFlowReducer</code> from the two "building blocks" I have outlined above.</p>
73<p>By composing <code>startFlowReducer</code> I mean that the result of <code>startFlowReducer</code> would be the result of the state after going through two reducers. We can use a pipe function to do that:</p>
74<pre><code class="hljs language-js"><span class="hljs-comment">// Accept a number functions, and return a function that pipes the results through the accepted functions</span>
75<span class="hljs-comment">// For example pipe(f, g, h) returns x => f(g(h(x)))</span>
76<span class="hljs-keyword">const</span> pipe = <span class="hljs-function">(<span class="hljs-params">...fns</span>) =></span> x => fns.reduce(<span class="hljs-function">(<span class="hljs-params">v, f</span>) =></span> f(v), x)</code></pre>
77<p>When trying to apply the above pipe function to our reducers we run into a problem. A function only returns one value, but our reducers require two arguments, state and action. If we were to pipe our reducers using this pipe function, the second reducer would be called with <code>undefined</code> as the action (which might work in this case since <code>showModalReducer</code> does not use the action result, but it won't work for other reducers that do).</p>
78<p>In order to solve that, we have to change the interface of our function to fit into the pipe. While the <code>state</code> param changes in between reducer calls, our <code>action</code> param does not. We can take advantage of that, by currying the reducers. <a href="https://medium.com/javascript-scene/curry-or-partial-application-8150044c78b8">Eric Elliott gives a very simple definition of currying</a>:</p>
79<blockquote>
80<p>Curry: A function that <strong>takes a function with multiple parameters</strong> as input and <strong>returns a function with exactly one parameter</strong>.</p>
81</blockquote>
82<p>In our case, this is what we're trying to do:</p>
83<pre><code class="hljs language-js"><span class="hljs-keyword">const</span> curryReducer = <span class="hljs-function">(<span class="hljs-params">fn, action</span>) =></span> state => fn(state, action)</code></pre>
84<p>The curried reducers accept the action in advanced, before called. For example our curried <code>changeViewReducer</code> would essentially look something like this:</p>
85<pre><code class="hljs language-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">curriedChangeViewReducer</span>(<span class="hljs-params">action</span>
85) </span>{
86  <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-params">state</span> =></span> changeViewReducer(state, action) <span class="hljs-comment">// The returned function only needs `state` as a param</span>
87}
88
89<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">curriedShowModalReducer</span>(<span class="hljs-params">action</span>) </span>{
90  <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-params">state</span> =></span> showModalReducer(state, action)
91}</code></pre>
92<p>The results are functions that accept state, and call our reducer with the action we provided at the moment of currying. We can pipe them.</p>
93<pre><code class="hljs language-js"><span class="hljs-keyword">const</span> startFlowReducer = <span class="hljs-function">(<span class="hljs-params">state, action</span>) =></span> {
94  <span class="hljs-keyword">const</span> curriedChangeViewReducer = curryReducer(changeViewReducer, action)
95  <span class="hljs-keyword">const</span> curriedShowModalReducer = curryReducer(showModalReducer, action)
96
97  <span class="hljs-keyword">return</span> pipe(curriedChangeViewReducer, curriedShowModalReducer)
98}</code></pre>
99<p>The operation of currying reducers then piping them can be written into a function:</p>
100<pre><code class="hljs language-js"><span class="hljs-keyword">const</span> pipeReducers = <span class="hljs-function">(<span class="hljs-params">...reducers</span>) =></span>
101  (state, action) =>
102    pipe(
103      ...reducers.map(<span class="hljs-function"><span class="hljs-params">fn</span> =></span> curryReducer(fn, action))
104    )(state);
105
106<span class="hljs-keyword">const</span> startFlowReducer(changeViewReducer, showModalReducer)</code></pre>
107<p>Breaking this down, <code>pipeReducers</code> is a function that accepts a number of reducer functions, and returns one combined reducer function.
108The combined reducer function runs pipe on all of the reducers after they have been curried so they can be piped. Calling that pipe function with the state would yield us our desired result.</p>
109</div></div><hr data-reactid="34"/><div data-reactid="35"><hr data-reactid="36"/><div data-reactid="37"><ul class="lfRQE" data-reactid="38"><li data-reactid="39"><div class="_1eeKI" data-reactid="40"><a class="_2mH_g" href="/posts/pipe-reducers/" data-reactid="41">Using Currying to Compose Reducers</a><div class="_2l2N1" data-reactid="42"><time data-reactid="43">Wed May 17 2017</time></div><div class="_1-sEy" data-reactid="44"><!-- react-text: 45 -->A use case demonstration of using currying for a very day-to-day need - composition of (flux) reducers<!-- /react-text --><!-- react-text: 46 --> <!-- /react-text --></div><a class="niUXw" href="/posts/pipe-reducers/" data-reactid="47"><span role="button" class="_1mmZb _1w6mX" data-reactid="48">Read More →</span></a></div></li><li data-reactid="49"><div class="_1eeKI" data-reactid="50"><a class="_2mH_g" href="/posts/fp-usecase/" data-reactid="51">A Use Case of Function Composition</a><div class="_2l2N1" data-reactid="52"><time data-reactid="53">Mon May 08 2017</time></div><div class="_1-sEy" data-reactid="54"><!-- react-text: 55 -->A use case demonstrating when applying classic functional programming concepts is appropriate<!-- /react-text --><!-- react-text: 56 --> <!-- /react-text --></div><a class="niUXw" href="/posts/fp-usecase/" data-reactid="57"><span role="button" class="_1mmZb _1w6mX" data-reactid="58">Read More →</span></a></div></li></ul></div></div></div></div></div><footer class="_1Ustb" data-reactid="59"><p data-reactid="60"><a href="https://phenomic.io" class="_21LuU" data-reactid="61"><!-- react-text: 62 -->Powered by <!-- /react-text --><span class="MMp-P" data-reactid="63">&lt;Phenomic /&gt;</span></a></p></footer></div></div>
109<script>window.__COLLECTION__ = [{"layout":"PageError","route":"404.html","description":"","__filename":"404.md","__url":"/404.html","__resourceUrl":"/404.html","__dataUrl":"/404.html.7f6093458561a2a9bb2178ef2cba0f0f.json"},{"title":"About Phenomic base theme","hero":"https://farm4.staticflickr.com/3949/15589950511_3675b15e59_k.jpg","cta":{"label":"Discover Phenomic now","link":"https://phenomic.io/","props":{"big":true}},"description":"You are currently viewing Phenomic base theme. For now Phenomic does not provide a native way to extend themes but since it's based on…","__filename":"about.md","__url":"/about/","__resourceUrl":"/about/index.html","__dataUrl":"/about/index.html.0aea56a9b9275dff733aa3556eabd122.json"},{"title":"Hi there. I'm Guy","metaTitle":"Guy Zissman","description":"Guy Zissman personal website","layout":"Homepage","__filename":"index.md","__url":"/","__resourceUrl":"/index.html","__dataUrl":"/index.html.70e81bb6d1458514b401b73d38c5322a.json"},{"title":"A Use Case of Function Composition","description":"A use case demonstrating when applying classic functional programming concepts is appropriate","date":"2017-05-08T00:00:00.000Z","layout":"Post","__filename":"posts/fp-usecase.md","__url":"/posts/fp-usecase/","__resourceUrl":"/posts/fp-usecase/index.html","__dataUrl":"/posts/fp-usecase/index.html.db91082fe2c478a2a59599cb8380eb22.json"},{"title":"Using Currying to Compose Reducers","description":"A use case demonstration of using currying for a very day-to-day need - composition of (flux) reducers","date":"2017-05-17T00:00:00.000Z","layout":"Post","__filename":"posts/pipe-reducers.md","__url":"/posts/pipe-reducers/","__resourceUrl":"/posts/pipe-reducers/index.html","__dataUrl":"/posts/pipe-reducers/index.html.8296d5e1f324fff37b2cb41534e27aaa.json"}];window.__INITIAL_STATE__ = {"pages":{"/posts/pipe-reducers/":{"head":{"title":"Using Currying to Compose Reducers","description":"A use case demonstration of using currying for a very day-to-day need - composition of (flux) reducers","date":"2017-05-17T00:00:00.000Z","layout":"Post"},"body":"<p>I have learned about currying not too long ago, but haven't been able to see uses for it in my work.\nFunctional Programming, especially techniques such as currying and partial application, can be a bit unintuitive,\nand thus can take time before it sinks in your brain and comes natural.</p>\n<p>The usecase I am writing about this time is not a brilliant answer for a rare and complex problem,\nbut instead a good answer for a day-to-day problem, commonly encountered when writing reducers (specifically for Flux/Redux).\nThis answer is powerful but requires, in my opinion, unintuitive tools, and for that reason I hope this post can be of value to some.</p>\n<p>The problem I am talking about is combining redcuers. In this post I will be using Redux but the concepts here apply in different environments as well.</p>\n<p>A Reducer in Redux is a pure function that accepts a state and an action, and returns a new, transformed, state.</p>\n<p>For example I may have the following reducers that handle actions in a modal with an alternating view:</p>\n<pre><code class=\"hljs language-js\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">changeViewReducer</span>(<span class=\"hljs-params\">state, action</span>
109) </span>{\n  <span class=\"hljs-keyword\">const</span> { viewName, index, count } = action.result;\n\n  <span class=\"hljs-keyword\">return</span> <span class=\"hljs-built_in\">Object</span>.assign({}, state, {\n    index,\n    count,\n    <span class=\"hljs-attr\">currentView</span>: viewName,\n  });\n}\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">startFlowReducer</span>(<span class=\"hljs-params\">state, action</span>) </span>{\n  <span class=\"hljs-keyword\">const</span> { viewName, index, count } = action.result;\n\n  <span class=\"hljs-keyword\">return</span> <span class=\"hljs-built_in\">Object</span>.assign({}, state, {\n    index,\n    count,\n    <span class=\"hljs-attr\">currentView</span>: viewName,\n    <span class=\"hljs-attr\">isModalOn</span>: <span class=\"hljs-literal\">true</span>,\n  });\n}</code></pre>\n<p><code>changeViewReducer</code> would handle an action of view change inside the modal, assigning index, count (used for a widget that shows the user which view is he viewing and how many are ahead) and currentView. <code>startFlowReducer</code> would handle an action that starts the flow, which means setting the same values as the former reducer, but in addition he has to set <code>isModalOn</code>: as true.</p>\n<p>This is a small case of code repetition, but it is not a far fetched scenario that with many more reducers, our code can get out of hand. Maintanence becomes hard when action data needs to be handled differently, or state properties get renamed or moved. In order to avoid the need to track a property in all of the reducers it is used and update it accordingly, some sort of code reuse is desired.</p>\n<p>If we break down our reducers to smaller buildings blocks, we recognize two roles that are fulfilled:</p>\n<ol>\n<li>Assigning <code>{ index, count, currentView: action.viewName }</code> to the state  - which is what <code>changeViewReducer</code> does.</li>\n<li>Assigning <code>{ isModalOn: true }</code> to the state - which is what <code>startFlowReducer</code> does in addition to what the former does. Let's call it <code>showModalReducer</code>.</li>\n</ol>\n<p><a href=\"http://www.guyzissman.com/posts/fp-usecase/\">In a former post I wrote</a> I demonstrate how by piping together small, pure, \"building block\" functions, we can compose functions for our complex operations. A good solution for our problem can be to compose <code>startFlowReducer</code> from the two \"building blocks\" I have outlined above.</p>\n<p>By composing <code>startFlowReducer</code> I mean that the result of <code>startFlowReducer</code> would be the result of the state after going through two reducers. We can use a pipe function to do that:</p>\n<pre><code class=\"hljs language-js\"><span class=\"hljs-comment\">// Accept a number functions, and return a function that pipes the results through the accepted functions</span>\n<span class=\"hljs-comment\">// For example pipe(f, g, h) returns x => f(g(h(x)))</span>\n<span class=\"hljs-keyword\">const</span> pipe = <span class=\"hljs-function\">(<span class=\"hljs-params\">...fns</span>) =></span> x => fns.reduce(<span class=\"hljs-function\">(<span class=\"hljs-params\">v, f</span>) =></span> f(v), x)</code></pre>\n<p>When trying to apply the above pipe function to our reducers we run into a problem. A function only returns one value, but our reducers require two arguments, state and action. If we were to pipe our reducers using this pipe function, the second reducer would be called with <code>undefined</code> as the action (which might work in this case since <code>showModalReducer</code> does not use the action result, but it won't work for other reducers that do).</p>\n<p>In order to solve that, we have to change the interface of our function to fit into the pipe. While the <code>state</code> param changes in between reducer calls, our <code>action</code> param does not. We can take advantage of that, by currying the reducers. <a href=\"https://medium.com/javascript-scene/curry-or-partial-application-8150044c78b8\">Eric Elliott gives a very simple definition of currying</a>:</p>\n<blockquote>\n<p>Curry: A function that <strong>takes a function with multiple parameters</strong> as input and <strong>returns a function with exactly one parameter</strong>.</p>\n</blockquote>\n<p>In our case, this is what we're trying to do:</p>\n<pre><code class=\"hljs language-js\"><span class=\"hljs-keyword\">const</span> curryReducer = <span class=\"hljs-function\">(<span class=\"hljs-params\">fn, action</span>) =></span> state => fn(state, action)</code></pre>\n<p>The curried reducers accept the action in advanced, before called. For example our curried <code>changeViewReducer</code> would essentially look something like this:</p>\n<pre><code class=\"hljs language-js\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">curriedChangeViewReducer</span>(<span class=\"hljs-params\">action</span>
109) </span>{\n  <span class=\"hljs-keyword\">return</span> <span class=\"hljs-function\"><span class=\"hljs-params\">state</span> =></span> changeViewReducer(state, action) <span class=\"hljs-comment\">// The returned function only needs `state` as a param</span>\n}\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">curriedShowModalReducer</span>(<span class=\"hljs-params\">action</span>) </span>{\n  <span class=\"hljs-keyword\">return</span> <span class=\"hljs-function\"><span class=\"hljs-params\">state</span> =></span> showModalReducer(state, action)\n}</code></pre>\n<p>The results are functions that accept state, and call our reducer with the action we provided at the moment of currying. We can pipe them.</p>\n<pre><code class=\"hljs language-js\"><span class=\"hljs-keyword\">const</span> startFlowReducer = <span class=\"hljs-function\">(<span class=\"hljs-params\">state, action</span>) =></span> {\n  <span class=\"hljs-keyword\">const</span> curriedChangeViewReducer = curryReducer(changeViewReducer, action)\n  <span class=\"hljs-keyword\">const</span> curriedShowModalReducer = curryReducer(showModalReducer, action)\n\n  <span class=\"hljs-keyword\">return</span> pipe(curriedChangeViewReducer, curriedShowModalReducer)\n}</code></pre>\n<p>The operation of currying reducers then piping them can be written into a function:</p>\n<pre><code class=\"hljs language-js\"><span class=\"hljs-keyword\">const</span> pipeReducers = <span class=\"hljs-function\">(<span class=\"hljs-params\">...reducers</span>) =></span>\n  (state, action) =>\n    pipe(\n      ...reducers.map(<span class=\"hljs-function\"><span class=\"hljs-params\">fn</span> =></span> curryReducer(fn, action))\n    )(state);\n\n<span class=\"hljs-keyword\">const</span> startFlowReducer(changeViewReducer, showModalReducer)</code></pre>\n<p>Breaking this down, <code>pipeReducers</code> is a function that accepts a number of reducer functions, and returns one combined reducer function.\nThe combined reducer function runs pipe on all of the reducers after they have been curried so they can be piped. Calling that pipe function with the state would yield us our desired result.</p>\n","__filename":"posts/pipe-reducers.md","__url":"/posts/pipe-reducers/","__resourceUrl":"/posts/pipe-reducers/index.html","__dataUrl":"/posts/pipe-reducers/index.html.8296d5e1f324fff37b2cb41534e27aaa.json","type":"Post"}}}</script>
109<script src="/phenomic.browser.e2de501d4838e0643688.js"></script>
109<script>
vendor: 345 bytes, lines 109-115
109
110  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
111  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
112  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
113  })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
114
115  ga('create', '
115UA-98798401-1
vendor: 39 bytes, lines 115-117
115', 'auto');
116  ga('send', 'pageview');
117
118</script>
118</body></html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.