The lens package
The combinators in Control.Lens provide a highly generic toolbox for composing families of getters, folds, traversals, setters and lenses.
A Getter a b c d is just any function (a -> c), which we've flipped into continuation passing style, forall r. (c -> r) -> a -> r and decorated with Const to obtain
type Getter a b c d = forall r. (c -> Const r d) -> a -> Const r b
Everything you can do with a function, you can do with a Getter, but note that because of the continuation passing style (.) composes them in the opposite order.
Since it is only a function, every Getter obviously only retrieves a single value for a given input.
A Fold a b c d is a generalization of something Foldable. It allows you to extract multiple results from a container. A Foldable container can be characterized by the behavior of foldMap :: (Foldable t, Monoid m) => (c -> m) -> t c -> m. Since we want to be able to work with monomorphic containers, we generalize this signature to forall m. Monoid m => (c -> m) -> a -> m, and then decorate it with Const to obtain
type Fold a b c d = forall m. Monoid m => (c -> Const m d) -> a -> Const m b
Every Getter is a valid Fold that simply doesn't use the Monoid it is passed.
Everything you can do with a Foldable container, you can with with a Fold and there are combinators that generalize the usual Foldable operations in Control.Lens.
A Traversal a b c d is a generalization of traverse from Traversable. It allows you to traverse over a structure and change out its contents with monadic or applicative side-effects. Starting from traverse :: (Traversable t, Applicative f) => (c -> f d) -> t c -> f (t d), we monomorphize the contents and result to obtain
type Traversal a b c d = forall f. Applicative f => (c -> f d) -> a -> f b
Every Traversal can be used as a valid Fold, because given a Monoid m, we have an Applicative for (Const m). Everything you can do with a Traversable container, you can with with a Traversal, and there are combinators that generalize the usual Traversable operations in Control.Lens.
A Setter a b c d is a generalization of fmap from Functor. It allows you to map into a structure and change out the contents, but it isn't strong enough to allow you to enumerate those contents. Starting with fmap :: Functor f => (c -> d) -> f c -> f d we monomorphize the type to obtain (c -> d) -> a -> b and then decorate it with Identity to obtain
type Setter a b c d = (c -> Identity d) -> a -> Identity b
Every Traversal is a valid Setter, since Identity is Applicative.
Everything you can do with a Functor, you can do with a Setter, and there are combinators that generalize the usual Functor operations in Control.Lens.
A Lens a b c d is a purely functional reference.
While a Traversal was a valid Fold, it wasn't a valid Getter. To make the Applicative for Const it required a Monoid for the argument we passed it, which a Getter doesn't recieve.
However, the instance of Functor for Const requires no such thing. If we weaken the type requirement from Applicative to Functor for Traversal, we obtain
type Lens a b c d = forall f. Functor f => (c -> f d) -> a -> f b
Every Lens is a valid Setter, choosing f = Identity.
Every Lens is a valid Fold that doesn't use the Monoid it is passed.
Every Lens is a valid Traversal that only uses the Functor part of the Applicative it is supplied.
Every Lens is a valid Getter, choosing f = Const r for an appropriate r
Since every Lens is a valid Getter it follows that it must view exactly one element in the structure.
The lens laws follow from this property and the desire for it to act like a Functor when used as a Setter.
Note that all of these types are type aliases, and you can compose these lenses with mere function compositon.
This is a generalization of the well-known trick for (.).(.) or fmap.fmap, and their less well-known cousins foldMap.foldMap traverse.traverse. It follows because each one is a function between values of type (x -> f y) and the composition takes the intersection of supplied functionality for you automatically!
For a longer description of why you should care about lenses, and an overview of why we use 4 parameters a, b, c, and d instead of just 2, see http://comonad.com/reader/2012/mirrored-lenses/.
Sometimes you won't need the flexibility those extra parameters afford you and you can use
type Simple f a b = f a a b b
to describe a Simple Lens, Simple Traversal or Simple Setter.
Note: If you merely want your library to provide lenses you may not have to actually import any lens library at all. For, say, a Simple Lens Bar Foo, just export a function with the signature:
foo :: Functor f => (Foo -> f Foo) -> Bar -> f Bar
and then you can compose it with other lenses using nothing more than (.) from the Prelude.
You can derive lenses automatically for many data types using Control.Lens.TH, and if a container is fully characterized by its lenses, you can use Control.Lens.Representable to automatically derive Functor, Applicative, Monad, and Derivable.
- No changelog available
For package maintainers and hackage trustees