Lenses & Composition

February 06, 2018

A lens is really just a function a -> b that we represent backwards1 and with an extra Functor f parameter lying around:

type Lens' a b = Functor f => (b -> f b) -> (a -> f a)

What does this mean for function composition?

Normal function composition looks like this:

(.) :: (b -> c) -> (a -> b) -> (a -> c)

f :: a -> b
g :: b -> c

g . f :: (a -> c)

We often have to read code that looks like this:

g . f $ x

This means “start with x, then run f on it, and run g after that.” This sentence reads opposite from how the code reads!

What about for lenses? Here we have f' and g' which behave similarly in some sense to f and g from before:

f' :: Functor f => (b -> f b) -> (a -> f a)
--  ≈ a -> b
g' :: Functor f => (c -> f c) -> (b -> f b)
--  ≈ b -> c

f' . g' :: Functor f => (c -> f c) -> (a -> f a)
--       ≈ a -> c

In the lens world, ^. behaves kind of like a flipped $ that turns lenses into getters, which lets us write code like this:

x ^. f' . g'

This means “start with x, then get f' from it, then get g' after that.” The sentence reads just like the code!

This is pretty cool, because it means that lenses (which are “functional” getters) read almost exactly like property access (which are “imperative” getters). Same concise syntax, but with an elegant semantics.


  1. I’m exaggerating a bit here 😅 To see what I really mean, see this post.

Union Types in Flow & Reason

Union types are powerful yet often overlooked. At work, I’ve been using Flow which thankfully supports union types. But as I’ve refactored more of our code to use union types, I’ve noticed that our bundle size has been steadily increasing! Continue reading

Case Exhaustiveness in Flow

Published on April 15, 2018

Some Intuition on Lenses

Published on February 06, 2018