Lens is unidiomatic Haskell
Published on
Edward Kmett writes:
Ironically if I had to say anything from the traffic in my inbox and on #haskell about it, it is mostly the old guard who gets disgruntled by lens.
So let me try and explain why that is. I’ll go ahead and say this:
lens
is unidiomatic Haskell.
By which I mean that lens
isn’t like any other library
that we normally use. It doesn’t follow the conventions of Haskell APIs,
on which I elaborate below.
Now let me clarify that this doesn’t necessarily mean that
lens
is a bad library. It’s an unusual library.
It’s almost a separate language, with its own idioms, embedded in
Haskell.
It is as unusual as, say, Yesod is. But unlike Yesod, which follows
Haskell’s spirit, not letter (syntax), lens
, I argue,
follows Haskell’s letter, but not its spirit.
So here’s why I think lens
violates the spirit of
Haskell:
In Haskell, types provide a pretty good explanation of what a function does. Good luck deciphering
lens
types.Here’s a random function signature I picked from
lens
:below :: Traversable f => APrism' s a -> Prism' (f s) (f a)
Despite having some (fairly basic) understanding of what prisms are, this signature tells me nothing at all.
So you have to rely on documentation much more than on types. Yeah, just like in Ruby.
Usually, types in Haskell are rigid. This leads to a distinctive style of composing programs: look at the types and see what fits where. This is impossible with
lens
, which takes overloading to the level mainstream Haskell probably hasn’t seen before.We have to learn the new language of the
lens
combinators and how to compose them, instead of enjoying our knowledge of how to compose Haskell functions. Formally,lens
types are Haskell function types, but while with ordinary Haskell functions you immediately see from types whether they can be composed, withlens
functions this is very hard in practice.The size of the
lens
API is comparable to the size of what I’d call «core Haskell» (i.e. more or less thebase
library). It is also similar in spirit tobase
: it has a big number of trivial combinations of basic functions, in order to create a base vocabulary in which bigger programs are expressed.Ordinary libraries, instead, give only basic functions/combinators, and rely on the vocabulary provided by
base
(orlens
) to compose them together.This is why I regard
lens
as a language in its own. And this demonstrates why learninglens
is hard: surely learning a new language is harder than learning a new library.Dependencies. A library as basic in its purpose as
lens
ideally should have almost no dependencies at all. Instead, other libraries should depend on it and implement its interface. (Or even do it without depending on it, as is possible withlens
.)A package implementing lenses that depends on a JSON parsing library and a gzip compression library sounds almost like a joke to me.
OTOH, it kind of makes sense if you think about
lens
as a language. It just ships with a “rich standard library”. Nice!Backward composition of lenses. It’s a minor issue, and I wouldn’t mention it if it wasn’t a great demonstration of how
lens
goes against the conventions of Haskell.
Note that I’m not trying to make a judgment here (although my tone
probably does give away my attitude towards lens
). I’m
simply explaining why people may dislike and resist it.
Nor am I trying to argue against any particular design decision of
lens
. I’m sure they all have valid rationale behind
them.
I just hope that someone will write an idiomatic Haskell library as
powerful as (or close to) lens
, with perhaps a different
set of compromises made. Otherwise, I’m afraid we all will have to learn
this new language sooner or later.