A few months ago, the folks at Well-Typed announced the
which aims to improve on the user experience compared to the
Oleg Grenrus has written an excellent migration guide
optics, so please have a look there for some more background.
generic-optics is essentially a port of
generic-lens that is
optics, and is designed to be a drop-in replacement
generic-lens. This means that if you’re already using
lens and decide to migrate to
optics, you should be able to replace the
generic-lens dependency with
generic-optics and expect things to just work.
To explain why I’m so excited about
optics, I’m going to
compare a real-life workflow between
First, language pragmas and imports:
Note that the module
Data.Generics.Product is shared between
generic-lens with the
lens library, we would import
optics, the import becomes
Now we define a simple record:
With either library, we can view the
a field using
If we ask what the type of
field @"a" is in GHCi, we already see
the advantage of
optics’s opaque representation.
Now let us use the
typed lens, which performs a type-directed lookup in
a product type, as long as there is a unique field with that type:
When the type of the field is not unique (such as if we tried to retrieve a field
generic-lens provides a helpful type error:
For situation likes this, both libraries provide a traversal called
types that focuses on all values of the given type.
Let’s see what happens if we replace
types in the above
example when using
This error is rather puzzling. Unless we know what’s going on under
the hood, it’s not obvious where the
Monoid constraint is coming from.
Compare this with
types @Int is a traversal, but
^. takes a getter!
Arguably this is a more helpful message. Consulting the documentation
optics, we find the combinator we’re looking for:
^.., which returns
all the values focused on by a traversal:
This now of course works in both libraries.
To summarise, using the two libraries should be nearly identical as
long as everything goes well and we’re not hitting type errors.
generic-optics (but really,
optics itself) shines is when things
do not go all that well, in which case the resulting error messages are a lot more
The above was just to give a little taste of using
generic-optics. The interface of
intended to be largely identical to that of
At the time of writing, the main difference is the support for overloaded
generic-lens, which allows writing
I intend to add support for this for
generic-optics too, but it
isn’t implemented yet.
Changes in generic-lens
To support this new interface,
generic-lens itself has undergone a major
reorganisation. I thought this was a good opportunity to clean some things
up and change the interface at places, which ultimately resulted in a new major
Most notably, GHC versions below 8.4 are no longer
generic-optics too) promises good
performance by making sure that the generic overhead is eliminated at
compile time. Doing so requires really careful coding practices, and
GHC’s optimiser changes between every version, which meant that
certain tricks that worked for 8.2 didn’t work for 8.6 and vice
versa. The result was horrible CPP macros to enable certain hacks on
certain versions of GHC. In the end, I decided it wasn’t worth the
effort to maintain these hacks for older versions of the compiler.
I intend to write a blog post in the near future describing some of these hacks, as they are quite interesting and potentially educational.
For a more comprehensive list of changes, refer to the changelog.
Thanks for reading this blog post, and I’m hope you’re as excited
generic-optics as I am! Since this release required a major refactoring
and moving things around, it is possible that some documentation is out of date, or
certain functions are not exported from where you would expect. If you find anything
that looks off, please either open a pull request or let me know on the issue tracker!
Finally, if you find
consider buying me a coffee!