In the previous articles we’ve seen why Functional Programming is fascinating so many developers these days.
Having a codebase made of reusable functions, high testable behaviors and no hidden state, just make software development easier.

But since the first JSMonday article, we’ve just scratched the surface of what Functional Programming is and how it makes your life easier. Today we’ll see a pure and amazing implementation of a couple of widely used methods in Object Oriented Programming: Getters and Setters.

In fact, a Lens is a pair of pure Getter and Setter which focus on a particular field of an object and respect the Lens Laws:

1. Setting something back that you just got out is a no-op

That means that when you set a value inside an object, if you immediately retrieve it, you should get back the same value you just inserted.

2. Getting something that you just set is exactly that thing

Setting a Lens value to x then immediately setting it to y is just like setting it to y directly.

3. If you put something repeatedly, the last put wins

If you’re updating an object for (let’s say) five times, once you need to get its value you should get the fifth set value (the last one).

So why should I use Lenses against standard OOP-like Getters and Setters?
Because Lenses can abstract your state more easily. As we’ve said before, Lenses are just pure functions which sets and takes back values from an object. No other manipulations to the state or side-effects are spawned during these two operations.
That means that if you need to change the whole structure of your state, you just need to change a little piece of your Lenses code to reflect these changes in your whole codebase, no more actions are required. Pretty cool, isn’t it?

Implementing Lenses

First of all, let’s create an object that will act as a store for our data:

const store = {
  firstName: "John",
  lastName: "Doe"
};

Then we need two pure functions that will set and get a value inside and from our store:

const get = (lens, store) => lens.get(store);
const set = (lens, value, store) => lens.set(value, store);

As you can see, when we call both get and set, we don’t make any kind of side effect: we just set and get a value from an object ( store).

Now, we need an accessor for our data inside the store:

const lens = (key) => ({
  get: (store) => store[key],
  set: (store, value) => ({ ...store, [key]: value })
});

Great! We now need to define the code to retrieve our desired values. For instance, let’s say we want to get both firstName and lastName from our store:

const firstNameLens = lens("firstName");
const lastNameLens  = lens("lastName");

We’re now ready to use our previously defined get and set functions to retrieve that data:

const firstName = get(firstNameLens, store);
const lastName  = get(lastNameLens, store);
    
console.log(firstName); // => "John"
console.log(lastName);  // => "Doe"

Awesome! And what if we want to set a new firstName?

const newStore = set(firstNameLens, store, "Mitch");
const newFirstNameLens = get(firstNameLens, newStore);
    
console.log(newFirstNameLens); // => "Mitch"

As you can see, we don’t want to manipulate our original store, ‘cause that would be a side effect. Instead, we just return a new store containing all the informations we need. You may have heard this concept in Redux.

We’ve just scratched the surface of what Lenses are and how they work. If you want a more detailed explanation, I would recommend this awesome talk by Flavio Corpa:

Did you like this article? Consider becoming a Patron!

This article is CC0 1.0 (Public Domain) licensed.