It’s existential on the inside

This is the eighth of a series of articles on “Type Parameters and
Type Members”. You may wish to
check out the beginning,
which introduces the PList type we refer to throughout this article
without further ado.

When you start working with type parameters, nothing makes it
immediately apparent that you are working with universal and
existential types at the same time. It is literally a matter of
perspective.

I will momentarily set aside a lengthy explanation of what this means,
in favor of some diagrams.

Universal (!) outside, existential inside

wazzle “returns” a type, alongside the list, because the existential
appears as part of the return type. However, duzzle2 places the
existential in argument position. So, as with all type-parameterized
cases, duzzle among them, this is one where the caller determines
the type.

We’ve discussed how you can
prove that duzzle ≡mduzzle2, in a
previous post. Now, it’s time to see why.

Type parameters are parameters

The caller chooses the value of a type parameter. It also chooses the
value of normal parameters. So, it makes sense to treat them the same.

Let’s try to look at fizzle’s type this way.

[A]=>PList[A]=>PList[A]

Existential types are pairs

If wazzle returns a type and a value, it makes sense to treat them
as a returned pair.

The duzzles are currying

With these two models, we can finally get to the bottom of
duzzle ≡mduzzle2. Here are their
types, rewritten in the forms we’ve just seen.

[A]=>List[A]=>Int([A],List[A])=>Int

Recognize that? They’re just the curried and uncurried forms of the
same function type.

You can also see why the same type change will not work for wazzle.

Int=>([A],List[A])[A]=>Int=>List[A]

We’ve moved part of the return type into an argument, which is…not the
same.

The future of types?

This formulation of universal and existential types is due to
dependently-typed systems, in which they are “dependent functions” and
“dependent pairs”, respectively, though with significantly more
expressive power than we’re working with here. They come by way of the
description of the Quest programming language in
“Typeful Programming” by Luca Cardelli,
which shows in a clear, syntactic way that the dependent view of
universal and existential types is perfectly cromulent to
non-dependent type systems like Scala’s.

It is also the root of my frustration that Scala doesn’t support a
forAll, like forSome but for universally-quantified types. After
all, you can’t work with one without the other.