A key point IMHO is to distinguish between two viewpoints on optics:
- for (beginner) users, explaining how to use an optics library, independently of representation choices;
- for implementers / advanced users, explaining how the various representations work and the implications of choosing e.g. profunctor vs. van Laarhoven.
Which are you trying to explain?
As one of the package authors I’m biased, but you may be interested in the package docs for the optics
package. That’s very much focused on the first of these points; the representation choice is treated as an internal implementation detail rather than something users of the library should have to care about.
Delving into optics to do that, I found it hard to explain the profunctor representation, especially with Traversal. Having hard time finding the typeclass of Profunctor which yields Traversal…
For these kinds of question about the profunctor representation I would refer to Profunctor Optics: Modular Data Accessors by Pickering, Gibbons and Wu, which shows a clean way to define profunctor traversals. But unlike the situation with the van Laarhoven representation, for profunctors the relevant typeclasses are not standardised in base
, so there is more room for differences in implementation (e.g. optics
uses a class that corresponds rather directly to the van Laarhoven representation).