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
Set A is said to be a subset of Set B if all the elements of Set A are also present in Set B. In other words, set A is contained inside Set B. Example: If set A has {X, Y} and set B has {X, Y, Z}, then A is the subset of B because elements of A are also present in set B.
The number of proper subsets is 15. In set theory, a set X is defined as a subset of the other set Y, if all the elements of set X should be present in the set Y. This can be symbolically represented by X ⊂ Y What are the two classifications of subset? Define proper and improper subsets.
Where, {}, {2}, {4}, {6}, {2,4}, {4,6}, {2,6} are the proper subsets and {2,4,6} is the improper subsets. Therefore, we can write {2,4,6} ⊆ P. Note: The empty set is an improper subset of itself (since it is equal to itself) but it is a proper subset of any other set. The power set is said to be the collection of all the subsets.
A ⊆ B; which means Set A is a subset of Set B. Note: A subset can be equal to the set. That is, a subset can contain all the elements that are present in the set. The subsets of any set consists of all possible sets including its elements and the null set.
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 a
s and b
s 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.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With