Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.Sign up
- What are goals of this project?
- How do you determine what falls into the scope of
- Will you be splitting out a separate
data-lensgood enough for you?
- Where can I learn about lenses?
- How does this relate to ‘Bananas, Lenses, Envelopes and Barbed Wire’?
- When should I define or use a
Getterrather than a function?
- How can I build lenses without depending on the package itself?
Q: What are the goals of this project? Why does this project exist?
lens library exists to provide more composable versions of the abstractions you already know how to use in Haskell. Virtually every Haskell programmer already knows how to work with functors and functions or
Traversable containers — we simply provide you with a vocabulary for composing them and working with their compositions along with specializations of these ideas to monomorphic “containers” such as
One goal of
lens has been to provide a consistent vocabulary that lets you access and work with pure data of any sort, while retaining the ability to be able to reason about your code with laws.
Q: How do you determine what falls into the scope of
As a rule
lens tries to incur no package dependency that is not either in the Haskell Platform or required to implement its own functionality.
That said, we’ve tried to provide a “Batteries Included” API that provides useful tools for operating with anything that does fall into its scope, and we’ve wandered outside of these lines occasionally, when to do otherwise would have a performance impact or the dependency was small and had large impact on the generality of the library.
Q: Will you be splitting out a separate
lens-core package? The
build-depends: list has a lot of stuff I don’t use.
A: This is on the surface a very reasonable request, but it doesn’t work very well in practice. To implement even basic
lens functionality requires a number of language extensions.
Consider the extensions needed to break out the types and combinators for lenses, traversals, etc. separately from the rest of the package. We’d need
Rank2Types to even write
Lens. Working with indexed lenses needs
TypeFamilies, because without type equality coercions type inference for them is unusable. By the time we get done with
Iso we’ve brought in a whole pile of extensions and already tied ourselves to the Glorious Glasgow Haskell Compiler.
Even with just this functionality, implementing these combinators already dragged in the
mtl and a large number of dependencies. We had to define a large number of internal types along the way, types we actually expose elsewhere to the user in the API, like
Bazaar, which have useful
Comonad instances. This forces us to implement them correctly, orphan those instances or remove functionality.
Since we’re already tied to GHC, and the Template Haskell code generator for
makeClassy is key to making the library usable, it makes sense to incorporate that directly into the base package. Implementing that brings with it dependencies on
The combinators in
Control.Lens.Plated are generally useful when working with any
Traversal and we use
data-lens good enough for you?
A: Most of the power of
lens comes from working with generalizations of the notion of a van Laarhoven lens. None of
yall, etc. provided this style of lens and most had attempted to generalize the idea of a
Lens by shoe-horning a
Monad or some other notion of partiality into the middle of it. This came at the expense of the laws that made working with lenses worth doing.
Providing lenses for any of these libraries required picking up a dependency on a package, which means that it is really impractical or impossible for a reasonably “core” package on hackage to reasonably provide lenses for them.
However, the style of lenses used by
lens can be defined using functions from the
Prelude. No dependencies need be incurred to supply lenses, merely to use them!
There really wasn’t a good library for working with van Laarhoven lens families when
lens was started.
lens-family had tried to be that library, but it required 3 separate packages to work with and used the same names between its Haskell 98
lens-family-core package and the main
lens-family package. Moreover, it is shackled by Haskell 98. That said, the combinators for working with lenses from
lens-family-core are mostly compatible with the lenses provided by or for use by this package.
By adopting and generalizing van Laarhoven lenses we are able to both provide rigorous laws for each of our lens variants and provide a better user experience, because almost any lens, prism, traversal, isomorphism, etc. that the user goes to reach for can be used with any combinator and it just “does the right thing”. Unlike earlier lens libraries explicit conversions are almost entirely absent, and the combinatorial explosion of combinators is eliminated along at least one axis.
Q: Where can I learn more about lenses in general?
A: There are a number of resources online. Here are a few:
- The first answer to “lenses, fclabels, data-accessor – which library for structure access and mutation is better” on Stack Overflow goes into some depth about how you can think about lenses and the different design trade-offs between the lens libraries that were then extant in Haskell.
- The author, Edward Kmett, has video on youtube from his presentation on “Lenses: A Functional Imperative” covering the approach originally used in scalaz library for Scala. Slides are available online.
Seth Tisue gave an excellent introduction to lenses as provided by Miles Sabin’s shapeless library in Scala. The turtle example from his talk is available in the
- If you prefer to learn by example, the
examples/folder contains a number of fully worked examples, including a playable game of pong and a brainfuck interpreter that were written by nandykins to learn his way around
- The Derivation page of this wiki covers some of the motivation about how and why van Laarhoven lenses compose so well.
- Russell O’Connor wrote a blog post on Polymorphic Update with van Laarhoven Lenses, which inspired Edward Kmett to post on his blog, the Comonad.Reader, Mirrored Lenses the immediate precursor to the approach taken here.
- The form of lenses we use was originally derived by Twan van Laarhoven in CPS based functional references A number of posts about lenses or “functional references” can be found on his blog.
- The term “van Laarhoven” lens was coined by Russell O’Connor in “Functor is to Lens as Applicative is to Biplate: Introducing Multiplate”. The concept now known as a Traversal can be viewed as a “biplate family” mixing the vocabulary from the Mirrored Lenses post and Russell’s paper.
Bazaarcomonad, which is used to characterize traversals and to implement many combinators is equivalent in power to an indexed version of the Cartesian Store comonad from Russell’s “Functor is to Lens as Applicative is to Biplate: Introducing Multiplate”, which is called the Kleene Store by him elsewhere. That type goes back farther to Twan van Laarhoven, who called it a
FunListin “A non-regular data type challenge”.
- The name lens goes back to Benjamin Pierce’s work on bidirectional programming. The notion of a lens in this package corresponds to his notion of a “very well-behaved lens”.
- Before that they were a folklore technique in the functional programming world, known as “functional references”:
- Janis Voightländer’s work on bidirectional programming is also related.
Q: How does this package relate to “Bananas, Lenses, Envelopes and Barbed Wire?”
A: It doesn’t.
Erik Meijer et al.’s “Functional Programming with Bananas, Lenses, Envelopes and Barbed Wire” is a wonderful paper about a completely unrelated topic.
In that paper they define catamorphisms (folds) which they indicate syntactically with “banana brackets”
(|..|) and they define anamorphisms (unfolds) which they indicate syntactically with “lenses”
[(..)] along with a couple of other recursion schemes.
The “banana bracket” term is still sometimes used to talk about catamorphisms, but the other colloquial terms for their notations never really caught on. The notion of “lenses” that they use is just a reference to their syntax, and has nothing to do with the notion of a lens used by this library.
Q: When should I define or use a
Getter rather than a function?
A: In general you shouldn’t bother defining values that are just a
Getter. It is almost always a better idea to just supply a function, and then drop it into the chain of lenses or traversals with
to, or by simply applying it to the final result.
Q: How can I build lenses without depending on the package itself?
A: It may be possible to define your lens using only base, or you may need to depend on one or both of profunctors and contravariant. More details are avaialble on the How can I write lenses without depending on lens? page.