Functional JavaScript: Lenses – DZone Web Dev | xxxFunctional JavaScript: Lenses – DZone Web Dev – xxx
菜单

Functional JavaScript: Lenses – DZone Web Dev

十一月 30, 2019 - MorningStar

Over a million developers have joined DZone.

{{announcement.body}}

{{announcement.title}}

Let’s be friends:

1024)” dc-slot=”ads.sl1.slot(articles[0], 0)” tags=”ads.sl1.tags(articles[0], 0)” size=”ads.sl1.size(articles[0], 0)” style=”border:0px;”>
1 && !articles[0].partner.isSponsoringArticle && (width > 1024)” dc-slot=”ads.sb2.slot(articles[0], 0)” tags=”ads.sb2.tags(articles[0], 0)” size=”ads.sb2.size(articles[0], 0)”>

Functional JavaScript: Lenses

DZone ‘s Guide to

Functional JavaScript: Lenses

In this article, we dive into functional programming with JavaScript, as we discuss how Lenses work as functional getters and setters.

Jan. 07, 20 · Web Dev Zone ·

Free Resource

Join the DZone community and get the full member experience.

Join For Free

Functional JavaScript: Lenses - DZone Web Dev

One of the most interesting talks among the ones I attended in lambda.world was Functional Lenses in JavaScript by FlavioCorpa. He talked about Functional Lenses in a practical way; what’s more, he started with his own small implementation (not for production), and then he talked about different libraries like Ramda or Monocle-TS.

The talk started with an easy-to-understand definition for those of us who are familiar with the procedural/imperative programming: “Lenses are basically functional getters and setters”. Basically, what we get with Lenses is the ability to reuse the data access of a structure in a modular and immutable way, either to obtain the data or to modify it. Later, we will see examples to better understand it.

Functional Lenses in JavaScript

Let’s start with a very small implementation of a Lens:

JavaScript

x
 

1

const Lens = (getter, setter) => {get: getter, set: setter};

Those who have not seen or do not know about functional Lenses might be asking: “and this can generate a talk of 1 hour?” We have to be aware that it is a concept of functional programming, so composition and immutability are very important concepts to consider when we use Lenses.

Continuing with our implementation, we will add types:

TypeScript

xxxxxxxxxx
1

 

1

type LensGetter<S,A> = (whole: S) => A;

2

type LensSetter<S,A> = (whole: S) => (part: A) => S;

3

4

interface Lens<S,A> {

5

    get: LensGetter<S,A>;

6

    set: LensSetter<S,A>;

7

}

8

9

xxxxxxxxxx

0

Now, we see how the getter is a simple function that receives an object (whole) and returns a piece of it (part). With the setter, we are looking to generate a new whole with the new part that we have passed to it. It’s basically the get/set functions we’re used to, right? Let’s continue creating our Getter/Setter implementations and see their use:

TypeScript

xxxxxxxxxx

1

1

12

 

1

xxxxxxxxxx

2

2

xxxxxxxxxx

3

3

xxxxxxxxxx

4

4

xxxxxxxxxx

5

5

xxxxxxxxxx

6

6

xxxxxxxxxx

7

7

xxxxxxxxxx

8

8

xxxxxxxxxx

9

9

type LensGetter<S,A> = (whole: S) => A;

0

10

type LensGetter<S,A> = (whole: S) => A;

1

11

type LensGetter<S,A> = (whole: S) => A;

2

12

type LensGetter<S,A> = (whole: S) => A;

3

As we see in our test, the get of our lens passing a User gives us its name and using the set of our lens, passes it a new name and returns the complete object with the changed name.

Here, one can think that as we code the implementation, the get can point to one thing and the set can modify another. This would not make much sense, so let’s continue. Like everything in this life (and more in the world of mathematics/programming), there are laws. Laws that must be met in order to ensure the correct functioning of, in this case, a Lens.

You may also like: Functional Programming in JavaScript.

Lens Laws

There are Lenses laws, and they are easy to understand. I will try to explain them in a simple way; please note, that you can find some useful literature about it at the end of the article.

1. (set after get) If I update with what I receive, the object does not change. (Identity)

TypeScript

type LensGetter<S,A> = (whole: S) => A;

4

1

 

1

type LensGetter<S,A> = (whole: S) => A;

5

If this law is met, we should see that the set and get must focus on the same part of the object

2. (get after set) If I update and then receive, I should receive what I have updated.

JavaScript

type LensGetter<S,A> = (whole: S) => A;

6

1

 

1

type LensGetter<S,A> = (whole: S) => A;

7

The first thing that will be executed is the set of our lens, which will return a new user with a new name. If we make the get of that new user, we should receive the new name.

3. (set after set) If I update twice, I get the updated object for the last time.

TypeScript

type LensGetter<S,A> = (whole: S) => A;

8

1

 

1

type LensGetter<S,A> = (whole: S) => A;

9

Look at the order; it executes first the internal, the user’s set with “newName.” With that object that returns to me, I change it again, but this time to “theNewName.” The last one is what we obtain. The expect reflects it.

View, Set, and Over

Now, we are going to implement three new functions: view, set, and over. These functions will be very simple, but they will help us work with Lenses:

TypeScript

type LensSetter<S,A> = (whole: S) => (part: A) => S;

0

1

 

1

type LensSetter<S,A> = (whole: S) => (part: A) => S;

1

2

type LensSetter<S,A> = (whole: S) => (part: A) => S;

2

3

type LensSetter<S,A> = (whole: S) => (part: A) => S;

3

As you can see, the three types are quite simple and will help us to work with the lenses in a much simpler way. You call the functions of the lens with the data that they touch:

TypeScript

type LensSetter<S,A> = (whole: S) => (part: A) => S;

4

1

 

1

type LensSetter<S,A> = (whole: S) => (part: A) => S;

5

2

type LensSetter<S,A> = (whole: S) => (part: A) => S;

6

3

type LensSetter<S,A> = (whole: S) => (part: A) => S;

7

So far, we have been fiddling with very specific entities. Let’s abstract from those specific types to start using generic ones:

TypeScript

type LensSetter<S,A> = (whole: S) => (part: A) => S;

8

1

 

1

type LensSetter<S,A> = (whole: S) => (part: A) => S;

9

2

0

3

1

By changing User and String for generics, such as S and A, we already have three functions that apply in all contexts. We only had to refactor the name of the function.

Now, we are going to generalize the part of the creation of the Lens together with its getters and setters.

TypeScript

2

1

 

1

3

2

4

3

5

4

6

5

7

In our prop function, as a parameter we are passing a value of type: keyof S. This type is a union type of all the public properties of the object S. In the following example, it will fail to compile if we try to assign to userProps something other than name or age:

TypeScript

8

1

 

1

9

2

interface Lens<S,A> {

0

3

interface Lens<S,A> {

1

4

interface Lens<S,A> {

2

5

interface Lens<S,A> {

3

6

interface Lens<S,A> {

4

As we can see, with a single call to a function indicating the part of the structure that we want to focus on it would be enough to have everything that we have explained in this article for now. So far so good.

Composition

And last but not least, we will work with the composition of Lenses. With the composition of Lenses, we will be able to reach the most profound data within our structure in a simpler way.

The first thing we will do is create a function that, given two Lenses, returns a composite Lens.
At the type-level, we could say, I have a Lens that speaks to type A as a data structure and B as a part of A, and I also have another Lens that knows B as a data structure and C as an internal part of it. Our function must return a Lens that knows A as a structure and lets us work with a level two part of type C:

TypeScript

interface Lens<S,A> {

5

1

 

1

interface Lens<S,A> {

6

2

interface Lens<S,A> {

7

3

interface Lens<S,A> {

8

4

interface Lens<S,A> {

9

The code is simple, and only by looking at the signature of the method, we can understand exactly what it does. From here, we will start using a slightly more complex data structure:

TypeScript

    get: LensGetter<S,A>;

0

1

17

 

1

    get: LensGetter<S,A>;

1

2

    get: LensGetter<S,A>;

2

3

    get: LensGetter<S,A>;

3

4

    get: LensGetter<S,A>;

4

5

    get: LensGetter<S,A>;

5

6

    get: LensGetter<S,A>;

6

7

    get: LensGetter<S,A>;

7

8

    get: LensGetter<S,A>;

8

9

    get: LensGetter<S,A>;

9

10

    set: LensSetter<S,A>;

0

11

    set: LensSetter<S,A>;

1

12

    set: LensSetter<S,A>;

2

13

    set: LensSetter<S,A>;

3

14

    set: LensSetter<S,A>;

4

15

    set: LensSetter<S,A>;

5

16

    set: LensSetter<S,A>;

6

17

    set: LensSetter<S,A>;

7

As a first step, what we are going to do is access, by composing Lenses, the name of the company of our contact. First, we must create two Lenses — one that focuses on the Company part of our Contact, and the one that focuses on the company name (string) of a Company:

TypeScript

    set: LensSetter<S,A>;

8

1

16

 

1

    set: LensSetter<S,A>;

9

2

}

0

3

}

1

4

}

2

5

}

3

6

}

4

7

}

5

8

}

6

9

}

7

10

}

8

11

}

9

12

0

13

1

14

2

15

3

16

4

Cool! We already have the ability to create Lenses, compose them, and work with them. That said, all the code in this article, despite working, is not recommended to use in production; our software development team in Apiumhub uses the Ramda library extensively although there are many other good libraries, such as Monocle-ts.

Where to Use a Lens

To finish, let’s talk about when is the right time to use Lenses and when NOT to use them.

I have read many articles and presentations where they talk about how to use them in a domain, but it is something that has to be well thought out.

The use case for a Lens is to create getters and setters, so we can get into issues of bad design of our domain if we end up breaking encapsulation.

I would dare to say that using domain Lenses is an anti-pattern. In certain scenarios, where they tell you to use Lenses in a domain, you see God Objects that need the help of all the possible technicalities to solve a bad design decision.

On the other hand, I see the use of Lenses in frontier layers to our domain — all DTO input by HTTP, database, events, etc.

Conclusion: Functional Lenses in JavaScript

As I mentioned above, there is a lot of literature that I used and here I leave it for you. Let me tell you that as you get deeper into the subject, you will enter a spiral of functional programming, mathematics in general and category theory in concrete (although abstract) that generates addiction.

References

Further Reading

Topics:
javascript ,lenses ,functional ,web dev ,typescript ,procedural programming

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

Web Dev Partner Resources

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.linkDescription }}

{{ parent.urlSource.name }}

· {{ parent.articleDate | date:’MMM. dd, yyyy’ }} {{ parent.linkDate | date:’MMM. dd, yyyy’ }}


Notice: Undefined variable: canUpdate in /var/www/html/wordpress/wp-content/plugins/wp-autopost-pro/wp-autopost-function.php on line 51