A FunList
is a datatype invented by Twan van Laarhoven in this blog post. A minor variation given by Bartosz Milewski looks like this:
data FunList a b t = Done t
| More a (FunList a b (b -> t))
An interesting fact about this datatype is that if we shuffle around the type parameters a bit, we have a Profunctor
:
data FunList t b a
= Done t
| More a (FunList (b -> t) b a)
mapResult :: (a -> b) -> FunList a x y -> FunList b x y
mapResult f (Done x) = Done $ f x
mapResult f (More a r) = More a $ mapResult (f .) r
instance Profunctor (FunList t)
where
dimap _ _ (Done t) = Done t
dimap f g (More a r) = More (g a) $ dimap f g $ mapResult (. f) r
Obeying dimap id id = id
and dimap (f . g) (h . i) = dimap g h . dimap f i
The class Profunctor
has several useful subclasses that represent families of profunctors (in the category theory sense) with additional equipment. Specifically, we can talk about profunctors with a "strength":
class Profunctor p => Strong t1 t2 p
where
pstrength :: p a b -> p (t1 a x) (t2 b x)
With laws dictated by the categorical notion of a "strong monad in a bicategory" (this is explained in detail in "Arrows are Strong Monads" by Kazuyuki Asada).
Similarly, since profunctors are "just" functors, we can look for whether a profunctor is monoidal:
class Profunctor p => SemigroupalProfunctor t1 t2 t3 p
where
combine :: t3 (p a b) (p c d) -> p (t1 a c) (t2 b d)
class SemigroupalProfunctor t1 t2 t3 p => MonoidalProfunctor t1 i1 t2 i2 t3 i3 p
where
unit :: i3 -> p i1 i2
With laws given by the definition of a monoidal functor.
Some interesting choices for t1
, t2
, t3
and i1
, i2
, i3
are (,)
, Either
, These
and ()
and Void
respectively.
The question is which such classes does the reshuffled FunList
instantiate? An implementation with some argument as to why it follows the laws would be very good, a proof of correctness would be excellent.
TODO: Specialize out list of laws for each class here