-
Notifications
You must be signed in to change notification settings - Fork 10
Description
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 aHowever, 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 applicationEvaluation 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.