Lots of use-once temporary variables. I used to do this too. I'd come up with creative ways to name those variables to sort of "self-document" what was happening. But it's generally a bad idea, here are some reasons why:

The more variables you create, the more likely you are to have a typo.

The operations performed on the data are surrounded by a lot of syntax noise.

Variable names don't take the place of comments.

Naming needless variables is an unnecessary mental load.

There are only two hard things in Computer Science: cache invalidation and naming things.
-- Phil Karlton

But what should we do instead? Clearly this isn't going to be easy code to work with:

return{...(JSON.parse(localStorage.getItem('userInfo'))),cache:{}}

Yikes! 😲

While ugly, the code above does show that it is possible to express this transformation as a series of function calls with no variables. The output of one function is the input of the next function, and so on. We want to pipe values between functions. One solution is to write a function that allows us to make this abstraction. So how do we do this?

As you can see, this function takes an initialValue and uses reduce to feed the output of one function into the input of the next one. If you are not sure why I use reduce here, part I of my reduce tutorial may be helpful. Now we can refactor the original code like so:

returnpipeline(// get user information stored as a string in local storagelocalStorage.getItem('userInfo'),// parse the stringJSON.parse,// reset the cache to an empty objobj=>({...obj,cache:{}}))

Isn't this much cleaner? It is easy to see how data flows from top to bottom.

There are two common challenges some developers may have with this style:

Types. How do you type a pipeline function?

Logging (for debugging). How do you log data between the pipeline chain?

We will address these questions here:

The Typed Version

There is a way to express this function using a self-referencing type, but that is complex and warrants a separate article. For those who are curious, this is what such a type would more or less look like:

Soultrain is a compact functional library written in Typescript.

Please note that this library is still experimental and there will be breaking changes. If you are kicking the tires on this within another experimental project, it is best to lock it down to a specific version.

Install

npm install soultrain

Soultrain

Soultrain is a compact functional library written in Typescript. It is inspired by Ramda and container-style programming based Algebraic Data Types.

Motivation

Ramda is a great tool, however, it relies on JS DOC comment specifications to provide type data. While this is good, Typescript provides superior static-typing. Thus this library aims to provide general purposes functional tools with much better static-typing.

Overview

This section will be completed later. From a high-level, the library provides smart curry, pipe, and pipeline functions with good type support. It currently provides two Monads, a Maybe and a Transduce. The former works similar to other Maybe libraries, however, it provides…

returnpipeline(// get user information stored as a string in local storagelocalStorage.getItem('userInfo'),// parse the stringJSON.parse,// log the data,noteLog('data before cache removal'),// reset the cache to an empty objobj=>({...obj,cache:{}}))

As an exercise for the reader, one could also create a function that conditionally triggers the debugger.

Functional Programming

The pipeline function, the eager sibling of pipe and compose, will invite you to write more functions. It will also demonstrate to you how powerful currying can be; and even inform you on the order your function parameters should be.

To illustrate this, notice the arrow function in our pipeline example. Isn't obj a single-use variable? All we're using it for is as a placeholder for our data... just a few character to the right!

Turns out that by making sure our source data is the last parameter and by using curried functions, we can simplify our code even further. Below I've written how this could be expressed in vanilla JS--notice that we want to override keys for the incoming object. It will be the second parameter--thus moving from left to right, overriding the second object, obj2, with the object in the first parameter, obj1.

So instead of placing obj2 to the right of the object spread, we reverse the order and place it on the left:

constmergeLeft=curry(// spread the second parameter first(obj1,obj2)=>({...obj2,...obj1}))

If you're unfamiliar with curry, it basically modifies our function so that it can be used as if it were also written this way:

constmergeLeft=obj1=>obj2=>({...obj2,...obj1})

Now our solution becomes:

returnpipeline(// get user information stored as a string in local storagelocalStorage.getItem('userInfo'),// parse the stringJSON.parse,// log the data,noteLog('data before cache removal'),// reset the cache to an empty objmergeLeft({cache:{}}))

Piping encourages us to write small, re-usable functions that perform specific tasks and that take the source data as the last parameter. This is similar to commands on the UNIX or Linux terminal. It's a functional programming concept that has been around for a long time. And like a great classic, it is a principle that proves itself better with time.

ES-Next

Piping data between functions is a great alternative to creating single-use variables which are only used to pass data from one function to another. Many programming languages recognize this and have an operator to perform this task. For JavaScript, there is the proposed pipeline operator

A proposal for adding the simple-but-useful pipeline operator to JavaScript.

ESNext Proposal: The Pipeline Operator

This proposal introduces a new operator |> similar to
F#OCamlElixirElm,
Julia,
Hack,
and LiveScript,
as well as UNIX pipes. It's a backwards-compatible way of streamlining chained function calls in a readable, functional manner, and provides a practical alternative to extending built-in prototypes.

⚠ Warning: The details of the pipeline syntax are currently unsettled. There are two competing proposals under consideration. This readme is a minimal proposal, which covers the basic features of the pipeline operator. It functions as a strawman for comparing the tradeoffs of the competing proposals.