Is there a name for this subset of bifunctors? - haskell

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.)

Related

Haskel - instance of data with two arguements

How to declare Functor instance of this data type:
data Productish a b = Productish a b
I've tried this:
instance (Functor a, Functor b) => Productish a b where
fmap f (Productish a b) = Productish (f a) (f b)
but compiler had showed error:
error: ‘fmap’ is not a (visible) method of class ‘Productish’
Firstly, your syntax is wrong. To define a Functor instance on Productish, you will need to do instance Functor (Productish a b) where ….
But there is also a more serious problem: The Functor typeclass can only be used to define a functor on one variable. So in order to define a Functor instance, you need to partially apply your data type. For instance, here’s the Maybe instance:
data Maybe a = Just a | Nothing
instance Functor Maybe where -- note that this isn’t ‘instance Functor (Maybe a)’!
fmap f (Just a) = Just (f a)
fmap f Nothing = Nothing
Similarly, to define a Functor instance for your Productish, you need to do:
instance Functor (Productish a) where
fmap f (Productish a b) = Productish a (f b)
(Note that you don’t need a Functor constraint on a, since you don’t need to map over a.)
So with Functor alone, you can only map over the second argument.
Luckily, there is also a typeclass which lets you map over both arguments. It’s called Bifunctor, and lives in the Data.Bifunctor module:
class Bifunctor p where
bimap :: (a -> b) -> (c -> d) -> p a c -> p b d
first :: (a -> b) -> p a c -> p b c
second :: (b -> c) -> p a b -> p a c
So to make a Bifunctor instance for your Productish type, use:
instance Bifunctor Productish where
bimap f g (Productish a b) = Productish (f a) (g b)
first f p = bimap f id p
second g p = bimap id g p

Bifunctor vs. Arrow methods

There is a bit of overlap between Bifunctor and Arrow methods:
class Bifunctor p where
first :: (a -> a') -> p a b -> p a' b
second :: (b -> b') -> p a b -> p a b'
bimap :: (a -> a') -> (b -> b') -> p a b -> p a' b'
class Arrow (~~>) where
...
first :: (a ~~> a') -> (a, b) ~~> (a', b)
second :: (b ~~> b') -> (a, b) ~~> (a, b')
(***) :: (a ~~> a') -> (b ~~> b') -> (a, b) ~~> (a', b')
The Bifunctor class comes with laws completely analogous to those of Functor.
The Arrow class comes with a number of laws different laws and a somewhat cryptic warning about (***): "Note that this is in general not a functor." Surprisingly (to me) there's only one law about (***):
first f >>> arr (id *** g) = arr (id *** g) >>> first f
The Arrow (->) instance and the Bifunctor (,) instance match up exactly, so that bimap #(,) = (***) #(->). Is there some special significance to this? Is there a meaningful hypothetical
class Foo (~~>) p where
biFoo :: (a ~~> a') -> (b ~~> b') -> p a b ~~> p a' b'
If so, does that admit functional dependencies?
Arrow is a (somewhat bastardized) precursor to a class of cartesian closed categories, or a least cartesian monoidal categories. Specifically, to monoidal categories whose tensor product is (,) and unit element ().
Recall that a monoidal category is characterised by the tensor product as a bifunctor, so there's your connection between Arrow and Bifunctor.
*** has in fact more laws than you listed, only, the library chooses to formulate those in terms of first instead. Here's an equivalent definition of the class:
class (Category k, Category k') => EnhancedCategory k k' where
arr :: k a b -> k' a b
-- arr id ≡ id
-- arr (f . g) = arr f . arr g
class (EnhancedCategory (->) a) => Arrow a where
(***) :: a b c -> a b' c' -> a (b,b') (c,c')
-- (f***id) . (g***id) ≡ (f.g)***id
-- (id***f) . (id***g) ≡ id***(f.g)
-- arr fst . (f***id) ≡ f . arr fst
-- arr snd . (id***g) ≡ g . arr snd
-- ¿ arr swap . (f***g) ≡ (g***f) . arr swap ?
-- ((f***g)***h) . assoc ≡ assoc . (f***(g***h))
diag :: a b (b,b)
first :: Arrow a => a b c -> a (b,d) (c,d)
first f = f***id
second :: Arrow a => a b c -> a (d,b) (d,c)
second g = id***g
(&&&) :: Arrow a => a b c -> a b d -> a b (c,d)
f&&&g = (f***g) . diag
Incidentally, it is also possible to remove the arr for lifting pure functions, and instead give the superclass only the dedicated methods fst, snd and assoc. I call that class Cartesian. This allows defining “arrow” categories that don't contain arbitrary Haskell functions; linear maps are an important example.
Arrow is equivalent to Strong + Category.
You can choose a different notion of strength to get a different kind of Arrow.
class Category a => ArrowChoice a where
arr :: (b -> c) -> a b c
(+++) :: a b c -> a b' c' -> a (Either b b') (Either c c')
In other words, the tensor product of your Cartesian closed category needn't be (,) exactly. Any tensor product you can come up with has a corresponding notion of strength, each of which would give you a corresponding variety of Arrow.
Notably, many profunctors are both Strong and Choice, so your Foo (which basically generalises Strong over a tensor product p) doesn't have a functional dependency.
The Control.Arrow module in base unfortunately muddles the hierarchy together a little bit (for example, their ArrowChoice has Arrow as a superclass).

Does each type have a unique catamorphism?

Recently I've finally started to feel like I understand catamorphisms. I wrote some about them in a recent answer, but briefly I would say a catamorphism for a type abstracts over the process of recursively traversing a value of that type, with the pattern matches on that type reified into one function for each constructor the type has. While I would welcome any corrections on this point or on the longer version in the answer of mine linked above, I think I have this more or less down and that is not the subject of this question, just some background.
Once I realized that the functions you pass to a catamorphism correspond exactly to the type's constructors, and the arguments of those functions likewise correspond to the types of those constructors' fields, it all suddenly feels quite mechanical and I don't see where there is any wiggle room for alternate implementations.
For example, I just made up this silly type, with no real concept of what its structure "means", and derived a catamorphism for it. I don't see any other way I could define a general-purpose fold over this type:
data X a b f = A Int b
| B
| C (f a) (X a b f)
| D a
xCata :: (Int -> b -> r)
-> r
-> (f a -> r -> r)
-> (a -> r)
-> X a b f
-> r
xCata a b c d v = case v of
A i x -> a i x
B -> b
C f x -> c f (xCata a b c d x)
D x -> d x
My question is, does every type have a unique catamorphism (up to argument reordering)? Or are there counterexamples: types for which no catamorphism can be defined, or types for which two distinct but equally reasonable catamorphisms exist? If there are no counterexamples (i.e., the catamorphism for a type is unique and trivially derivable), is it possible to get GHC to derive some sort of typeclass for me that does this drudgework automatically?
The catamorphism associated to a recursive type can be derived mechanically.
Suppose you have a recursively defined type, having multiple constructors, each one with its own arity. I'll borrow OP's example.
data X a b f = A Int b
| B
| C (f a) (X a b f)
| D a
Then, we can rewrite the same type by forcing each arity to be one, uncurrying everything. Arity zero (B) becomes one if we add a unit type ().
data X a b f = A (Int, b)
| B ()
| C (f a, X a b f)
| D a
Then, we can reduce the number of constructors to one, exploiting Either instead of multiple constructors. Below, we just write infix + instead of Either for brevity.
data X a b f = X ((Int, b) + () + (f a, X a b f) + a)
At the term-level, we know we can rewrite any recursive definition
as the form x = f x where f w = ..., writing an explicit fixed point equation x = f x. At the type-level, we can use the same method
to refector recursive types.
data X a b f = X (F (X a b f)) -- fixed point equation
data F a b f w = F ((Int, b) + () + (f a, w) + a)
Now, we note that we can autoderive a functor instance.
deriving instance Functor (F a b f)
This is possible because in the original type each recursive reference only occurred in positive position. If this does not hold, making F a b f not a functor, then we can't have a catamorphism.
Finally, we can write the type of cata as follows:
cata :: (F a b f w -> w) -> X a b f -> w
Is this the OP's xCata type? It is. We only have to apply a few type isomorphisms. We use the following algebraic laws:
1) (a,b) -> c ~= a -> b -> c (currying)
2) (a+b) -> c ~= (a -> c, b -> c)
3) () -> c ~= c
By the way, it's easy to remember these isomorphisms if we write (a,b) as a product a*b, unit () as1, and a->b as a power b^a. Indeed they become
c^(a*b) = (c^a)^b
c^(a+b) = c^a*c^b
c^1 = c
Anyway, let's start to rewrite the F a b f w -> w part, only
F a b f w -> w
=~ (def F)
((Int, b) + () + (f a, w) + a) -> w
=~ (2)
((Int, b) -> w, () -> w, (f a, w) -> w, a -> w)
=~ (3)
((Int, b) -> w, w, (f a, w) -> w, a -> w)
=~ (1)
(Int -> b -> w, w, f a -> w -> w, a -> w)
Let's consider the full type now:
cata :: (F a b f w -> w) -> X a b f -> w
~= (above)
(Int -> b -> w, w, f a -> w -> w, a -> w) -> X a b f -> w
~= (1)
(Int -> b -> w)
-> w
-> (f a -> w -> w)
-> (a -> w)
-> X a b f
-> w
Which is indeed (renaming w=r) the wanted type
xCata :: (Int -> b -> r)
-> r
-> (f a -> r -> r)
-> (a -> r)
-> X a b f
-> r
The "standard" implementation of cata is
cata g = wrap . fmap (cata g) . unwrap
where unwrap (X y) = y
wrap y = X y
It takes some effort to understand due to its generality, but this is indeed the intended one.
About automation: yes, this can be automatized, at least in part.
There is the package recursion-schemes on hackage which allows
one to write something like
type X a b f = Fix (F a f b)
data F a b f w = ... -- you can use the actual constructors here
deriving Functor
-- use cata here
Example:
import Data.Functor.Foldable hiding (Nil, Cons)
data ListF a k = NilF | ConsF a k deriving Functor
type List a = Fix (ListF a)
-- helper patterns, so that we can avoid to match the Fix
-- newtype constructor explicitly
pattern Nil = Fix NilF
pattern Cons a as = Fix (ConsF a as)
-- normal recursion
sumList1 :: Num a => List a -> a
sumList1 Nil = 0
sumList1 (Cons a as) = a + sumList1 as
-- with cata
sumList2 :: forall a. Num a => List a -> a
sumList2 = cata h
where
h :: ListF a a -> a
h NilF = 0
h (ConsF a s) = a + s
-- with LambdaCase
sumList3 :: Num a => List a -> a
sumList3 = cata $ \case
NilF -> 0
ConsF a s -> a + s
A catamorphism (if it exists) is unique by definition. In category theory a catamorphism denotes the unique homomorphism from an initial algebra into some other algebra. To the best of my knowledge in Haskell all catamorphisms exists because Haskell's types form a Cartesian Closed Category where terminal objects, all products, sums and exponentials exist. See also Bartosz Milewski's blog post about F-algebras, which gives a good introduction to the topic.

What's distributing a functor over a tuple called?

Is there a name for this family of operations?
Functor f => f (a, b) -> (f a, f b)
Functor f => f (a, b, c) -> (f a, f b, f c)
...
Functor f => f (a, b, ..., z) -> (f a, f b, ..., f z)
They're easy to implement, just trying to figure out what to call it.
\fab -> (fst <$> fab, snd <$> fab)
For me, it came up in the context of f ~ (x ->).
In your specific context f ~ (x ->), I think they can be called "power laws".
Indeed, in theory, it is common to write A -> B as the power B^A. The pair type (A,B) is also commonly written as a product (A*B).
Your first law is then written as
(A*B)^C = A^C * B^C
and is a classic type isomorphism. This can be easily generalized to tuples in the obvious way.
In the general case, where f is an arbitrary functor, I can't think of nothing else than "distribution", right now.
There is Data.Distributive which is the dual of Data.Traversable. It provides the distribute function which can be specialized e.g. as f (Stream a) -> Stream (f a) or distribute :: f (Vec n a) -> Vec n (f a). The latter example is a homogeneous variant of your family of functions.
But we can generalize Data.Distributive a bit just like lenses generalize functors. Enter Colens:
type Colens s t a b = forall f. Functor f => (f a -> b) -> f s -> t
Here is the mirror of Control.Lens.Each:
class Coeach s t a b | s -> a, t -> b, s b -> t, t a -> s where
coeach :: Colens s t a b
instance (a~a', b~b') => Coeach (a,a') (b,b') a b where
coeach f p = (f $ fst <$> p, f $ snd <$> p)
instance (a~a2, a~a3, b~b2, b~b3) => Coeach (a,a2,a3) (b,b2,b3) a b where
coeach f p = ...
...
And just like with each we can iterate over tuples
each_id1 :: Applicative f => (f a, f a) -> f (a, a)
each_id1 = each id
each_id2 :: Applicative f => (f a, f a, f a) -> f (a, a, a)
each_id2 = each id
with coeach we can coiterate over tuples:
coeach_id1 :: Functor f => f (a, a) -> (f a, f a)
coeach_id1 = coeach id
coeach_id2 :: Functor f => f (a, a, a) -> (f a, f a, f a)
coeach_id2 = coeach id
This is still homogeneous, though. I don't know lens much, so can't say whether there is a heterogeneous each and the corresponding coeach.

Defining f(x,y) = (x + y) % 3 in Haskell [duplicate]

Ordinary function composition is of the type
(.) :: (b -> c) -> (a -> b) -> a -> c
I figure this should generalize to types like:
(.) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
A concrete example: calculating difference-squared. We could write diffsq a b = (a - b) ^ 2, but it feels like I should be able to compose the (-) and (^2) to write something like diffsq = (^2) . (-).
I can't, of course. One thing I can do is use a tuple instead of two arguments to (-), by transforming it with uncurry, but this isn't the same.
Is it possible to do what I want? If not, what am I misunderstanding that makes me think it should be possible?
Note: This has effectively already been asked here, but the answer (that I suspect must exist) was not given.
My preferred implementation for this is
fmap . fmap :: (Functor f, Functor f1) => (a -> b) -> f (f1 a) -> f (f1 b)
If only because it is fairly easy to remember.
When instantiating f and f1 to (->) c and (->) d respectively you get the type
(a -> b) -> (c -> d -> a) -> c -> d -> b
which is the type of
(.) . (.) :: (b -> c) -> (a -> a1 -> b) -> a -> a1 -> c
but it is a bit easier to rattle off the fmap . fmap version and it generalizes to other functors.
Sometimes this is written fmap fmap fmap, but written as fmap . fmap it can be more readily expanded to allow more arguments.
fmap . fmap . fmap
:: (Functor f, Functor g, Functor h) => (a -> b) -> f (g (h a)) -> f (g (h b))
fmap . fmap . fmap . fmap
:: (Functor f, Functor g, Functor h, Functor i) => (a -> b) -> f (g (h (i a))) -> f (g (h (i b))
etc.
In general fmap composed with itself n times can be used to fmap n levels deep!
And since functions form a Functor, this provides plumbing for n arguments.
For more information, see Conal Elliott's Semantic Editor Combinators.
The misunderstanding is that you think of a function of type a -> b -> c as a function of two arguments with return type c, whereas it is in fact a function of one argument with return type b -> c because the function type associates to the right (i.e. it's the same as a -> (b -> c). This makes it impossible to use the standard function composition operator.
To see why, try applying the (.) operator which is of type (y -> z) -> (x -> y) -> (x -> z) operator to two functions, g :: c -> d and f :: a -> (b -> c). This means that we must unify y with c and also with b -> c. This doesn't make much sense. How can y be both c and a function returning c? That would have to be an infinite type. So this does not work.
Just because we can't use the standard composition operator, it doesn't stop us from defining our own.
compose2 :: (c -> d) -> (a -> b -> c) -> a -> b -> d
compose2 g f x y = g (f x y)
diffsq = (^2) `compose2` (-)
Usually it is better to avoid using point-free style in this case and just go with
diffsq a b = (a-b)^2
I don't know of a standard library function that does this, but the point-free pattern that accomplishes it is to compose the composition function:
(.) . (.) :: (b -> c) -> (a -> a1 -> b) -> a -> a1 -> c
I was going to write this in a comment, but it's a little long, and it draws from both mightybyte and hammar.
I suggest we standardize around operators such as .* for compose2 and .** for compose3. Using mightybyte's definition:
(.*) :: (c -> d) -> (a -> b -> c) -> (a -> b -> d)
(.*) = (.) . (.)
(.**) :: (d -> e) -> (a -> b -> c -> d) -> (a -> b -> c -> e)
(.**) = (.) . (.*)
diffsq :: (Num a) => a -> a -> a
diffsq = (^2) .* (-)
modminus :: (Integral a) => a -> a -> a -> a
modminus n = (`mod` n) .* (-)
diffsqmod :: (Integral a) => a -> a -> a -> a
diffsqmod = (^2) .** modminus
Yes, modminus and diffsqmod are very random and worthless functions, but they were quick and show the point. Notice how eerily easy it is to define the next level by composing in another compose function (similar to the chaining fmaps mentioned by Edward).
(.***) = (.) . (.**)
On a practical note, from compose12 upwards it is shorter to write the function name rather than the operator
f .*********** g
f `compose12` g
Though counting asterisks is tiring so we may want to stop the convention at 4 or 5 .
[edit] Another random idea, we could use .: for compose2, .:. for compose3, .:: for compose4, .::. for compose5, .::: for compose6, letting the number of dots (after the initial one) visually mark how many arguments to drill down. I think I like the stars better though.
As Max pointed out in a comment:
diffsq = ((^ 2) .) . (-)
You can think of f . g as applying one argument to g, then passing the result to f. (f .) . g applies two arguments to g, then passes the result to f. ((f .) .) . g applies three arguments to g, and so on.
\f g -> (f .) . g :: (c -> d) -> (a -> b -> c) -> a -> b -> d
If we left-section the composition operator with some function f :: c -> d (partial application with f on the left), we get:
(f .) :: (b -> c) -> b -> d
So we have this new function which expects a function from b -> c, but our g is a -> b -> c, or equivalently, a -> (b -> c). We need to apply an a before we can get what we need. Well, let's iterate once more:
((f .) .) :: (a -> b -> c) -> a -> b -> d
Here's what I think is an elegant way to achieve what you want. The Functor type class gives a way to 'push' a function down into a container so you can apply it to each element using fmap. You can think of a function a -> b as a container of bs with each element indexed by an element of a. So it's natural to make this instance:
instance Functor ((->) a) where
fmap f g = f . g
(I think you can get that by importing a suitable library but I can't remember which.)
Now the usual composition of f with g is trivially an fmap:
o1 :: (c -> d) -> (b -> c) -> (b -> d)
f `o1` g = fmap f g
A function of type a -> b -> c is a container of containers of elements of type c. So we just need to push our function f down twice. Here you go:
o2 :: (c -> d) -> (a -> (b -> c)) -> a -> (b -> d)
f `o2` g = fmap (fmap f) g
In practice you might find you don't need o1 or o2, just fmap. And if you can find the library whose location I've forgotten, you may find you can just use fmap without writ
ing any additional code.

Resources