Lenses & Composition

February 5, 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.

Read More

Intersection Types in Sorbet are Surprisingly Common

Conventional knowledge is that union types are common and intersection types are rare. But actually that’s not the case—intersection types show up in nearly every program Sorbet type checks thanks to control flow. Continue reading

What makes type checking Ruby hard?

Published on December 29, 2019

Surgery on Code from the Command Line

Published on July 30, 2019