I’ve been playing around with GHC a little bit and thought I would try to examine exactly what existential datatypes do. I will keep this post rather informal to share my intuitions that I’ve gained so far into a non-standard part of Haskell that appears to have some interesting uses, partially in an effort to make this post accessible to those that don’t approach Haskell from a well-versed mathematical background and partially because I am not so bold as to attempt treating the subject formally when I have only learned a little through experimentation. Any corrections to this post would be greatly appreciated.

With that said, we will make use of a contrived example for the sake of simplicity . I don’t seem to have code highlighting, so apologies in advance for that; however, this post is a Literate Haskell file, so you can copy this into a plaintext file and all will be well.

To start off, we enable the existential quantification extension:

> {-# LANGUAGE ExistentialQuantification #-}

Existentially quantified datatypes essentially allow you to hide the type values of a datatype’s parameters from the outside world. As a simple example, consider the following:

> data Thingum a = forall b. Show b => Thing b a

Ignoring the “forall b. Show b =>” segment for a moment, notice that a type variable b is used in the type constructor Thing but doesn’t appear in the datatype’s type signature. Indeed, if we examine the kind of Thingum, we’ll notice something odd: Thing has the kind (* -> *)! If we write Thingum as “data Thingum a b = Thing b a”, (b being an instance of Show doesn’t really matter for kinds), then the kind comes out as (* -> * -> *). What each * on the left hand of an arrow denotes is a “slot” for a type that needs to be filled in in order for us to get any real result out of calculations involving this type. Thus, with our existentially quantified datatype, the data of type b inside of Bm is essentially inaccessible outside of Bm. This is where “forall b. Show b =>” comes in. By declaring that the type variable b must be an instance of show, we can, in a limited way, interact with b. Here’s what I mean demonstrated through an example:

We’ll declare a new type Foo:

> data Foo = forall a. Test a

So far so good. In fact, this is rather exciting! We can put any type of “a” we want into this datatype! Congratulations, we just bypassed Haskell’s type system! Well, not really. Can you think of why? Here’s a hint: think about what happens when we want to free our type from its Test container.

Let’s define a function to do just that:

> bar :: Foo -> a

> bar (Test x) = x

Uhhh… well what is the x? And why isn’t this compiling? Is x a String? Is it an Int? Is it a function? Now that we have this value, what can we do with it? Well, with just this code, we can’t really do anything with it because we can’t know what functions can accommodate x as a parameter. This is why we need the value to be of a type that has is an instance of a typeclass, such as “forall b. Show b” in our first example. (Note that the forall in this context simply gives the compiler a hint that we explicitly want b to be existentially quantified in our datatype). Now, Haskell has a fact about b that it can work with. We know that when all is said and done, we can convert b into a String and do something useful with it. We can toss any showable thing we want into the b slot, and when we decide we want to read about it, we are able to because the compiler can verify that show works at compile time:

> showHidden (Thing showMe _) = show showMe

Bam! We’ve freed our b from its chains, and have done so in a typesafe way. However, the only way out for a value of type b in this example is through the show function. So, unless you only intend to be using b as a string, it would be prudent to ensure that the typeclass instance that you use can fully support the features you need.

I’ll leave it to your imagination to come up with more interesting uses for this, but this just about covers the basics. Feel free to comment with any interesting uses of existentially qualified types. I’d love to see what people do with these odd extensions to Haskell98.