A \"lens\" and a \"partial lens\" seem rather similar in name and in concept. How do they differ? In what circumstances do I need to use one or the other?
Tagging Scala
A lens is a "functional reference" that allows you to extract and/or update a generalized "field" in a larger value. For an ordinary, non-partial lens that field is always required to be there, for any value of the containing type. This presents a problem if you want to look at something like a "field" which might not always be there. For example, in the case of "the nth element of a list" (as listed in the Scalaz documentation @ChrisMartin pasted), the list might be too short.
Thus, a "partial lens" generalizes a lens to the case where a field may or may not always be present in a larger value.
There are at least three things in the Haskell lens library that you could think of as "partial lenses", none of which corresponds exactly to the Scala version:
Maybe
type.They all have their uses, but the first two are too restricted to include all cases, while Traversal
s are "too general". Of the three, only Traversal
s support the "nth element of list" example.
For the "Lens
giving a Maybe
-wrapped value" version, what breaks is the lens laws: to have a proper lens, you should be able to set it to Nothing
to remove the optional field, then set it back to what it was, and then get back the same value. This works fine for a Map
say (and Control.Lens.At.at gives such a lens for Map
-like containers), but not for a list, where deleting e.g. the 0
th element cannot avoid disturbing the later ones.
A Prism
is in a sense a generalization of a constructor (approximately case class in Scala) rather than a field. As such the "field" it gives when present should contain all the information to regenerate the whole structure (which you can do with the review
function.)
A Traversal
can do "nth element of a list" just fine, in fact there are at least two different functions ix and element that both work for this (but generalize slightly differently to other containers).
Thanks to the typeclass magic of lens
, any Prism
or Lens
automatically works as a Traversal
, while a Lens
giving a Maybe
-wrapped optional field can be turned into a Traversal
of a plain optional field by composing with traverse
.
However, a Traversal
is in some sense too general, because it is not restricted to a single field: A Traversal
can have any number of "target" fields. E.g.
elements odd
is a Traversal
that will happily go through all the odd-indexed elements of a list, updating and/or extracting information from them all.
In theory, you could define a fourth variant (the "affine traversals" @J.Abrahamson mentions) that I think might correspond more closely to Scala's version, but due to a technical reason outside the lens
library itself they would not fit well with the rest of the library - you would have to explicitly convert such a "partial lens" to use some of the Traversal
operations with it.
Also, it would not buy you much over ordinary Traversal
s, since there's e.g. a simple operator (^?) to extract just the first element traversed.
(As far as I can see, the technical reason is that the Pointed typeclass which would be needed to define an "affine traversal" is not a superclass of Applicative
, which ordinary Traversal
s use.)