As I understand it, each van Laarhoven optic type can be defined by a constraint on a type constructor:
type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t
type Traversal s t a b = forall f. Applicative f => (a -> f b) -> s -> f t
-- etc.
If we choose Monad
as the constraint, does it form some kind of "optic" in a meaningful way?
type Something s t a b = forall f. Monad f => (a -> f b) -> s -> f t
My intuition is that the Monad
constraint might be too restrictive to get any value out of a structure like this: since the Const
functor is not a Monad
, we can't do the trick of specializing f
to Const
in order to derive a view
-like function. Still, we can do some things with this Something
type; it's just not clear to me if we can do anything particularly useful with it.
The reason I'm curious is because the type of a van Laarhoven optic is suspiciously similar to the type of a function that modifies a "mutable reference" type like IORef
. For example, we can easily implement
modifyIORefM :: MonadIO m => IORef a -> (a -> m a) -> () -> m ()
which, when partially-applied to an IORef
, has the shape
type SomethingIO s t a b = forall f. MonadIO f => (a -> f b) -> s -> f t
where a = b
and s = t = ()
. I'm not sure whether this is a meaningful or meaningless coincidence.
Practically speaking, such an optic is a slightly inconvenient
Traversal
.That's because, practically speaking, we use a
Traversal
:for two things. Getting a list of
a
s from ans
, which we can do with theConst
functor:and replacing the
a
s withb
s to turn thes
into at
. One method is to use theState
functor, and ignoring issues with matching the counts ofa
s andb
s, we have:If we instead have an optic using a
Monad
constraint:we can still perform these two operations. Since
State
is a monad, thesetListOf
operation can use the same implementation:For
toListOf
, there's noMonad
instance forConst [a]
, but we can use aWriter
monad to extract thea
values, as long as we have a dummyb
value to make the type checker happy:or, since Haskell has bottom:
Self-contained code: