haskelltypeclasskleisli

Application of functions and Kleisli arrows


(.) and (<=<) are quite similar:

(.)   ::            (b ->   c) -> (a ->   b) -> (a ->   c)
(<=<) :: Monad m => (b -> m c) -> (a -> m b) -> (a -> m c)

and are available as a method in the Category type class ((->) and Kleisli instances):

(<<<) :: (Category f) => f b c -> f a b -> f a c

($) and (=<<) are also quite similar:

($)   ::            (a ->   b) ->   a ->   b
(=<<) :: Monad m => (a -> m b) -> m a -> m b

Is there a type class that abstracts over these application functions?


Solution

  • Both your examples are arrow mappings of functors (not Functors, but functors in the broader categorical sense), just like fmap is the arrow mapping of a Functor. (=<<), for instance, is the arrow mapping of a functor from Kleisli m to (->) for some monad m. An appropriate generalisation, then, is one that accounts for functors between different categories. Control.Categorical.Functor provides that:

    class (Category r, Category t) => Functor f r t | f r -> t, f t -> r where
      fmap :: r a b -> t (f a) (f b)
    

    Armed with that, you would be able to write an instance in the spirit of:

    -- `(.)` is plain old Prelude `(.)`, and not the generalised `Category` one.
    instance Monad m => Functor m (Kleisli m) (->) where
        fmap = (=<<) . runKleisli
    

    Or, for something you can actually run:

    {-# LANGUAGE MultiParamTypeClasses #-}
    {-# LANGUAGE FlexibleInstances #-}
    {-# LANGUAGE GeneralizedNewtypeDeriving #-}
    
    import Control.Arrow (Kleisli(..))
    import qualified Control.Categorical.Functor as F
    
    newtype BindF m a = BindF { runBindF :: m a }
        deriving (Functor, Applicative, Monad, Show)
    
    instance Monad m => F.Functor (BindF m) (Kleisli (BindF m)) (->) where
        fmap = (=<<) . runKleisli
    
    GHCi> F.fmap (Kleisli (BindF . replicate 2)) (BindF [1,2,3])
    BindF {runBindF = [1,1,2,2,3,3]}
    

    A similar instance might be written, for instance, for (<*>), in terms of the Static category. As for ($), it is the arrow mapping of the identity functor in (->), and so it is merely fmap for Identity sans the Identity wrapper (cf. Daniel Wagner's comment to the question).