Thanks! That was exactly the sort of response I was looking for.
This explains why you need to double up for your current definitions. To
choose between two booleans (which will in turn allow you to choose between
'a's), you need a CB (CB a). You can eliminate the asymmetric type, though,
like so:
cand p q t f = p (q t f) f
cand :: CB a -> CB a -> CB a
You can probably always do this, but it will become more tedious the more
complex your functions get.
Note: this is universal quantification, not existential.
> >type CB a = forall a . a -> a -> a
In the new type, the parameter 'a' is misleading. It has no connection to the
'a's on the right of the equals sign. You might as well write:
type CB = forall a. a -> a -> a
And now, hopefully, a key difference can be seen: we no longer have the result
type for case analysis as a parameter of the type. Rather, they must work 'for
all' result types, and we can choose which result type to use when we need to
eliminate them (and you can choose multiple times when using the same boolean
value in multiple places).
One may think about explicit typing and type abstraction. Suppose we have your
first type of boolean at a particular type T. We'll call said type CBT. Then
you have:
CBT = T -> T -> T
and values look like:
\(t :: T) (f :: T) -> ...
By contrast, values of the second CB type look like this:
\(a :: *) (t :: a) (f :: a) -> ...
*snip*
cand (T :: *) (p :: CB T) (q :: CB T) = ...
cand gets told what T is; it doesn't get to choose.
Hopefully I didn't make that too over-complicated, and you can glean something
useful from it. It turned out a bit longer than I expected.