Suggestion regarding (.) and map

Cale Gibbard cgibbard at gmail.com
Thu Apr 24 17:31:09 EDT 2008


2008/4/24 Dan Doel <dan.doel at gmail.com>:
> On Thursday 24 April 2008, Wolfgang Jeltsch wrote:
>  > I don't think that this is reasonable.  (.) corresponds to the little
>  > circle in math which is a composition.  So (.) = (<<<) would be far better.
>
>  Were I building a library, this might be the direction I'd take things.
>  They're two incompatible generalizations, and you have to decide which is
>  more important to you.
>
>  For instance, you can generalize from arrows into categories (with objects in
>  *):
>
>     class Category (~>) where
>       id  :: a ~> a
>       (.) :: (b ~> c) -> (a ~> b) -> (a ~> c)
>
>  And, of course, from this, you get the usual meanings for (->):
>
>     instance Category (->) where
>       id x = x
>       (f . g) x = f (g x)
>
>  An example of a Category that isn't an Arrow (I think) is:
>
>     newtype Op (~>) a b = Op { unOp :: b ~> a }
>
>     instance Category (~>) => Category (Op (~>)) where
>       id = Op id
>       -- (.) :: (b <~ c) -> (a <~ b) -> (a <~ c)
>       (Op f) . (Op g) = Op (g . f)
>
>     type HaskOp = Op (->)
>
>  (Why is this even potentially useful? Well, if you define functors with
>  reference to what two categories they relate, you get (pardon the illegal
>  syntax):
>
>     map :: (a ~1> b) -> (f a ~2> f b)
>
>  Which gives you current covariant endofunctors if (~1>) = (~2>) = (->), but it
>  also gives you contravariant endofunctors if (~1>) = (->) and (~2>) = Op
>  (->). Is this a useful way of structuring things in practice? I don't know.)
>
>  Now, going the (.) = map route, one should note the following Functor
>  instance:
>
>     instance (Arrow (~>)) => Functor ((~>) e) where
>       -- fmap :: (a -> b) -> (e ~> a) -> (e ~> b)
>       fmap f g = arr f <<< g
>
>  So, in this case (.) is composition of a pure function with an arrow, but it
>  does not recover full arrow composition. It certainly doesn't recover
>  composition in the general Category class above, because there's no operation
>  for lifting functions into an arbitrary Category (think Op: given a function
>  (a -> b), I can't get a (b -> a) in general).
>
>  (At a glance, if you have the generalized Functors that reference their
>  associated Categories, you have:
>
>     map (a ~1> b) -> (e ~3> a) ~2> (e ~3> b)
>
>  so for (~1>) = (~3>), and (~2>) = (->), you've recovered (.) for arbitrary
>  categories:
>
>     instance (Category (~>) => Functor ((~>) e) (~>) (->) where
>       map f g = f . g
>
>  so, perhaps with a generalized Functor, you can have (.) = map *and* have (.)
>  be a generalized composition.)
>
>  Now, the above Category stuff isn't even in any library that I know of, would
>  break tons of stuff (with the generalized Functor, which is also kind of
>  messy), and I haven't even seriously explored it, so it'd be ridiculous to
>  request going in that direction for H'. But, restricted to the current
>  libraries, if you do want to generalize (.), you have to decide whether you
>  want to generalize it as composition of arrows, or as functor application.
>  The former isn't a special case of the latter (with the current Functor, at
>  least).
>
>  Generalizing (.) to Arrow composition seems more natural to me, but
>  generalizing to map may well have more uses.
>
>  -- Dan

Right, my own preference in this regard is to generalise in the
direction that (<<<) would be a method of Category, which is a
generalisation of Arrow.

We currently at least have way more Functor instances than Category
instances, so it seems sensible to pick the shorter notation for the
more common case, but I do strongly think we should start pushing
things in this direction. These are all really nice, extremely general
ideas which can make libraries nicely uniform.

 - Cale


More information about the Haskell-prime mailing list