code / art / projects

Haskell State of the Lens, Etc.

Jan31st, 20129:40 pm

For my own purposes, I wanted to take a tour of all the various formulations of
lenses on hackage and thought I would post the lightly-curated results here. I
just grepped the package list page for the following terms: lens, label,
record, accessor, editor, and reference. Some were not sufficiently general to
include here.

Here were the ones implementing “lens-like” functionality, loosely in order of
my opinion on their usefulness for my own purposes:

fclabels: straightforward, lenses built on underlying Point type, with support for lenses that can fail in ArrowZero

data-accessor: package description has nice background, implementation (not exported, oddly) is same as Data.Lens, apparently used to have wonky State monad implementation, seems from a similar school as “lenses” package

state-record: Lens type composed of simple setter/getter function pair, with TH generator and misc. functions for use with State monad

lenses: based on MonadState/StateT, odd formulation with no concrete type representation for the lens itself

For an analysis of various approaches to lenses which don’t necessarily have an
implementation on hackage, these slides
by Twan van Laarhoven were quite good, even without the accompanying talking
human. Conal Elliot’s Semantic editor combinators
pattern seems also relevant to mention here.

My own thoughts

The appeal of lenses for me is that they offer a first-class setter/getter on
which one can build abstractions
and more powerful libraries, not simply save the programmer some key strokes.
Lenses that simply throw an error when applied to the wrong constructor are not
suitable for this purpose.

IMHO the only package above that provides a straightforward lens type for real
algebraic data types is the seemingly unused ‘partial-lens’ variant on
Data.Lens. In which the simplified representation is:

newtype Lens a b = Lens (a -> Maybe (b -> a, b))

The ‘fclabels’ package is great, but handles failure on set only w/r/t both
the outer value and the inner value to be set. This means we can perform
validation on a data type with its setter (cool!), but means we
cannot partially apply a lens and get back a pure setter b -> a which is a
real problem for me.