How To Simplifiy Code By Removing Variables
Tags: javascript, typescript, functional, pipeline
Published: 2019-03-21 09:12:35

Have you ever found yourself doing something that looks similar to this pattern?

```typescript
const userInfoFromStorage = localStorage.getItem('userInfo')
const userInfoParsed = JSON.parse(userInfoFromStorage)
const userInfoClearedCache = { ...userInfoParsed, cache: {} }  
return userInfoClearedCache
```

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. 

```markdown
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:

```javascript
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? 

Here is one solution using `reduce`:

```javascript
const pipeline = ( initialValue, ...fns ) => (
  fns.reduce( 
    ( val, fn ) => fn( val ), 
    initialValue 
  )
)
```

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](https://dev.to/babak/why-you-should-use-reduce-instead-of-loops----part-i-5dfa) may be helpful. Now we can refactor the original code like so:

```javascript

return pipeline(
  // get user information stored as a string in local storage
  localStorage.getItem('userInfo'),
  // parse the string
  JSON.parse,
  // reset the cache to an empty obj
  obj => ({ ...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:

1. Types. How do you type a `pipeline` function?
2. 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:

https://github.com/babakness/soultrain/blob/v0.0.43/src/type/_experiements.ts#L18

Alternatively, we can simply use TypeScript's function overloading to express how the type should evolve. For example:

```typescript
function pipeline<A,B>( a: A,  ab: (a: A) => B ): B
function pipeline<A,B,C>( a: A,  ab: (a: A) => B, bc: (a: B) => C ): C
function pipeline<A,B,C,D>( a: A, ab: (a: A) => B, bc: (a: B) => C, cd: (c: C) => D):D
// and so on
```

This is essentially how `pipeline` works in my library Soultrain. 

{% github https://github.com/babakness/soultrain %}


The benefit of the typed version is getting the compiler to double-check the code. Here is an example that the JavaScript version would not catch:

<img src="https://thepracticaldev.s3.amazonaws.com/i/qnm6vket3cyuahxan7k5.png" width="500" />


The compiler has noticed that `roo` is not a property on the object returned by `processor.process`

# Logging / Debugging

To log data flowing between the functions in the pipeline, one could simply use a log function that both logs and returns the data it receives. With types it could be written as such:

```typescript
const log = <A>(data: A): A => (console.log(data),data)
```

Or, to log with a message one could define log like this:

```typescript
const noteLog = (note: unknown) => (
  <A>(data: A): A => ( console.log(note, data), data )
)
```


Which can be used thus:

```javascript
return pipeline(
  // get user information stored as a string in local storage
  localStorage.getItem('userInfo'),
  // parse the string
  JSON.parse,
  // log the data,
  noteLog('data before cache removal'),
  // reset the cache to an empty obj
  obj => ({ ...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:

```javascript
const mergeLeft = 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:

```javascript
const mergeLeft = obj1 => obj2 => ({ ...obj2, ...obj1 })
```


Now our solution becomes:

```javascript
return pipeline(
  // get user information stored as a string in local storage
  localStorage.getItem('userInfo'),
  // parse the string
  JSON.parse,
  // log the data,
  noteLog('data before cache removal'),
  // reset the cache to an empty obj
  mergeLeft({ 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

{% github https://github.com/tc39/proposal-pipeline-operator %}

Which would allow us to rewrite our original example like this:

```javascript
return (
 // get user information stored as a string in local storage
 localStorage.getItem('userInfo'),
  // parse the string
  |> JSON.parse
  // reset the cache to an empty obj
  |> obj => ({ ...obj, cache: {} })
) 
```

Until then, we can benefit from using the `pipeline` function today to avoid unnecessary variable declarations.