Max Bolingbroke
2010-06-27 09:54:28 UTC
I'm wondering if someone can cast some light on a pattern I've come
across, which I'm calling the "mother of all X" pattern after Dan
Piponi's blog post
(http://blog.sigfpe.com/2008/12/mother-of-all-monads.html). Edward
Kmett has also explored these ideas here:
http://www.mail-archive.com/haskell-***@haskell.org/msg57738.html
Preliminaries
===
Q: What is the "mother of all X", where X is some type class?
3. (We may also add the constraint that D is somehow the "smallest
such" data type, but I don't know in exactly what sense I mean this).
So the "mother of all X" is a data type that somehow encodes all of
the functions that the X type class gives you.
An example is in order!
Example 1: Yoneda is the mother of all Functors
===
The code in this example and the next one is shamelessly stolen from
to make use of the Functor instance for f: all of the methods of
Functor f have been somehow encoded into the Yoneda data type!
Functor subclass Y. But (Yoneda f) is not the mother of all Y, because
type is quantified over any superclass of Functor, and we want to run
it with Functor instantiated to some F, if we instead run it with
Functor instantiated to (Yoneda f) and then use lowerYoneda, we will
automatically get guaranteed fmap fusion.
Example 2: Codensity is the mother of all Monads
===
performance improvement, notably in the case of free monads. See
http://haskell.org/haskellwiki/Performance/Monads or
http://wwwtcs.inf.tu-dresden.de/~voigt/mpc08.pdf.
Example 3: Wotsit is the mother of all Categories
===
I don't actually know what the right name for this data type is, I
monoids (reassociation realised by assocativity of function
application), which is not surprising because Category is just a
monoid. There is probably some connection between NBE and Yoneda (e.g.
"Normalization and the Yoneda embedding", but I can't get access to
this paper electronically).
Conclusion
===
So I have a lot of questions about this stuff:
1. Is there a way to mechanically derive the "mother of all X" from
the signature of X? Are these all instances of a single categorical
framework?
2. Is there a mother of all idioms? By analogy with the previous three
($ x) k))). But <*> still eludes me!
3. Since (Codensity m) enforces >>= associativity, perhaps it can be
used to define correct-by-construction monads in the style of
operational or MonadPrompt?
Any insight offered would be much appreciated :-)
Cheers,
Max
across, which I'm calling the "mother of all X" pattern after Dan
Piponi's blog post
(http://blog.sigfpe.com/2008/12/mother-of-all-monads.html). Edward
Kmett has also explored these ideas here:
http://www.mail-archive.com/haskell-***@haskell.org/msg57738.html
Preliminaries
===
Q: What is the "mother of all X", where X is some type class?
lift :: X d => d a -> D a
lower :: X d => D a -> d a
instance X D
With *no superclass constraints*.lower :: X d => D a -> d a
instance X D
3. (We may also add the constraint that D is somehow the "smallest
such" data type, but I don't know in exactly what sense I mean this).
So the "mother of all X" is a data type that somehow encodes all of
the functions that the X type class gives you.
An example is in order!
Example 1: Yoneda is the mother of all Functors
===
The code in this example and the next one is shamelessly stolen from
-- flip fmap :: forall a. f a -> (forall b. (a -> b) -> f b)
newtype Yoneda f a = Yoneda { runYoneda :: forall b. (a -> b) -> f b }
And the injections. As it turns out, we don't need the Functornewtype Yoneda f a = Yoneda { runYoneda :: forall b. (a -> b) -> f b }
liftYoneda :: Functor f => f a -> Yoneda f a
liftYoneda f = Yoneda (flip fmap f)
lowerYoneda :: Yoneda f a -> f a
lowerYoneda f = runYoneda f id
Finally, we can write the Functor instance. Notice that we don't needliftYoneda f = Yoneda (flip fmap f)
lowerYoneda :: Yoneda f a -> f a
lowerYoneda f = runYoneda f id
to make use of the Functor instance for f: all of the methods of
Functor f have been somehow encoded into the Yoneda data type!
instance Functor (Yoneda f) where
fmap f m = Yoneda (\k -> runYoneda m (k . f))
Note that we can also write an instance (Y f => Y (Yoneda f)) for anyfmap f m = Yoneda (\k -> runYoneda m (k . f))
Functor subclass Y. But (Yoneda f) is not the mother of all Y, because
instance Applicative f => Applicative (Yoneda f) where
pure = liftYoneda . pure
mf <*> mx = liftYoneda (lowerYoneda mf <*> lowerYoneda mx)
Why is (Yoneda f) interesting? Because if I take some expression whosepure = liftYoneda . pure
mf <*> mx = liftYoneda (lowerYoneda mf <*> lowerYoneda mx)
type is quantified over any superclass of Functor, and we want to run
it with Functor instantiated to some F, if we instead run it with
Functor instantiated to (Yoneda f) and then use lowerYoneda, we will
automatically get guaranteed fmap fusion.
Example 2: Codensity is the mother of all Monads
===
-- (>>=) :: forall a. m a -> (forall b. (a -> m b) -> m b)
newtype Codensity m a = Codensity { runCodensity :: forall b. (a -> m b) -> m b }
liftCodensity :: Monad m => m a -> Codensity m a
liftCodensity m = Codensity ((>>=) m)
lowerCodensity :: Monad m => Codensity m a -> m a
lowerCodensity m = runCodensity m return
instance Functor (Codensity f) where
fmap f m = Codensity (\k -> runCodensity m (k . f))
instance Applicative (Codensity f) where
pure x = Codensity (\k -> k x)
mf <*> mx = Codensity (\k -> runCodensity mf (\f -> runCodensity mx (\x -> k (f x))))
instance Monad (Codensity f) where
return = pure
m >>= k = Codensity (\c -> runCodensity m (\a -> runCodensity (k a) c))
Again, using (Codensity m) where you used m before can yield anewtype Codensity m a = Codensity { runCodensity :: forall b. (a -> m b) -> m b }
liftCodensity :: Monad m => m a -> Codensity m a
liftCodensity m = Codensity ((>>=) m)
lowerCodensity :: Monad m => Codensity m a -> m a
lowerCodensity m = runCodensity m return
instance Functor (Codensity f) where
fmap f m = Codensity (\k -> runCodensity m (k . f))
instance Applicative (Codensity f) where
pure x = Codensity (\k -> k x)
mf <*> mx = Codensity (\k -> runCodensity mf (\f -> runCodensity mx (\x -> k (f x))))
instance Monad (Codensity f) where
return = pure
m >>= k = Codensity (\c -> runCodensity m (\a -> runCodensity (k a) c))
performance improvement, notably in the case of free monads. See
http://haskell.org/haskellwiki/Performance/Monads or
http://wwwtcs.inf.tu-dresden.de/~voigt/mpc08.pdf.
Example 3: Wotsit is the mother of all Categories
===
I don't actually know what the right name for this data type is, I
-- (>>>) :: forall a b. t a b -> (forall c. t b c -> t a c)
newtype Wotsit t a b = Wotsit { runWotsit :: forall c. t b c -> t a c }
liftWotsit :: Category t => t a b -> Wotsit t a b
liftWotsit t = Wotsit ((>>>) t)
lowerWotsit :: Category t => Wotsit t a b -> t a b
lowerWotsit t = runWotsit t id
instance Category (Wotsit t) where
id = Wotsit id
t1 . t2 = Wotsit (runWotsit t2 . runWotsit t1)
This is *strongly* reminiscent of normalisation-by-evaluation fornewtype Wotsit t a b = Wotsit { runWotsit :: forall c. t b c -> t a c }
liftWotsit :: Category t => t a b -> Wotsit t a b
liftWotsit t = Wotsit ((>>>) t)
lowerWotsit :: Category t => Wotsit t a b -> t a b
lowerWotsit t = runWotsit t id
instance Category (Wotsit t) where
id = Wotsit id
t1 . t2 = Wotsit (runWotsit t2 . runWotsit t1)
monoids (reassociation realised by assocativity of function
application), which is not surprising because Category is just a
monoid. There is probably some connection between NBE and Yoneda (e.g.
"Normalization and the Yoneda embedding", but I can't get access to
this paper electronically).
Conclusion
===
So I have a lot of questions about this stuff:
1. Is there a way to mechanically derive the "mother of all X" from
the signature of X? Are these all instances of a single categorical
framework?
2. Is there a mother of all idioms? By analogy with the previous three
-- (<**>) :: forall a. i a -> (forall b. i (a -> b) -> i b)
newtype Thingy i a = Thingy { runThingy :: forall b. i (a -> b) -> i b }
But I can't see how to write either pure or <*> with that data type.newtype Thingy i a = Thingy { runThingy :: forall b. i (a -> b) -> i b }
newtype Thingy i a = Thingy { runThingy :: forall b. Yoneda i (a -> b) -> i b }
Because you can write pure (pure x = Thingy (\k -> lowerYoneda (fmap($ x) k))). But <*> still eludes me!
3. Since (Codensity m) enforces >>= associativity, perhaps it can be
used to define correct-by-construction monads in the style of
operational or MonadPrompt?
Any insight offered would be much appreciated :-)
Cheers,
Max