DEV Community

Discussion on: What dev topic do you think you should understand, but don't?

Collapse
 
kspeakman profile image
Kasey Speakman • Edited

A lens is just a way to get and set a part of a data structure. Seems like a "why would anyone need this?" situation at first. However, they can be composed to provide shortcut access to deeply nested states. The main place I found this useful is in doing functional UIs. Because I end up with deep state hierarchy to represent UI pieces, consisting of both records and unions. Even if it was just records, updating a nested property is pretty gross with immutability. E.g.

type C = { R : int }
type B = { C : C }
type A = { B : B }
type X = { A : A }

{x with A = {x.A with B = {x.A.B with C = {x.A.B.C with R = x.A.B.C.R + 1}}}}

So if this is something you do a lot, you can construct a lens to simplify updating R. A lens has get and set operations, but this is only set.

x |> x2rSet (fun oldR -> oldR + 1)

And you could even compose from other lenses. Here is a naive example of only the set part:

let x2aSet f x = { x with A = f x.A }
let a2bSet f a = { a with B = f a.B }
let b2cSet f b = { b with C = f b.C }
let c2rSet f c = { c with R = f c.R }
let x2rSet f x = x2aSet (a2bSet (b2cSet (c2rSet f))) x

Of course lens libraries exist to make it nicer to construct these with both get and set operations. However, I find I do not really use lens libraries. I usually just construct my own helper function "lenses" as needed.

Collapse
 
deciduously profile image
Ben Lovy • Edited

Wow, thank you! This is really helpful. I've definitely run into nested property hell. I think you're right, I've been overthinking it. I'm pretty sure I could apply this pattern to a recent project of mine - practical use is the best way to learn it.

I really appreciate the examples!