Lens is unidiomatic Haskell
April 24, 2014
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
Here’s a random function signature I picked from
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
lenscombinators and how to compose them, instead of enjoying our knowledge of how to compose Haskell functions. Formally,
lenstypes are Haskell function types, but while with ordinary Haskell functions you immediately see from types whether they can be composed, with
lensfunctions this is very hard in practice.
The size of the
lensAPI is comparable to the size of what I’d call «core Haskell» (i.e. more or less the
baselibrary). It is also similar in spirit to
base: 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
lens) to compose them together.
This is why I regard
lensas a language in its own. And this demonstrates why learning
lensis hard: surely learning a new language is harder than learning a new library.
Dependencies. A library as basic in its purpose as
lensideally 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 with
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
lensas 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
lensgoes 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.