Search code examples
haskellcategory-theory

Where is the bifunctor for functions in haskell?


I can't find bifunctor analog of fmap.

Explanation:

Functor for objects - datatype constructor. Type - a -> f a

Functor for functions - fmap. Type - (a -> b) -> (fa -> fb)

Bifunctor for objects - result of bimap f g, where f :: (a -> a'), g :: (b -> b'). Type - p a b -> p a' b'

Bifunctor for functions - ?. Type - p (a -> b) (c -> d) -> p (a' -> b') (c' -> d')

Here is why I think bifunctor have such type (am I right?) with some example

enter image description here


UPDATE

enter image description here


UPDATE2

p (a -> b) (c -> d) -> p (a' -> b') (c' -> d') in the image above is morphism from bifunctor to bifunctor and also profunctor (because all functions are profunctors)

Summary:

I've thought p (a -> b) (c -> d) -> p (a' -> b') (c' -> d') is bifunctor for functions, but it's not. Bifunctor for morphisms is bimap. Type: (a -> b) -> (α -> β) -> p a α -> p b β.

I've thought p (a -> b) (c -> d) -> p (a' -> b') (c' -> d') is something unusual, but it's not, it's just function


Solution

  • Functor for objects - datatype constructor. Type - a -> f a

    Functor for functions - fmap. Type - (a -> b) -> (fa -> fb)

    Although this makes broadly sense, it is important to realise that the arrows above have three different meanings.

    Functor for objects - datatype constructor. Type - a ⟼ f a

    Functor for functions - fmap. Type - (a ⟿ b) ⟶ (f a ⟿ f b)

    where

    • is a type-level “maps-to symbol” that associates the type a with a type f a. This does not have anything to do with value-level functions whose domain is a and codomain f a. (Those are found in applicatives/monads, but that's a different story.)
    • is a type constructor for some morphism. In the Hask category, those morphisms happen to be Haskell functions, but that's just a special case.
    • is an actual function-type constructor.

    You may for now forget about the distinction between the latter two, but and are really quite different conceptually. Basically, is like the arrow you write in a lambda

    Maybe :: Type -> Type
    Maybe = \a ⟼ Maybe a
    

    whereas is just a way to express that you're abstracting over function-things.

    Another related thing that might not be clear is that the objects you're talking about are Haskell types. Not values (as OO objects are).

    So, I would phrase the listing you gave above thus:

    • Functor

      • for objects: datatype constructor. Kind Type -> Type, mapping-association a ⟼ f a.
      • for morphisms: fmap. Type: (a -> b) -> (f a -> f b).
    • Bifunctor

      • for objects: datatype constructor. Kind Type×Type -> Type, or curried Type -> Type -> Type, mapping-association a ⟼ b ⟼ p a b.
      • for morphisms: bimap. Type: (a -> b) -> (α -> β) -> p a α -> p b β.

    Actually, Haskell does not have or what you wrote with a -> f a. This would be a type-level lambda, but type-level functions can actually only be expressed as type families, i.e. the closest you could get to expressing a ⟼ f a is type instance Functored a = f a.