Adhere to Functional Programming…a bit

Vue v3 has been updated to include the new Composition API, which, like React’s hooks update a couple of years back, is fully embracing functional programming patterns.

Both Hooks and the Composition API are both reflections of the fact that functional programming has taken the dev world by storm in the past few years.

I started doing some more serious reading in to functional concepts about two years ago because I wanted to finally understand what a Monad is. Tbh I still don’t fully understand them, but I did get a good understanding of functional principles.

Many devs will know this by now, so to quickly outline them:

  1. Functions should be able to stand in for a literal value.
  2. They should only accept a single argument.
  3. They shouldn’t mutate their values.
  4. They shouldn’t rely on or cause side effects (like using other functions from the global scope or adding to the window).

I’ve found that keeping close to these principles can make my code super clean and also very optimised, but I’ve also found that when I push them too far my code can slip into being pretentious, like it’s written to be unreadable.

The tipping point

I got some really good advice a few years ago from a dev who said, “write stupid code first, then make it gradually smarter”.

Let’s have a look at a bit of the script I wrote to make some social share buttons at each phase. From stupid to succinct and then into pretentious. I wanna try and pinpoint where the tipping point is.

Ok this is the stupedest version of the code, it goes step by step. First I grab the element, a twitter icon, by it’s id. Next I add a click event listener that opens a new window with the Twitter share link, encodeURI just makes sure the spaces and special chars format properly.

This works fine but it’s not re-usable and it only works for Twitter.

Let’s wrap it in a function, make id and url arguments and call the function passing in the Twitter-specific parts rather than hard-coding them.

Now it’s a step toward being functional — well at least it is a function!— , but there’s a couple of problems. Firstly I don’t like that window.location.href is being used inside the function, it means we can only ever use this function for the current window’s href.

The other problem is there’s no error handling here, el could evaluate to undefined which would break everything when I try to access el.onclick.

To solve the onclick problem we’ll use addEventListener instead, this way we can just use ?. to access it without fear of throwing any errors.

The other thing I’ve done is moved window.location.href into the upper scope so now the function isn’t doing mutating url as much.

That’s better, now the function isn’t even specifically for social sharing, it’s just a helper to add any url to any element’s click event.

But this still really isn’t a functional function. It takes more than one argument, it uses document.getElementById, window.open and encodeURI, which are all side-effects, it technically mutates url with encodeURI and it doesn’t return anything!

If we’re going to make addUrlToOnclick properly functional, it can’t rely on document.getElementById, we have to get the element first, then pass it in as an argument:

Next, we want to return a value so that’ll be the element, with the click event added to it:

[Does element.addEventListener count as a mutation? Donno, never mind.]

Oh also, window and encodeURI also need to be used outside of the function so we’ll just use a callback function as the second argument.

We can’t call this addUrlToOnclick anymore because we removed that functionality. Let’s call it addEventToElement.

Also we still have too may arguments, functional programming only allows one. Let’s have addUrlToOnclick return another function that accepts the second argument:

Here’s how it’ll get called:

Now this looks pretty good, it’s reusable — if a bit verbose — and it’s also pretty explicit. But we’re still relying on closures to add our callback without making more functions, so we could go further.

Let’s go further.

What we want to do is pass a plain function in as our callback so let’s make that function first:

But this will break because windowOpen calls as soon as you pass it in the argument so lets have it return a third function:

Now this is getting messy so I’d also make some of the arguments into variables to give them clearer names and then pass them into the function:

Now the code — like at the start — isn’t very re-usable, so let’s just use encodeURI directly inside windowOpen

…and make elementById into a function as well…

Now everything is a function:

Now, I don’t know about you but I think this has gone a bit far. If I wanted to re-use addEventToElement I’d have to also drag in another two functions as well.

I’ve basically just ended up re-writing addEventListener. I’ve been forced to make my function too low-level to do the job that I wanted it to do.

Also this looks super confusing…to me. I don’t think it’s very clear what’s going on.

Which phase should I have stopped?

I think many would say at the end of Phase 5

This is where the code looks most like a React hook. It’s clear what’s going on, the implementation is pretty transparent and it’s functional but not pretentious.

But I actually stopped at Phase 4

This was the most re-usable and feature-full phase of the script.

I had to use the function on three separate elements and, while the Phase 5 version was the most transparent, this one did what I needed and did it with a much smaller footprint.

But it’s not at all functional.

Is functional programming pretentious?

That’s not at all what I’m saying here. I really like functional programming, it makes me think about programming in a new light and it allows for some really interesting patterns.

Functional programming is cool, and it’s responsive, and learning it will make you better and programming but there’s no need to feel intimidated by the code chads out there. It’s more important to be clear than clever.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store