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.

Read More

Prefer .then() over .catch()

When designing asynchronous APIs that could error in Flow, prefer using `.then` for both successful and failure cases. Flow exposes a relatively unsafe library definition for the `.catch` method, so it’s best to avoid it if you can. Continue reading

Union Types in Flow & Reason

Published on April 19, 2018

Case Exhaustiveness in Flow

Published on April 15, 2018