FAQ

Ingo Blechschmidt edited this page Mar 11, 2015 · 41 revisions

General

Resources

Usage Guidelines


General FAQs

Q: What are the goals of this project? Why does this project exist?

A: The 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 Foldable and 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 Text and ByteString.

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 lens?

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 Prism and 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 Context and 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 makeLenses and 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 containers.

The combinators in Control.Lens.Plated are generally useful when working with any Traversal and we use Plated internally.

Q: Wasn’t fclabels or 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 fclabels, data-lens, data-accessor, lenses, 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.


Resources

Q: Where can I learn more about lenses in general?

A: There are a number of resources online. Here are a few:

  • 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 examples/ folder.
  • 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 lens.
  • The Derivation page of this wiki covers some of the motivation about how and why van Laarhoven lenses compose so well.
  • 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 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”.

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.


Usage Guidelines

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.