Home

Awesome

microlens

Hackage version microlens on Stackage Nightly Stackage LTS version Cabal build BSD3 license

A tiny part of the lens library with no dependencies.

If you're completely new to this whole lenses thing

Read this tutorial. It's for lens, but it applies to microlens just as well (except for module names).

What is microlens?

microlens is a lens library, just like lens, but smaller. It provides essential lenses and traversals (like _1 and _Just), as well as ones which are simply nice to have (like each, at, and ix), and some combinators (like failing and singular), but everything else is stripped. As the result, microlens has no dependencies. However, there are also separate packages (microlens-ghc and microlens-platform) which provide additional instances and let you use each and friends with various container types.

If you're writing an app, you should probably use microlens-platform and not microlens. You'll get additional functions, instances, makeLenses, and other useful things. microlens is mostly for library writers and for toying with lenses. See Which microlens library should I use? for more.

Here are the build times for all libraries in the family:

PackageBuild time with dependenciesPure build time
microlens3.5s3.5s
microlens-th7.2s4.5s
microlens-ghc5.7s3.3s
microlens-mtl8.8s3.7s
microlens-platform1m47s4.9s
microlens-contra1m12s2.1s
microlens-aeson3m47s9.2s
microlens-process9.8s3.8s
lens4m10s1m12s

Other features:

The reason microlens exists is that lens is a huge library with lots of dependencies, but lenses are very useful and it's not nice to limit them to applications and bigger packages. (I'm not talking about exporting lenses, I'm talking about using lenses to write code.) microlens attempts to be a library that would be a nearly unquestionable win for some people.

Which microlens library should I use?

If you don't know where to start, start with microlens-platform and change later if necessary. If you need Prisms or Isos, use microlens-pro instead. If you're a library author or otherwise want a low dependency footprint, start with microlens and only add others when needed (likely you will want microlens-th to conveniently generate lenses for your datatypes).

Migrate from lens

Otherwise, everything should work just fine without any code changes – the microlens API mirrors the lens API. The license is the same, too.

(The list might look big, but in reality it isn't and in the majority of cases you'll be able to migrate just fine. “If it compiles and you didn't have to change any type signatures, it works.”)

If you're unsure, just open an issue in your project, mention me (@neongreen), and I'll look at your code and tell you whether it'll work or not.

All packages in the family

Unofficial:

If you're writing a library, use microlens and other packages as needed; if you're writing an application, perhaps use microlens-platform.

Versions of microlens-ghc and microlens-platform are incremented whenever versions of their dependencies are incremented, so if you're using these packages it's always enough to specify just their versions and nothing else. In other words, there's no risk of the following happening:

Competitors

So, I recommend:

What's bad about this package

I hate it when people advertise things without also describing their disadvantages, so I'll list the ones I can think of here.

Design decisions

microlens doesn't include anything lens doesn't include, even though sometimes I'm very tempted to improve something in microlens just because I have control over it.

I don't mind adding new functions from lens to the package, even when done in an inconsistent way (e.g. I added mapAccumLOf just because someone needed it, but I haven't added mapAccumROf even though that would've been more consistent). However, I am only able to add functions as long as microlens stays small, so if you plan to adopt microlens first and make dozens of requests for function additions later, this package is not for you.


Most *Of functions aren't included. If you don't know, those are sumOf, lengthOf, setOf, etc., and they are roughly equivalent to following:

sumOf    l s = sum          (s ^.. l)
lengthOf l s = length       (s ^.. l)
setOf    l s = Set.fromList (s ^.. l)

(Where ^.. takes something which extracts several targets, and returns a list of those targets. E.g. (1, 2) ^.. both is [1, 2]).

I guess the reason for including them all into lens (and there's an awful lot of them) is somewhere between

I suspect that the last reason is the most important one. The last reason is also the one I dislike most.

There are lots of functions which work on lists; lists are something like “the basic collection/stream type” in Haskell. GHC tries a lot to optimise code which produces and consumes lists; admittedly, it doesn't always succeed. lens seems to be trying to sidestep this whole list machinery.

The latter way is theoretically nicer, but not when you've got the rest of huge ecosystem using lists as the preferred way of information flow, otherwise you're bound to keep rewriting all functions and adding Of to them. lens is good for creating functions which extract data, and for creating functions which update structures (nested records, etc.), but it's probably not good enough to make the whole world want to switch to writing lens-compatible consumers of data.


Nothing indexed is included since it's impossible to get Conjoined without adding a pile of dependencies:

class ( Choice p, Corepresentable p
      , Comonad (Corep p), Traversable (Corep p)
      , Strong p, Representable p
      , Monad (Rep p), MonadFix (Rep p)
      , Distributive (Rep p)
      , ArrowLoop p, ArrowApply p, ArrowChoice p
      )
      => Conjoined p

class Conjoined p => Indexable i p

Instances of Ixed, Each, At, etc are all split off into separate packages, which is understandable, because otherwise we'd have to have vector as a dependency (the alternative is having orphan instances, which I'm not particularly afraid of). However, even instances for libraries shipped with GHC (such as array) are in their own package. There are 2 reasons for this:

What about lens-family?

lens-family is another small lenses library which is mostly compatible with lens (unless I decide to nitpick and say that its makeLensesBy and intAt aren't present in lens at all), which has few dependencies, and which provides Template Haskell in a separate package as well.

It looks like lens-family values cleanness and simplicity, which unfortunately means that it might've been hard for me (if possible at all) to convince its maintainer to make changes which would bring it closer to lens (INLINE pragmas, using unsafe #. operator, adding each, etc). I actually like cleanness and dislike excessive optimisation (especially of the kind that is used in lens) too, but making a library I would like wasn't my goal. The goal was to push people who aren't using a lens library towards using one.