Skip to content

Missing example of interaction with existential types? #27

@TD5

Description

@TD5

This library is a really great example of pushing the limits of what you can do with F#!

When I created #26 and discussed this with someone, they quickly pointed out that GADTs are supposed to have something to do with existential types, but they couldn't actually see any existential types in the example! Below, I've captured what I think is going on. Does it make sense? Am I missing something?

The current example

I think the existing example is neat because it is so simple:

type 'a Expr =
| Const of Teq<int, 'a> * int
| Add of Teq<int, 'a> * Expr<int> * Expr<int>
| IsZero of Teq<bool, 'a> * Expr<int>
| If of Expr<bool> * Expr<'a> * Expr<'a>

In pseudocode (for readability and comparison to the literature):

Const  :: int                           -> Expr int
Add    :: Expr int -> Expr int          -> Expr int
IsZero :: Expr int                      -> Expr bool
If     :: Expr bool -> Expr a -> Expr a -> Expr a

However, I think it is actually too simple, and misses out on one of the key applications of GADTs!

A future example

Consider this example based on a snippet from wikipedia,

GADT definition pseudocode:

  Lift :: a                     -> Lam a        -- ^ lifted value
  Pair :: Lam a -> Lam b        -> Lam (a, b)   -- ^ product
  Lam  :: (Lam a -> Lam b)      -> Lam (a -> b) -- ^ lambda abstraction
  App  :: Lam (a -> b) -> Lam a -> Lam b        -- ^ function application

Evaluation function pseudocode:

eval (Lift v)   = v
eval (Pair l r) = (eval l, eval r)
eval (Lam f)    = \x -> eval (f (Lift x))
eval (App f x)  = (eval f) (eval x)

Notice how the App case is a bit special, because its a type parameter doesn't appear on the right-hand side of the constructor signature: App :: Lam (a -> b) -> Lam a -> Lam b. What this means is that when you have a Lam b in hand that happens to be in the App case, you don't know what the type of a is - you only know you have a Lam (a -> b) and a Lam a such that the a agrees (which means the type checker lets you apply one to the other)!

The implication here is that in order to construct this situation and hide the a away, you need a way to encode existentials! A while ago, I wrote about existentials in F#, but it would be really nice if there was a second example in this project which demonstrated how to bring these two ideas together.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions