
On 19/10/2007, apfelmus
Cale Gibbard wrote:
fmap (f . g) x = fmap f (fmap g x) becomes: (f . g) . x = f . (g . x) fmap id x = x becomes id . x = x
Nice! Can this be done in Category Theory, too? I mean, it would be nice to internalize morphism, functors, natural transformations, ... in one and the same category (like Hask), so there's less fuss. I.e. given a category C , construct an category C\infty that is basically the same as C but also contains the functors, natural transformations etc. of C and has this handy infix (.) operation.
Well, sort of, though they typically don't use quite that notation for it. There is work on generalising to higher-dimensional categories, where, for example, treating Cat as a 2-category, you have 0-cells which are categories, 1-cells, which are functors, and 2-cells, which are natural transformations between parallel functors. Natural transformations support two kinds of composition, called vertical (which is the usual one) and horizontal composition, so typically some separate notation, like an asterisk, is chosen for horizontal composition.
I've tried this out for a while, and it is actually rather nice to use in many cases. Functor application is common enough that having a one-character representation for it is great.
I can't remember using fmap/liftM very often, but if I use `liftM`, then often in infix notation, so and infix symbol for fmap is indeed a very good idea.
However, (.) in that role confuses me because I always think that the right argument should be function. In other words, I'm fine with
print . sum . map read . lines . readFile
( with a hypothetical instance Category (a -> IO b) ) while your proposal gives rise to
show . sum . map read . lines . readFile "foo.txt"
which makes me feel ill.
Hmm, it doesn't really bother me so much. There are a number of ways to read that. At the two extremes, you can read it as the composition (show . sum . map read . lines) being functorially applied to (readFile "foo.txt), or as a chain of functor applications with IO as the functor at the other extreme. Of course, these are equal.
In my opinion, function composition and function application should have separate notations. The new (.) blurs these lines too much for my taste (i.e. (.) :: (a -> b) -> Id a -> Id b) and I prefer <$> (or even plain $) for fmap .
In addition, I always longed for categories without an embedding (a -> b) -> c a b , they keep popping up while I program in Haskell and more often than I need infix fmap . Also, I dislike (>>>) or (<<<) and very much prefer (.) for them.
But in the end, we can have both worlds of (.) without name clash! Simply annotate functors with the category they operate on :)
class Category c => Functor c f where (.) :: c a b -> f a -> f b
instance Functor (->) [] where (.) = map
instance Category c => Functor c (c d) where (.) = `o`
Regards, apfelmus
Of course! That's quite a nice idea. Then we can have monads over an arbitrary category on the objects of Hask as well, though I'm not sure of too many examples yet, apart from the obvious one for Arrows. class Functor c m => Monad c m where return :: a -> m a join :: m (m a) -> m a (>>=) :: m a -> (a -> m b) -> m b x >>= f = join (f . x) join x = x >>= id - Cale