Biapplicative and Bimonad? - haskell

Haskell's Data.Bifunctor is basically:
class Bifunctor f where
bimap :: (a -> c) -> (b -> d) -> f a b -> f c d
I could find a Biapply as well. My question is, why isn't there a complete bi-hierarchy (bierarchy?) like:
class Bifunctor f => Biapplicative f where
bipure :: a -> b -> f a b
biap :: f (a -> b) (c -> d) -> f a c -> f b d
class Biapplicative m => Bimonad m where
bibind :: m a b -> (a -> b -> m c d) -> m c d
bireturn :: a -> b -> m a b
bireturn = bipure
bilift :: Biapplicative f => (a -> b) -> (c -> d) -> f a c -> f b d
bilift f g = biap $ bipure f g
bilift2 :: Biapplicative f => (a -> b -> c) -> (x -> y -> z) -> f a x -> f b y -> f c z
bilift2 f g = biap . biap (bipure f g)
Pair is an instance of these:
instance Bifunctor (,) where
bimap f g (x,y) = (f x, g y)
instance Biapplicative (,) where
bipure x y = (x,y)
biap (f,g) (x,y) = (f x, g y)
instance Bimonad (,) where
bibind (x,y) f = f x y
And types like...
data Maybe2 a b = Fst a | Snd b | None
--or
data Or a b = Both a b | This a | That b | Nope
...would IMO have instances as well.
Are there not enough matching types? Or is something concerning my code deeply flawed?

A monad in category theory is an endofunctor, i.e. a functor where the domain and codomain is the same category. But a Bifunctor is a functor from the product category Hask x Hask to Hask. But we could try to find out what a monad in the Hask x Hask category looks like. It is a category where objects are pairs of types, i.e. (a, b), and arrows are pairs of functions, i.e. an arrow from (a, b) to (c, d) has type (a -> c, b -> d). An endofunctor in this category maps pairs of types to pairs of types, i.e. (a, b) to (l a b, r a b), and pairs of arrows to pairs of arrows, i.e.
(a -> c, b -> d) -> (l a b -> l c d, r a b -> r c d)
If you split this map function in 2, you'll see that an endofunctor in Hask x Hask is the same as two Bifunctors, l and r.
Now for the monad: return and join are arrows, so in this case both are 2 functions. return is an arrow from (a, b) to (l a b, r a b), and join is an arrow from (l (l a b) (r a b), r (l a b) (r a b)) to (l a b, r a b). This is what it looks like:
class (Bifunctor l, Bifunctor r) => Bimonad l r where
bireturn :: (a -> l a b, b -> r a b)
bijoin :: (l (l a b) (r a b) -> l a b, r (l a b) (r a b) -> r a b)
Or separated out:
class (Bifunctor l, Bifunctor r) => Bimonad l r where
bireturnl :: a -> l a b
bireturnr :: b -> r a b
bijoinl :: l (l a b) (r a b) -> l a b
bijoinr :: r (l a b) (r a b) -> r a b
And similar to m >>= f = join (fmap f m) we can define:
bibindl :: l a b -> (a -> l c d) -> (b -> r c d) -> l c d
bibindl lab l r = bijoinl (bimap l r lab)
bibindr :: r a b -> (a -> l c d) -> (b -> r c d) -> r c d
bibindr rab l r = bijoinr (bimap l r rab)
Relative monads
Recently, relative monads have been developed. A relative monad doesn't need to be an endofunctor! If we translate from the paper to Bifunctors in Haskell, you get:
class RelativeBimonad j m where
bireturn :: j a b -> m a b
bibind :: m a b -> (j a b -> m c d) -> m c d
Which defines a monad relative to the bifunctor j. If you pick j to be (,) you get your definition.
The laws are the same as the monad laws:
bireturn jab `bibind` k = k jab
m `bibind` bireturn = m
m `bibind` (\jab -> k jab `bibind` h) = (m `bibind` k) `bibind` h
The first law prevents Maybe2 from being an instance, because bibind has to be able to extract both values from the result of bireturn.

Related

How to implement this higher order function

I'm a newbe in functional programming, and I'm trying to solve the following exercise;
Given the type
type Cont r a = (a -> r) -> r
Implement the following higher-order function
mapReader :: (a -> b) -> (Cont r a) -> Cont r b
The first step would be to simplify the types, which gives:
mapReader :: (a -> b) -> ((a -> r) -> r) -> (b -> r) -> r
Next, define the parameters that need to be provided in this function. These parameters are three functions so we get
mapReader :: (a -> b) -> ((a -> r) -> r) -> (b -> r) -> r
mapReader f g h = _1
From here, we can define the following types:
f :: a -> b
g :: (a -> r) -> r
h :: b -> r
_1 :: r
But now I'm stuck. There are two functions that result in r, and one of them contains another function (a -> r). How can I start defining r? Any hints are much appreciated!
We have
f :: a -> b
g :: (a -> r) -> r
h :: b -> r
and we need
_1 :: r
There are two ways we can make r: g and h.
Let's try using h. h takes an argument of type b. The only way to get one of those is using f. f takes an argument of type a, and ... we don't have any way to get one of those.
So now let's try using g instead:
mapReader f g h = g _2
We're told
_2 :: a -> r
Since we're constructing a function, we can apply lambda abstraction as usual:
mapReader f g h = g (\a -> _3)
a :: a
_3 :: r
But wait ... now we have an a, so we can go back to our first attempt:
mapReader f g h = g (\a -> h (f a))
Or, more compactly,
mapReader f g h = g (h . f)
What if instead of going back to the first attempt we did it the second way again?
mapReader' f g h =
g (\a1 -> g (\a2 -> _4))
_4 :: r
You could go this way forever, but you could also stop here in two different ways:
mapReader2 f g h =
g (\_ -> g (h . f))
mapReader3 f g h =
g (\a1 -> g (\_ -> h (f a1)))
Oy! These are three different functions that all have the same type, and as shown this approach can be used to generate an infinite family of functions! How can you decide which one you want? You have to consider the intention. g's argument is the continuation, so you want to compose a function with what you're passing g, not call g multiple times. So mapReader is the "correct" answer.
More precisely, mapReader is supposed to map morphisms for the continuation functor. That requires in particular that
mapReader id = id
That is,
mapReader id g h = g (h . id)
= g h
That's unconditionally true for the correct definition, but not for any of the others.
Start by looking at what you can do with the three arguments.
You can compose f and h: h . f :: a -> r.
You can apply g to h . f: g (h . f) :: r.
So you could simply say that mapReader f g h = g (h . f). There's not enough information here to specify what r is; it depends entirely on what
arguments g and h are given to mapReader.
So you have
f :: a -> b
h :: b -> r
g :: (a -> r) -> r
There's also the forward functional composition operator,
(>>>) :: (a -> b) -> (b -> r) -> (a -> r)
and the reversed application operator,
(&) :: t -> (t -> r) -> r
so that
f >>> h :: ......... -- what?
and
(f >>> h) & g :: ......... -- what else?
Can you come up with the definitions of (>>>) and (&), just from their types?
Let me get you started on the first one.
(>>>) :: (a -> b) -> (b -> r) -> (a -> r)
means that
(>>>) (f :: a -> b) :: (b -> r) -> (a -> r)
(>>>) (f :: a -> b) (g :: b -> r) :: (a -> r)
(>>>) (f :: a -> b) (g :: b -> r) (x :: a) :: r
So again we write them down
f :: a -> b
g :: b -> r
x :: a
f x :: b
g (f x) :: ....
And that's that.
The most important rule that we used here, is
x :: a
f :: a -> r
f x :: r

Why does join . (flip fmap) have type ((A -> B) -> A) -> (A -> B) -> B?

Some playing around with functors and monads in ghci led me to a value whose type and behaviour I would like to understand better.
The type of \x -> join . x is (Monad m) => (a -> m (m b)) -> (a -> m b) and the type of \y -> y . (flip fmap) is (Functor f) => ((a -> b) -> f b) -> (f a -> c).
Version 8.2.2 of ghci permits the definition h = join . (flip fmap).
Why does h have type ((A -> B) -> A) -> (A -> B) -> B?
In particular, why do the functor and monad constraints disappear? Is this really the correct and expected behaviour? As a follow up, I would also like to ask:
Why does evaluating h (\f -> f u) (\x -> x + v) for integers u and v give u + 2v in every case?
In short: due to type deduction, Haskell knows that m and f are in fact a partially instantiated arrow.
Deriving the type
Well let us do the math. The function join . (flip fmap) is basically your given lambda expression \x -> join . x with as argument (flip fmap), so:
h = (\x -> join . x) (flip fmap)
Now the lambda expression has type:
(\x -> join . x) :: Monad m => (a -> m (m b)) -> (a -> m b)
Now the argument flip fmap has type:
flip fmap :: Functor f => f c -> ((c -> d) -> f d)
(we here use c and d instead of a and b to avoid confusion between two possibly different types).
So that means that the type of flip fmap is the same as the type of the argument of the lambda expression, hence we know that:
Monad m => a -> m (m b)
~ Functor f => f c -> ((c -> d) -> f d)
---------------------------------------
a ~ f c, m (m b) ~ ((c -> d) -> f d)
So we now know that a has the same type as f c (this is the meaning of the tilde ~).
But we have to do some extra computations:
Monad m => m (m b)
~ Functor f => ((c -> d) -> f d)
--------------------------------
m ~ (->) (c -> d), m b ~ f d
Hence we know that m is the same as (->) (c -> d) (basically this is a function where we know that input type, here (c -> d), and the output type is a type parameter of m.
So that means that m b ~ (c -> d) -> b ~ f d, so this means that f ~ (->) (c -> d) and b ~ d. An extra consequence is that since a ~ f c, we know that a ~ (c -> d) -> c
So to list what we derived:
f ~ m
m ~ (->) (c -> d)
b ~ d
a ~ (c -> d) -> c
So we now can "specialize" the types of both our lambda expression, and our flip fmap function:
(\x -> join . x)
:: (((c -> d) -> c) -> (c -> d) -> (c -> d) -> d) -> ((c -> d) -> c) -> (c -> d) -> d
flip fmap
:: ((c -> d) -> c) -> (c -> d) -> (c -> d) -> d
and type of flip fmap now perfectly matches with the type of the argument of the lambda expression. So the type of (\x -> join . x) (flip fmap) is the result type of the lambda expression type, and that is:
(\x -> join . x) (flip fmap)
:: ((c -> d) -> c) -> (c -> d) -> d
But now we of course did not yet obtained the implementation of this function. We are however already a step further.
Deriving the implementation
Since we now know that m ~ (->) (c -> d), we know we should lookup the arrow instance of a monad:
instance Monad ((->) r) where
f >>= k = \ r -> k (f r) r
So for a given function f :: r -> a, as left operand, and a function k :: a -> (r -> b) ~ a -> r -> b as operand, we construct a new function that maps a variable x to k applied to f applied to x, and x. It is thus a way to perform some sort of preprocessing on an input variable x, and then do the processing both taking into account the preprocessing and the original view (well this is an interpretation a human reader can use).
Now join :: Monad m => m (m a) -> m a is implemented as:
join :: Monad m => m (m a) -> m a
join x = x >>= id
So for the (->) r monad, this means that we implement this as:
-- specialized for `m ~ (->) a
join f = \r -> id (f r) r
Since id :: a -> a (the identity function) returns its argument, we can further simplify it to:
-- specialized for `m ~ (->) a
join f = \r -> (f r) r
or cleaner:
-- specialized for `m ~ (->) a
join f x = f x x
So it basically is given a function f, and will then apply an argument twice to that function.
Furthermore we know that the Functor instance for the arrow type is defined as:
instance Functor ((->) r) where
fmap = (.)
So it is basically used as a "post processor" on the result of the function: we construct a new function that will do the post processing with the given function.
So now that we specialized the function enough for the given Functor/Monad, we can derive the implementation as:
-- alternative implementation
h = (.) (\f x -> f x x) (flip (.))
or by using more lambda expressions:
h = \a -> (\f x -> f x x) ((flip (.)) a)
which we can now further specialize as:
h = \a -> (\f x -> f x x) ((\y z -> z . y) a)
-- apply a in the lambda expression
h = \a -> (\f x -> f x x) (\z -> z . a)
-- apply (\z -> z . a) in the first lambda expression
h = \a -> (\x -> (\z -> z . a) x x)
-- cleaning syntax
h a = (\x -> (\z -> z . a) x x)
-- cleaning syntax
h a x = (\z -> z . a) x x
-- apply lambda expression
h a x = (x . a) x
-- remove the (.) part
h a x = x (a x)
So h basically takes two arguments: a and x, it then performs function application with a as function and x as parameter, and the output is passed to the x function again.
Sample usage
As sample usage you use:
h (\f -> f u) (\x -> x + v)
or nicer:
h (\f -> f u) (+v)
so we can analyze this like:
h (\f -> f u) (+v)
-> (+v) ((\f -> f u) (+v))
-> (+v) ((+v) u)
-> (+v) (u+v)
-> ((u+v)+v)
So we add u+v to v.
Types line up easier with >>>:
a -> b >>>
b -> c ::
a -> c
Here, we have
join . flip fmap == flip fmap >>> join
flip fmap :: Functor f => f a -> ((a -> b) -> f b )
join :: Monad m => (m (m b)) -> m b
----------------------------------------------------------
flip fmap >>> join ::
(Functor f, Monad m) => f a -> m b , ((a -> b) ->) ~ m, f ~ m
::
(Functor f, Monad f) => f a -> f b , f ~ ((a -> b) ->)
:: ((a -> b) -> a) -> ((a -> b) -> b)
Simple, mechanical, mundane.
To see what it does, combinatory style definitions are usually easiest to twiddle with,
(join . flip fmap) f g x =
join (flip fmap f) g x = -- join f x = f x x
(`fmap` f) g g x = -- f `fmap` g = f . g
(g . f) g x
g (f g) x
So we don't need x after all (or do we?). The join and fmap definitions for functions are given in the margins. We've arrived at
(join . flip fmap) f g = g (f g) -- f :: (a -> b) -> a, g :: a -> b
-- f g :: a , g (f g) :: b
Another way is starting from the types, going by the rule of modus ponens,
((a -> b) -> a) (a -> b) -- f g
---------------------------
(a -> b) a -- g (f g)
---------------------------------------
b

Manual type inference in Haskell

Consider the function
f g h x y = g (g x) (h y)
What is its type? Obviously I can just use :t f to find out, but if I need to deduce this manually, what's the best way to go about this?
The method I have been shown is to assign types to parameters and deduce from there - e.g. x :: a, y :: b gives us that g :: a -> c and h :: b -> d for some c,d (from g x, h y) and then we keep on making deductions from there (c = a from g (g x) (h y) etc.).
However this sometimes just turns into a huge mess and often I'm not sure how to make further deductions or work out when I'm done. Other problems sometimes happen - for instance, in this case x will turn out to be a function, but that was not obvious to me before cheating and looking up the type.
Is there a specific algorithm that will always work (and is reasonable for a human to execute quickly)? Otherwise, are there some heuristics or tips that I am missing?
Let's inspect the function at the top level:
f g h x y = g (g x) (h y)
We will begin by assigning names to types, then going along and specialising them as we learn more about the function.
Firstly, let's assign a type to the top expression. Let's call it a:
g (g x) (h y) :: a
Let's take the first argument out and assign types respectively:
-- 'expanding' (g (g x)) (h y) :: a
h y :: b
g (g x) :: b -> a
And again
-- 'expanding' g (g x) :: b -> a
g x :: c
g :: c -> b -> a
And again
-- 'expanding' g x :: c
x :: d
g :: d -> c
But hold on: we now have that g :: c -> b -> a and that g :: d -> c. So by inspection, we know that c and d are equivalent (written c ~ d) and also that c ~ b -> a.
This can be inferred by simply comparing the two types for g that we have inferred. Note that this is not a type contradiction, since the type variables are general enough to fit their equivalents. This would be a contradiction if we had inferred, for instance, that Int ~ Bool somewhere.
So we now have the following information in total: (a little work omitted)
y :: e
h :: e -> b
x :: b -> a -- Originally d, applied d ~ b -> a.
g :: (b -> a) -> b -> a -- Originally c -> b -> a, applied c ~ b -> a
This was done by substituting the most specific form of each type variable, that is substituting c and d for the more specific b -> a.
So, simply inspecting which arguments go where, we see that
f :: ((b -> a) -> b -> a) -> (e -> b) -> (b -> a) -> e -> a
This is confirmed by GHC.
Well the function is:
f g h x y = g (g x) (h y)
or more verbose:
f g h x y = (g (g x)) (h y)
Intially we assume that all the four parameters (g, h, x, and y) have different types. We also introduce an output type for our function (here t):
g :: a
h :: b
x :: c
y :: d
f g h x y :: t
But now we are going to perform some inference. We see for example g x, so this means that there is a function application with g the function, and x the parameter. This means that g is a function, with as input type c, so we redefine the type of g to:
g :: a ~ (c -> e)
h :: b
x :: c
y :: d
f g h x y :: t
(here the tilde ~ means that two types are the same, so a is the same as c -> e)).
Since g has type g :: c -> e, and x has type c, this thus means that the result of the function application g x has type g x :: e.
We see another function application, g as function, and g x as argument. So this means that the input type of g (which is c), should be equal to the type of g x (which is e), hence we know that c ~ e, so the types now are:
c ~ e
g :: a ~ (c -> c)
h :: b
x :: c
y :: d
f g h x y :: t
Now we see a function application with h the function, and y the argument. So that means that h is a function, and the input type is the same as the type of y :: d, so h has type d -> f, so that means:
c ~ e
g :: a ~ (c -> c)
h :: b ~ (d -> f)
x :: c
y :: d
f g h x y :: t
finally we see a function application with g (g x) the function, and h y the argument, so that means that the ouput type of g (g x) :: c should be a function, with f as input type, and t as output type, so that means that c ~ (f -> t), and therefore:
c ~ e
c ~ (f -> t)
g :: a ~ (c -> c) ~ ((f -> t) -> (f -> t))
h :: b ~ (d -> f)
x :: (f -> t)
y :: d
f g h x y :: t
So that means that since f has those parameters g, h, x and y, the type of f is:
f :: ((f -> t) -> (f -> t)) -> (d -> f) -> (f -> t) -> d -> t
-- \_________ __________/ \__ ___/ \__ ___/ |
-- v v v |
-- g h x y
You already described how to do it, but maybe you missed the unification step. That is, sometimes we know that two variables are the same:
x :: a
y :: b
g :: a -> b -- from g x
h :: c -> d -- from h y
a ~ b -- from g (g x)
We know that a and b are the same, because we passed g x, a b, to g, which expects an a. So now we replace all the bs with a, and keep going until we have considered all subexpressions...
With regard to your "huge mess" comment, I have a couple things to say:
This is the way to do it. If it's too hard, you just need to practice, and it will get easier. You will start to develop an intuition and it will come more easily.
This particular function is not an easy function to do. I've been programming Haskell for 12 years and I still need to go through the unification algorithm on paper for this one. The fact that it is so abstract doesn't help -- if I knew what this function's purpose was it would be much easer.
Simply write down all the entities' types under them:
f g h x y = g (g x) (h y)
x :: x y :: y
h :: y -> a , h y :: a
g :: x -> b , g x :: b
g :: b -> (a -> t) , x ~ b , b ~ (a -> t)
f :: (x -> b) -> (y -> a) -> x -> y -> t , x ~ b , b ~ (a -> t)
f :: (b -> b) -> (y -> a) -> b -> y -> t , b ~ (a -> t)
-- g h x y
Thus f :: ((a -> t) -> (a -> t)) -> (y -> a) -> (a -> t) -> y -> t. That's all.
Indeed,
~> :t let f g h x y = g (g x) (h y) in f
:: ((t1 -> t) -> t1 -> t) -> (t2 -> t1) -> (t1 -> t) -> t2 -> t
This goes like this:
x must have some type, let's call it x: x :: x.
y must have some type, let's call it y: y :: y.
h y must have some type, let's call it a: h y :: a. hence h :: y -> a.
g x must have some type, let's call it b: g x :: b. hence g :: x -> b.
g _ _ must have some type, let's call it t. hence g :: b -> a -> t.
which is the same as g :: b -> (a -> t).
the two type signatures for g must unify, i.e. be the same under some substitution of type variables involved, since the two signatures describe the same entity, g.
thus we must have x ~ b, b ~ (a -> t). This is the substitution.
Having all the types of the arguments to f, we know it produces what g produces, i.e. t. So we can write down its type, (x -> b) -> (y -> a) -> x -> y -> t.
Lastly, we substitute the types according to the substitution, to reduce the number of type variables involved. Thus we substitute b for x first, and then a -> t for b, each time removing the eliminated type variable from the substitution.
When the substitution is empty, we are DONE.
Of course we could have chosen to replace b with x at first, ending up with the substitution x ~ (a -> t), and then we'd end up with the same type in the end, if we always replace the simpler types with the more complex ones (like, replacing b with (a -> t), and not vice versa).
Simple steps, guaranteed results.
Here's another attempt at shorter / clearer derivation. We focus on the fact that g x serves as g's argument, thus g x :: x (and the trivial part still remains, h y :: a):
f g h x y = g (g x) (h y) {- g :: g , h :: h , x :: x , y :: y
g h x y x y , g x :: x -- !
x a t , g x a :: t
x a :: t ... x ~ a->t
f :: g ->h ->x ->y->t
f :: (x ->x )->(y->a)->x ->y->t
f :: ((a->t)->a->t)->(y->a)->(a->t)->y->t -}
Pretty simple after all.
The last argument in the definition can be elided, as f g h x = (g . g) x . h.

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.

`bind` Equivalent to join (fmap f m)?

This excellent answer in this question demonstrates how bind can be written in terms of join and fmap:
(>>=) :: m v -> (v -> m w) -> m w
says "if you have a strategy to produce a v, and for each v a
follow-on strategy to produce a w, then you have a strategy to produce
a w". How can we capture that in terms of join?
mv >>= v2mw = join (fmap v2mw mv)
But, I don't understand how v2mw, which has a type of a -> m b type checks to the first argument of fmap.
fmap :: Functor f => (a -> b) -> f a -> f b
Let's say v2mw :: c -> m d, just so things aren't ambiguous, and
fmap :: Functor f => (a -> b) -> f a -> f b
Then fmap v2mw works out so that f ~ m, a ~ c and b ~ m d, so
fmap v2mw :: m c -> m (m d)
and join :: m (m e) -> m e, so join (fmap v2mw mv) has type m d as expected.

Resources