Currying and Redux Middleware

Sean
4 min readApr 13, 2021

Currying allows us to create middleware for Redux that doesn’t interfere with any of its functionality.

I can create a curried function that logs a sentence built by calling that function multiple times with different arguments:

function logger(geeting) {
return function(name) {
console.log(greeting, 'My name is ', name)
}
}

logger('Hi!')('Bob')

// Hi! My name is Bob

It will only log the sentence the second time it’s called.

Using this technique we can pass first the redux store then store.dispatch to a function that logs the state of the store each time dispatch is called:

function logger(store) {
return function(dispatchArg) {
return function(action) {
const result = dispatchArg(action)
console.log('next state', store.getState())
return result
}
}
}

const newDispatch = logger(store)(store.dispatch)

Notice I only called logger twice, this is because the dispatch function it returns will now be used like it normally is, accepting an action, except it'll also run the extra code we gave it!

This isn’t how dispatch is used in practice. The point of using currying is so that we don't have to mutate the original dispatch variable.

Instead the logger function can be passed to applyMiddleWare which will integrate it with store.dispatch subtly.

Functional programming separates the concept of passing variables and expressing values. If you think about passing a variable in javascript.

const num = 9
const nine = num

console.log(nine)

You express the variables in exactly the same way you assign them.

We can do exactly the same with a function, except when it’s expressed we have to call it by adding the parentheses:

const num = () => 9
const nine = num
console.log(nine())

Another difference is we mutate a variable like:

let nums = { first: 1 }nums.second = 2console.log(nums)// { first: 1, second: 2 }

How could this be copied in a functional way?

let nums = () => ({ first: 1 })

Nope. Now we can't change the value without overwriting the whole function.

let nums = item => ({ first: 1, ...item })

Now we have to pass nums an argument otherwise it'll break.

let nums = item => (
!item ?
{ first: 1 }
:
{ first: 1, ...item }
)

console.log(nums({ second: 2 }))
// { first: 1, second: 2 }

That's better, but very different. Now nums represents multiple possibilities rather than a single variable.

We can have it express extra values in the object when we call it but we have to decide on what they are at the time the function is expressed. nums doesn’t hold on to the second: 2 value, it just expresses whatever argument you give it when it’s called.

If we curry nums we can pass and store the second: 2 value:

let nums = item => () => (
!item ?
{ first: 1 }
:
{ first: 1, ...item }
)

nums = nums({ second: 2 })

console.log(nums())
// { first: 1, second: 2 }

Functional programming swaps the balance between dynamic mutations and dynamic expressions. What do I mean by that?

An object variable can be mutated in any way the dev chooses but once it's changed it can only express the values it has.

let myName = { value: 'Sean' }

console.log(myName.value)
console.log(myName.value)
console.log(myName.value)
console.log(myName.value)
// Sean

A function cannot be mutated easily. Even the curried example above can only be added to once and then it must either be expressed or completely overwritten.

But it can be expressed in a very controlled but mutable fashion.

let greeting = name => `Hi! I'm ${name}`

console.log(greeting('Sean'))
console.log(greeting('Tim'))
console.log(greeting('Scott'))
console.log(greeting('Harry'))

It makes sense that this kind of programming would suite the browser environment so well. If you think about an application’s stack there’s a database, a server and a client.

What should a database be good at? A database is storage and persistence. It should reliably take care of data but it should also be able to retrieve it quickly and concisely. It’s the memory.

What should a server be good at? A server is essentially middleware, and routing. It’s the gatekeeper to the store, it also needs to do a lot of data conversion. It’s a translator and formulator.

What should the client be good at? The client is the user interface, it must be responsive and interactive. It expresses the values stored and translated by the db and server. It’s the mouthpiece and spokesman of the system.

Functions allow us to express unknown values at run-time but they also constrain the way they are used which allows those values to stay relatively predictable to the application.

Unlike an object which can be assigned any type of value, a function can reject unwanted types and return values the application expects making our code both controlled and dynamic.

--

--