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
UPDATE
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
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.
You don't need a Bifunctor instance for (->), just (,):
b1 :: a -> Id a
b2 :: a -> Id2 a
-- instance Bifunctor (,) where
-- bimap f g (x, y) = (f x, g y)
f :: (Int, Float) -> (Id Int, Id2 Float)
f = bimap b1 b2
Related
Bifunctors have a map function with this signature:
bimap :: (a -> b) -> (c -> d) -> p a c -> p b d
You could also have a map like this:
othermap :: ((a, c) -> (b, d)) -> p a c -> p b d
Types with this function are a strict subset of bifunctors (you can always define bimap using othermap but not vice versa). Is there a name for the second signature?
Follow-up: what about this intermediate function?
halfothermap :: ((a, c) -> b) -> (c -> d) -> p a c -> p b d
A type which is a Bifunctor need not have the same number of a values as b values. Consider, for example,
data TwoLists a b = TwoLists [a] [b]
It is easy to implement bimap, but othermap is a real problem, especially if one of the lists is empty.
othermap f (TwoLists [] (b:bs)) = TwoLists [] _
What can you do here? You need to call f to convert all the bs to a list of type [d], but you can only call that function if you have an a in hand.
Perhaps even worse is a type which doesn't really ever have b values at all:
data TaggedFunction k a b = TaggedFunction a (k -> b)
instance Bifunctor (TaggedFunction k) where
bimap f g (TaggedFunction a b) = TaggedFunction (f a) (g . b)
How can you implement othermap for this type? You could update the function, because you have an a in hand and will have a b by the time you need a d. But there's no way for you to replace the a with a c, because you can't get hold of a b to call othermap's function with.
So you can't put this function in Bifunctor. Perhaps you're asking, why not put this in a new class? I think leftroundabout is right that the class is too constraining to be useful. othermap can be defined only when you have the same number of as and bs in your structure, i.e. when your structure is some functor f wrapped around a tuple of type (a, b). For example, instead of TwoLists we could have defined
newtype PairList a b = PairList [(a, b)]
and that can have an othermap definition. But it would just be
othermap f (PairList vs) = PairList (fmap f vs)
Likewise instead of TaggedFunction we could have defined
newtype MultiFunction k a b = MultiFunction (k -> (a, b))
but the othermap definition is again just a wrapped call to fmap:
othermap f (MultiFunction g) = MultiFunction (fmap f g)
So perhaps the best way to imagine defining this abstraction would be as, not a typeclass function, but an ordinary function that operates over a type that captures this composition:
newtype TupleFunctor f a b = TupleFunctor (f (a, b))
othermap :: Functor f => ((a, b) -> (c, d))
-> TupleFunctor f a b -> TupleFunctor f c d
othermap f (TupleFunctor x) = TupleFunctor (fmap f x)
(Expanding on a comment by #glennsl...)
The type p a c "contains" both a and c.
But a function of type (a, c) -> (b, d) requires values of type a and c to be called, and a value of type p a c doesn't necessarily have both.
othermap #Either :: ((a, c) -> (b, d)) -> Either a c -> Either b d
othermap f (Left x) = ?
othermap f (Right y) = ?
There's no way for othermap to produce values of type b or d, because it can't call f in either case.
The type of the first argument implies a bifunctor that is isomorphic to (,), which is not true of all possible bifuntors. othermap is, in fact, strictly more specific than bimap. (There seems to be a contravariant relationship here that I won't try to make precise.)
Yoneda has a valid type as long as the higher-rank extension is set:
newtype Yoneda f a = Yoneda (forall b. (a -> b) -> f b) which yields the type (forall b. (a -> b) -> f b) -> Yoneda f a.
b is picked by the consumer of Yoneda and hence doesn't appear on the LHS of the newtype declaration.
Coyoneda, however, doesn't make sense to me:
data Coyoneda f a = forall b. Coyoneda (b -> a) (f b) which yields (b -> a) -> f b -> Coyoneda f a.
b is explicitly quantified but the quantifier doesn't seem to be in the right position to render the type variable rank 2. Nonetheless b isn't listed on the LHS of the data declaration. So what is it? Almost existential? This is only an uneducated guess, because I don't quite understand existential quantifiers and assume Haskell doesn't support them.
data Coyoneda f a = forall b. Coyoneda (b -> a) (f b)
Can also be written with GADT syntax:
data Coyoneda f a where
Coyoneda :: forall b. (b -> a) -> f b -> Coyoneda f a
The explicit quantification is optional.
Coyoneda :: (b -> a) -> f b -> Coyoneda f a
Coyoneda :: forall f a b. (b -> a) -> f b -> Coyoneda f a
So you got it right: b is existentially quantified here, since it doesn’t appear in the result type of the Coyoneda data constructor.
I am learning from the "Free Applicative Functors". Surely, the question I am going to ask is kind of aside with respect to main idea of the paper, but still...
...on the page 6 there is an attempt to generalize Functor to MultiFunctor:
class Functor f ⇒ MultiFunctor f where
fmap0 :: a → f a
fmap1 :: (a → b) → f a → f b
fmap1 = fmap
fmap2 :: (a → b → c) → f a → f b → f c
...
I can not see how this definition is justified from the category theory's viewpoint: fmap2 seems to be just a bifunctor, i.e. a functor defined on a product category. By definition, product category is given by all possible ordered pairs of objects and morphisms are pairs as well, hence: fmap2 :: (a -> a', b -> b') -> (f a, f b) -> (f a', f b') looks and feels like more appropriate signature.
I can understand the way of thinking standing behing the (a -> b -> c) -> f a -> f b -> f c choice: it is just the most obvious way to take known (a -> b) -> f a -> f b signature and force it to work with binary functions, rather then unary. But isMultiFunctor (given by the definition above) actually a bi-/multifunctor in the sense that category theory expects it to be?
P.S. The reason why I am curious is that it seems like one can't get to the Applicative by generalizing Functor, though paper states that one can.
I think the category theory angle you are taking is wrong. There is a Bifunctor class (with a map of type (a -> b) -> (c -> d) -> f a c -> f b d) but that is not what this generalisation is. If one uncurries some functions then the signature of fmap2 looks like:
fmap2 :: ((a,b) -> c) -> (f a, f b) -> f c
And by considering fmap2 id, we see that what we are implementing is not a bifunctor but a cartesian functor (i.e. a monoidal functor between cartesian categories), with fmap2 id :: (f a, f b) -> f (a,b) being the natural transformation:
One can then get an applicative from this Multifunctor generalisation. Just change pure for fmap0 and (<*>) for fmap2 ($).
Let's start with the obvious: fmap0 is pure.
Here's one you made a mistake on: fmap2 is liftA2.
(bimap is very different - (a -> b) -> (c -> d) -> f a b -> f c d)
And if you go back to the definition of Applicative, you see that it has a default implementation of (<*>), which is liftA2 id, which allows you to define it in terms of pure and either liftA2 or (<*>).
So yes, that class is equivalent to Applicative.
This is yet another Haskell-through-category-theory question.
Let's take something simple and well-known as an example. fmap?
So fmap :: (a -> b) -> f a -> f b, omitting the fact that f is actually a Functor. As far as I understand, (a -> b) -> f a -> f b is nothing but a syntax sugar for the (a -> b) -> (f a -> f b); hence conclusion:
(1) fmap is a function producing a function.
Now, Hask contains functions as well, so (a -> b) and, in particular, (f a -> f b) is an object of the Hask (because objects of the Hask are well-defined Haskell types - a-ka mathematical sets - and there indeed exists set of type (a -> b) for each possible a, right?). So, once again:
(2) (a -> b) is an object of the Hask.
Now weird thing happens: fmap, obviously, is a morphism of the Hask, so it is a function, that takes another function and transform it to a yet another function; final function hasn't been applied yet.
Hence, one needs one more Hask's morphism to get from the (f a -> f b) to the f b. For each item i of type a there exists a morphism apply_i :: (f a -> f b) -> f b defined as \f -> f (lift i), where lift i is a way to build an f a with particular i inside.
The other way to see it is GHC-style: (a -> b) -> f a -> f b. On the contrast with what I've written above, (a -> b) -> f a is mapping to the regular object of the Hask. But such a view contradicts fundamental Haskell's axiom - no multivariate functions, but applied (curried) alternatives.
I'd like to ask at this point: is (a -> b) -> f a -> f b suppose to be an (a -> b) -> (f a -> f b) -> f b, sugared for simplicity, or am I missing something really, really important there?
is (a -> b) -> f a -> f b suppose to be an (a -> b) -> (f a -> f b) -> f b, sugared for simplicity
No. I think what you're missing, and it's not really your fault, is that it's only a very special case that the middle arrow in (a -> b) -> (f a -> f b) can be called morphism in the same way as the outer (a -> b) -> (f a -> f b) can. The general case of a Functor class would be (in pseudo-syntax)
class (Category (──>), Category (~>)) => Functor f (──>) (~>) where
fmap :: (a ──> b) -> f a ~> f b
So, it maps morphisms in the category whose arrows are denoted ──> to morphisms in the category ~>, but this morphism-mapping itself is just plainly a function. Your right, in Hask specifically function-arrows are the same sort of arrows as the morphism arrows, but this is mathematically speaking a rather degenerate scenario.
fmap is actually an entire family of morphisms. A morphism in Hask is always from a concrete type to another concrete type. You can think of a function as a morphism if the function has a concrete argument type and a concrete return type. A function of type Int -> Int represents a morphism (an endomorphism, really) from Int to Int in Hask. fmap, however has type Functor f => (a -> b) -> f a -> f b. Not a concrete type in sight! We just have type variables and a quasi-operator => to deal with.
Consider the following set of concrete function types.
Int -> Int
Char -> Int
Int -> Char
Char -> Char
Further, consider the following type constructors
[]
Maybe
[] applied to Int returns a type we could call List-of-Ints, but we usually just call [Int]. (One of the most confusing things about functors when I started out was that we just don't have separate names to refer to the types that various type constructors produce; the output is just named by the expression that evaluates to it.) Maybe Int returns the type we just call, well, Maybe Int.
Now, we can define a bunch of functions like the following
fmap_int_int_list :: (Int -> Int) -> [Int] -> [Int]
fmap_int_char_list :: (Int -> Char) -> [Int] -> [Char]
fmap_char_int_list :: (Char -> Int) -> [Char] -> [Int]
fmap_char_char_list :: (Char -> Char) -> [Char] -> [Char]
fmap_int_int_maybe :: (Int -> Int) -> Maybe Int -> Maybe Int
fmap_int_char_maybe :: (Int -> Char) -> Maybe Int -> Maybe Char
fmap_char_int_maybe:: (Char -> Int) -> Maybe Char -> Maybe Int
fmap_char_char_maybe :: (Char -> Char) -> Maybe Char -> Maybe Char
Each of these is a distinct morphism in Hask, but when we define them in Haskell, there's a lot of repetition.
fmap_int_int_list f xs = map f xs
fmap_int_char_list f xs = map f xs
fmap_char_int_list f xs = map f xs
fmap_char_char_list f xs = map f xs
fmap_int_int_maybe f x = case x of Nothing -> Nothing; Just y -> Just (f y)
fmap_int_char_maybe f x = case x of Nothing -> Nothing; Just y -> Just (f y)
fmap_char_int_maybe f x = case x of Nothing -> Nothing; Just y -> Just (f y)
fmap_char_char_maybe f x = case x of Nothing -> Nothing; Just y -> Just (f y)
The definitions don't differ when the type of f differs, only when the type of x/xs differs. That means we can define the following polymorphic functions
fmap_a_b_list f xs = map f xs
fmap_a_b_maybe f x = case x of Nothing -> Nothing; Just y -> Just (f y)
each of which represents a set of morphisms in Hask.
fmap itself is an umbrella term we use to refer to constructor-specific morphisms referred to by all the polymorphic functions.
With that out of the way, we can better understand fmap :: Functor f => (a -> b) -> f a -> f b.
Given fmap f, we first look at the type of f. We might find out, for example, that f :: Int -> Int, which means fmap f has to return one of fmap_int_int_list or fmap_int_int_maybe, but we're not sure which yet. So instead, it returns a constrained function of type Functor f => (Int -> Int) -> f Int -> f Int. Once that function is applied to a value of type [Int] or Maybe Int, we'll finally have enough information to know which morphism is actually meant.
Now weird thing happens: fmap, obviously, is a morphism of the Hask, so it is a function, that takes another function and transform it to a yet another function; final function hasn't been applied yet.
Hence, one needs one more Hask's morphism to get from the (f a -> f b) to the f b. For each item i of type a there exists a morphism apply_i :: (f a -> f b) -> f b defined as \f -> f (lift i), where lift i is a way to build an f a with particular i inside.
The notion of application in category theory is modelled in the form of CCC's - Cartesian Closed Categories. A category 𝓒 is a CCC if you have a natural bijection 𝓒(X×Y,Z) ≅ 𝓒(X,Y⇒Z).
In particular this implies that there exists a natural transformation 𝜺 (the evaluation), where 𝜺[Y,Z]:(Y⇒Z)×Y→Z, such that for every g:X×Y→Z there exists a 𝝀g:X→(Y⇒Z) such that, g = 𝝀g×id;𝜺[Y,Z]. So when you say,
Hence, one needs one more Hask's morphism to get from the (f a -> f b) to the f b.
The way you go from (f a -> f b) to the f b, or using the notation above, from (f a ⇒ f b) is via 𝜺[f a,f b]:(f a ⇒ f b) × f a → f b.
The other important point to keep in mind is that in Category Theory "elements" are not primitive concepts. Rather an element is an arrow of the form 𝟏→X,where 𝟏 is the terminal object. If you take X=𝟏 you have that 𝓒(Y,Z) ≅ 𝓒(𝟏×Y,Z) ≅ 𝓒(𝟏,Y⇒Z). That is, the morphisms g:Y→Z are in bijection to elements 𝝀g:𝟏→(Y⇒Z).
In Haskell this means functions are precisely the "elements" of arrow types. So in Haskell an application h y would be modelled via the evaluation of 𝝀h:𝟏→(Y⇒Z) on y:𝟏→Y. That is, the evaluation of (𝝀h)×y:𝟏→(Y⇒Z)×Y, which is given by the composition (𝝀h)×y;𝜺[Y,Z]:𝟏→Z.
For the sake of completeness, this answer focuses on a point that was addressed in various comments, but not by the the other answers.
The other way to see it is GHC-style: (a -> b) -> f a -> f b. On the contrast with what I've written above, (a -> b) -> f a is mapping to the regular object of the Hask.
-> in type signatures is right-associative. That being so, (a -> b) -> f a -> f b is really the same as (a -> b) -> (f a -> f b), and seeing (a -> b) -> f a in it would be a syntactic mix-up. It is no different from how...
(++) :: [a] -> [a] -> [a]
... doesn't mean that partially applying (++) will give us an [a] list (rather, it gives us a function that prepends some list).
From this point of view, the category theory questions you raise (for instance, on "need[ing] one more Hask's morphism to get from the (f a -> f b) to the f b") are a separate matter, addressed well by Jorge Adriano's answer.
What is the general term for a functor with a structure resembling QuickCheck's promote function, i.e., a function of the form:
promote :: (a -> f b) -> f (a -> b)
(this is the inverse of flip $ fmap (flip ($)) :: f (a -> b) -> (a -> f b)). Are there even any functors with such an operation, other than (->) r and Id? (I'm sure there must be). Googling 'quickcheck promote' only turned up the QuickCheck documentation, which doesn't give promote in any more general context AFAICS; searching SO for 'quickcheck promote' produces no results.
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
(=<<) :: Monad m => (a -> m b) -> m a -> m b
Given that Monad is more powerful an interface than Applicative, this tell us that a -> f b can do more things than f (a -> b). This tells us that a function of type (a -> f b) -> f (a -> b) can't be injective. The domain is bigger than the codomain, in a handwavey manner. This means there's no way you can possibly preserve behavior of the function. It just doesn't work out across generic functors.
You can, of course, characterize functors in which that operation is injective. Identity and (->) a are certainly examples. I'm willing to bet there are more examples, but nothing jumps out at me immediately.
So far I found these ways of constructing an f with the promote morphism:
f = Identity
if f and g both have promote then the pair functor h t = (f t, g t) also does
if f and g both have promote then the composition h t = f (g t) also does
if f has the promote property and g is any contrafunctor then the functor h t = g t -> f t has the promote property
The last property can be generalized to profunctors g, but then f will be merely a profunctor, so it's probably not very useful, unless you only require profunctors.
Now, using these four constructions, we can find many examples of functors f for which promote exists:
f t = (t,t)
f t = (t, b -> t)
f t = (t -> a) -> t
f t = ((t,t) -> b) -> (t,t,t)
f t = ((t, t, c -> t, (t -> b) -> t) -> a) -> t
Also note that the promote property implies that f is pointed.
point :: t -> f t
point x = fmap (const x) (promote id)
Essentially the same question: Is this property of a functor stronger than a monad?
Data.Distributive has
class Functor g => Distributive g where
distribute :: Functor f => f (g a) -> g (f a)
-- other non-critical methods
Renaming your variables, you get
promote :: (c -> g a) -> g (c -> a)
Using slightly invalid syntax for clarity,
promote :: ((c ->) (g a)) -> g ((c ->) a)
(c ->) is a Functor, so the type of promote is a special case of the type of distribute. Thus every Distributive functor supports your promote. I don't know if any support promote but not Distributive.