Check out a free preview of the full Hardcore Functional Architecture Patterns in JavaScript course
The "Lenses" Lesson is part of the full, Hardcore Functional Architecture Patterns in JavaScript course featured in this preview video. Here's what you'd learn in this lesson:
Brian explains that lenses are built on functors and, compose backwards going left to right, adds that it is possible to write an entire application with lenses, and demonstrates how to treat properties as functors.
Transcript from the "Lenses" Lesson
[00:00:00]
>> I've set us up already, and this is because lenses are so simple. They're so complex, it's like a really deep subject, you can do anything and everything with lenses. You can do everything you can with functors with lenses which are built on functors, and the way we're gonna just use lenses is pretty light, but know that you can do the most amazing things, you can rewrite every app in just lenses.
[00:00:33]
[LAUGH] Although, I think that a lot of people have push back because Haskell and other lens libraries defined it in terms of all these little squiggly operators and it's just like what is this? It doesn't read right, so that's one drawback of lenses in those languages. Here we just have functions and they're fine, but you can define a lens that maps over things, you can define a lens, so take a step back.
[00:01:02]
So what I've done here is I brought in the lens library from Lambda, it's a fairly naive. If you're gonna use a lens library and like really commit to lenses, I would highly recommend looking around there's one called optica I think, and then there's a few other ones out there.
[00:01:23]
But definitely wanna focus on [LAUGH] no pun intended, the top parts, the top level, simple stuff for this class. So I usually define a little namespaced object, because my lenses tend to be the same names as the things in the app. So I like to namespace it with L, [LAUGH] and what we've done is we've said LensProp('name'), and that'll be the name lens, and we have lensProp Street.
[00:01:52]
And that's the street lens lensProp address, that's the address lens, how would I use that? Well, we have view and over to view one of these things, I have this nested object and if I wanna view the street say, I'll just call view, I'll pass it the lens that I want.
[00:02:09]
Street, and street takes an address, right? So I have to actually compose[(L.address, L.street, )] lenses composed like functions, and they let me jump into the address and into the street and I can just view that on this user. So let's do that was console.res, console.log(res) and let's node lenses 0 there is is named maple.
[00:02:46]
Let's get the name L.name, Is maple. So I was able to jump in to the steeply nested structure through composition of properties, that's cool. What if do over, over allows me to modify that toUpper, so I can say hey, jump into the name, name part and let's just pull this out into a street addrStreetName [LAUGH] that I lost it.
[00:03:22]
That's fine, did I copy it? I copied it sweet all right addrStreetName, and we're just gonna say over this lens, we're gonna turn it to upper. And what's interesting to me is that this provides a very, very clean way to jump all the way down there, change it and get the whole thing back and it's immutable.
[00:03:47]
It made a copy of it along the way, and it can do so in a very efficient way. And if we're thinking about functors, and we're thinking about these properties as kind of like opening up a box and getting the next thing, and opening up that box and getting the next thing, then boxing them all back up.
[00:04:05]
That's how maps work, right? You're mapping over these things and getting out of target and then putting more back together. And that's largely how these are implemented under the hood, also means that we can start to make, I don't think this has L.mapped, but if I had a Either.of this name I could totally do that.
[00:04:29]
I could totally say, jump in and go over my either, and then get to the name, turn it to uppercase. So this could be inside a task, and we can jump inside of functors inside of properties inside of different parts of a giant structure, hit that one piece and then put it all back together.
[00:04:48]
And that's map by definition, open stuff up, let's get it out and change it and then puts it back together for you. So this stuff gets really, really powerful, we're not gonna do that right here, but I do have a lens package that does do that, if you wanna look at my GitHub.
[00:05:05]
It was just porting Edward commits lens library and most people these days are using pro functor lenses, yes.
>> Are these explicitly using something like an observer pattern, or is this more like a reader mode documentation?
>> That's a great question, so we're not holding on to a ref and having any kind of wired changes.
[00:05:32]
This is very similar to closures ref stuff, or they had that view library that's a little out of date now, but it was similar to this stuff where they kinda treated it like lenses. This is different, it's literally like treating properties as a functor and there's a Pumping them up and changing stuff and putting them back, and profunctor, lenses are implemented a little bit different give you the same power, probably more power.
[00:06:00]
And you could jump through functors and all sorts of stuff in properties, you could also do this with like a lens that says, hey, you know what? I'm gonna grab the first or the zeroth right? Should be Lens prop, whoops, 0, then we could say, yeah, we have users, right, and we'll just start with L.0.
[00:06:25]
And we could pass in users, now we're jumping through the array, of all the, I guess have to grab the first one out now to look at it. But look, we jumped through the array of users grab the first one, and then change maple. And again if I was to call mapped on a different lens library that supported that it would run through everyone and change every name and do it all on mutable.
[00:06:52]
So these are really, really powerful, they're really just getters and setters that are unmutable and let you compose like functions. You're kinda like I have this ref to the street name now, as you kinda mentioned. It's almost like a reference to a big spot in a big structure and you're able to just get in there and change it, and come back out without loosing your data.
[00:07:13]
Which is really nice, especially if you're doing this really nice composition, and you're just like in the middle of your composition, you wanna change one little value. But typically with a setter, you would throw away the entire structure, change the value and then as your composition goes, you've lost the entire structure.
[00:07:28]
Lenses preserve the structure, so as you run through your pipeline of functions, the structure is maintained, you just changed one piece.
>> This person said worth mentioning lenses composed backwards, but you already covered that earlier
>> That is worth mentioning yes for sure, normally composition would run name, street, address, bomp.
[00:07:46]
It would go right to left, this actually goes left to right yeah that's good mention, good call out. Lenses composed backwards for if you start to see how it's implemented you see why but it's weird to see that there are composed like left to right instead of right to left where you always see.
[00:08:02]
But this is a standard function composition, this is not a special lens composition.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops