The Comonad.Readertypes, (co)monads, substructural logic

Generalizing (.)

If we take a look at the Haskell (.) operator:

(.) :: (a -> b) -> (e -> a) -> e -> b

and take a moment to reflect on the type of fmap

fmap :: Functor f => (a -> b) -> f a -> f b

and the unnamed Reader monad from Control.Monad.Reader

instance Functor ((->) r)

we see that fmap applied to the Reader functor rederives (.).

fmap_reader :: (a -> b) -> (e -> a) -> e -> b

So if we were willing to forgo ease of learning, and to bake in the Reader monad as a primitive, we could quite concisely redefine (.) to give it a more general signature:

module Dot where
import Control.Monad.Reader
import Prelude hiding ((.))
infixr 0 .
(.) :: Functor f => (a -> b) -> f a -> f b
(.) = fmap

In this context, existing code continues to type check. For instance,

((+2) . (*3)) 5 ==> 17

And the . above doubles as filling the role of the * map operator mentioned in Richard Bird's 1990 Calculus of Functions paper generalized to any Functor.

((+2) . (*3)) . [1..10] ==> [5,8,..32]
((+2) . (*3)) . Just 5 ==> Just 17
((+2) . (*3)) . Nothing ==> Nothing

I was able to test this with the just about every example golfed back and forth on the #haskell channel in the last 6 months.

I'm not advocating this as a practice for Haskell as it is somewhat terrifying to think of how to teach to new programmers, but I found the exercise to be enlightening.

Discussion

Edward KmettNovember 9th, 2006 at 1:22 am

As an aside, the idea came from observing the fact that lambdabot’s pointfree conversion command @pl had taken to using fmap instead of (.) in a lot of places.

Edward KmettJuly 24th, 2007 at 10:05 am

Thanks. Fixed.

I honestly, just typed that off the cuff.

Thats what I get for not actually compiling it before posting ;)

Paul KeirMarch 11th, 2011 at 7:44 am

Another great post and really neat idea. I tried

((+2) . (*3)) 5

and also

((+2) . (*3)) $ 5

but this

(+2) . (*3) $ 5

complains about a missing Num instance for the literal ‘5′. It makes me wonder why the 5 is accepted in any case, as it’s not (here at least) an instance of Functor?

Edward KmettMarch 11th, 2011 at 3:02 pm

Paul:

Back when I wrote this post I gave the updated (.) the wrong precedence, it should be

infixr 9 .

Original post and discussion · Download Markdown