A naive approach to functional programming

Rafael Campos
8 min readFeb 21, 2020

--

by lindsay-henwood

I’m trying to learn functional programming, and that could be quite overwhelming.

Search for functional programming often results in a bunch of unreadable lingoes (for me at least), articles trying to wrap the F-word in new names like ‘mappable’, or people that do not understand it but are writing tutorials either way.

That isn’t what I’m trying to do here. What I’m doing is learn in public.

Some real functional programming content

First, and that is worth even if you do not keep reading to the end, check this out:

- Professor Frisby Introduces Composable Functional JavaScript by Brian Lonsdorf: This is an incredible free course and is the source of most of the stuff you are going to see here, so do a favor to yourself and watch it.

- Category Theory by Bartosz Milewski: If you’re feeling adventurous and want to discover the crazy world of the Category Theory, watch this series of videos (I’m watching for the second time, and probably I’ll need at least one more, but worth it).

Now that I pointed you to people who know what they’re talking about, I’m free to throw here that stuff that is in my head while I’m trying to figure that out.

What?! Why?!

As far as I understand, Category Theory is a way to try to structure things through abstraction.

Instead of walking in the streets trying to draw a map, we use a satellite picture for that.

We have these objects and the relations between them, and we’re forbidden to look into the objects (not because we want to punish ourselves, but to create structures that serve to more than one type of category), so all our assertions need to be inferred from those relations, called morphisms.

Let’s stop right there. Why on earth I need to engage in this math theory?! I’m not trying to understand the entire world through math. I want to become a better programmer.

Ok, I’m with you on that. But do you remember that those math guys have this constraint of only seeing things through their relations? If you take types as a Category and function as morphisms, you end up with a bunch of knowledge on how to deal with functions and can apply that to make your code better. I’m talking about composition and predictability.

So the main goal is to write code that is predictable and easy to reason about, and we can get that if we fit our functions and data structures into the Category Theory laws.

Tools

First, we need to use pure functions.

There is a bunch of rules to check a function as pure, but I’ll stick with referential transparency, that translates to I can swap my function invocation for the value it returns, like a lookup table. So, for that, no global variables, no side effects.

I knew it! I work with Web Apps, my life is side effects: DOM manipulation, users inputs, Ajax calls, are you crazy?!

Relax, take a deep breath 🧘‍♂️. We’ll continue to do all that, believe me.

Also, we need currying that’s a partial application where the end function always expects one argument (function arity 1). Languages like Haskel have that into it, but with JS we need to create a function or use a lib for that.

Ok, but there are more than pure functions. I’ll throw a bunch of loose definitions here, so we can feel more comfortable when I talk Category Theory terms (and of course, it’s valid to remember that’s what I got about them):

- Functors = A type that implements a map method (not related to iteration, but applying a function on its value) preserving composition (F(x).map(f).map(g) === F(x).map(g(f(x)))) and identity (F(x).map(x => x) = F(x)).

- Pointed Functors = Functor that implements an of method (that is a way to throw a value inside our container).

- Applicative Functors = Functor that implements the method ap (apply the value of one container on the value of another container, remember, the value can be a function).

- Semigroups = A type that implements a concat method that is associativity, that means ’Hello’ + (‘World’ + ‘!’) = (‘Hello’ + ‘World’) + ‘!’`

- Monoids = It’s a semigroup that has an empty method. In other words, it’s safe to work no matter the number of elements: `empty string + ‘all fine’ = ‘all fine’` no errors in our dear console.

- Monads = Is a pointed functor that implements a flatMap or chain method (instead of nested Monads, it’ll return a unidimensional Monad);

- Natural transformation = FunctorA(value) => FunctorB(value).

- Isomorphism = You can change from a type to another and vice-versa without losing information.

I already can see people coming with torches and forks, to punish me for all the nonsense that I put here. But hold your horses. That’s a living document from my learning process, and that’s what I get so far.

Ok, ok. But I’m starting to lose my interest. Can we see some code? I want to check how to implement all of that!

Fair enough. Let’s move on.

Identity Functor

Have you ever saw those Category Theory diagrams, where there is a dot, and an arrow that goes from that dot to itself?

Those are identity functors. In our case, where our category is a set of types, they’re endofunctors (because they map to the same set).

Ok, but why would I be interested in such a thing, a functor that maps to itself?

Because that’s our ticket for the marvelous world of composition.

We’re going to put our value inside this container (functor) and use its map method to apply a function on its value and then return another functor to keep that going, till we finish with our operations and redeem our result using fold.

First, let’s define our Identity Functor:

There are five methods here:

- map: We apply a function to our value and put the result back in a functor to keep composing.
- chain: Imagine that the function that you want to `map` returns another `Identity`. In that case, we’ll end up with `Identity(Identity(value))`. Chain flat that, resulting in a single `Identity(value)`.
- fold: It’s tough, but sooner or later, our value needs to leave the cozy and secure functor.
- inspect: Friendly console.

Wait a minute. You said five methods, but there are only four here!

Yeap, the identity call has the same effect as the of method, that is the way to insert the value inside our functor.

Either

And how about conditional constructs and error handler? Can functional programming improve that aspect of coding?

Yes, it can!

In this talk Scott Wlaschin uses this Railway Track model:

Railway Track Model

Here we have functions that can return two different things, and we have a functor that can help us with that called Either:

That serves to handle errors, if some pops up, we skip the other steps in our chain. But also for skipping unnecessary computation, imagine that we have three predicates, but we only need one to be true to pass to the next step. If the first one returns true, we don’t need to compute the next two, we skip them and move on in our pipe.

Applicative

I want to go rad and put our curried functions in our pipeline. However, I don’t want to fold just for that.

To do so, I need to apply functors to functors:

F(x => x + 10) -> F(10) = F(20)

In the end, we need a method ap in a Functor containing a function as value and call that passing another Functor with our expect argument into it:

ap: (functor) => functor.map(value)

A pointed functor with an ap method is called an applicative functor.

Typing that with TypeScript end up being a pain (TypeScript does not have higher-kinded types like Haskel, and I got lost within so many letters using HKT like in [fp-ts](https://github.com/gcanti/fp-ts)), so I cheated a little bit:

Applicative Functors are not only about currying but also they help us parallelism. In a case where we have a couple of fetch calls, and we wrap them in a functor:

Natural Transformation

We saw that we could pass a function to a map method, let’s say a => b, and get F(a) => F(b). But how about on the Functor itself F(a) => G(a)?

Let’s use our Identity and Either:

const identityToEither = <A>(i: Identity<A>) =>
i.fold(x => either<undefined, A>(undefined, x))

In this point we already know that we need to respect some law to fit in the predictable Category world:

nt(x).map(f) === nt(x.map(f))

So, we need to put our function to the test:

identityToEither(identity(10))
.map(x => x * x)
.inspect() === identityToEither(identity(10).map(x => x * x)).inspect()
// true (fireworks)

Finally, now I can understand this:

Natural Transformation

Follow the path ➡️ ⬇️ (red) is equal to go ⬇️ ➡️ (blue):

nt(F(a).map(f)) === nt(F(a)).map(f) === G

And of course, there is some more pro stuff like:

⬇️ ↘️ ➡️ + P

⬆️ ⬆️ ⬇️ ⬇️ ⬅️ ➡️ ⬅️ ➡️ + B + A + Start

Haha! Pretty funny, it’s a Konami Code, haha… But wait, I’m losing focus here. Why do I want to transform a functor in another?

You don’t want nail with a saw or cut a log with a hammer. In the same way, you use different functors for each type of task.

However, the functions of your pipeline can return different functors, and we have Natural Transformations at our disposal to fit them in a new functor when we need the methods that it has to offer. Like we took a battery from a drilling machine into a circular saw (Okay, enough of analogies).

Isomorphism

Ok by now, we’re starting to get the gist of morphisms (arrows), so let’s put some spicy on it.

We saw a bunch of arrows from a to b. But what if we also have a morphism from b to a?

In this case, they are no equal, but isomorphic. We can map a -> b and b -> a without losing information.

What?!

I guess an example it’s better than all my gibresh:

const insert = x => [x]
const extract = a => a[0]
const start = 10
const advanceOneSpace = insert(start)
const goBackOneSpace = extract(advanceOneSpace)
start === goBackOneSpace // True — I guess we’re a stuck in this Monopoly game.

That gives us the ability to use a natural transformation to temporarily put our value in another Functor, solve a task that suits its peculiarities, and then put it back into the original Functor.

Higher Kinded Types

Till now, I’m dodging the TS inability to deal with generics of generics

F<A<B>>

Here an explanation of what is HKT and how we can simulate its behavior in TS: Higher kinded types in TypeScript, static and fantasy land by gcanti.

It seems it’s time to try that out:

So now we have a Record of types. We can define them using HKT<[Identifier], [Type]> for functors holding values from a single type. Or HKT<[Identifier], [TypeA], [TypeB]> for functors that can hold A|B.

Like gcanti show in his Medium, I’m using Module Augmentation to add each one of my functors:

With that, I changed my first version of Applicative, that only can handle Identity Functors, for any Functor A, and create a new Applicative2 to deal with Functor A|B:

Work in progress…

I’m still learning and reiterating over the fundamentals trying to get functional programming.

If you want to join me on this journey: My Journal Repo

--

--

Rafael Campos

Senior React Developer @SkipTheDishes, addicted to online courses and learning new technologies