Search code examples
gadthigher-rank-typespurescript

Rank-2 types in data constructors


I've been trying to encode GADTs in PureScript using rank-2 types, as described here for Haskell

My code looks like:

data Z
data S n

data List a n 
  = Nil (Z -> n)
  | Cons forall m. a (List a m) (S m -> n)

fw :: forall f a. (Functor f) => (forall b . (a -> b) -> f b) -> f a
fw f = f id

bw :: forall f a. (Functor f) => f a -> (forall b . (a -> b) -> f b)
bw x f = map f x

nil :: forall a. List a Z
nil = fw Nil

cons :: forall a n. a -> List a (S n)
cons a as = fw (Cons a as)


instance listFunctor :: Functor (List a) where
    map f (Nil k) = Nil (f <<< k)
    map f (Cons x xs k) = Cons x xs (f <<< k)

The compiler complains Wrong number of arguments to constructor Main.Cons, referring to the LHS pattern match in the Functor instance.

What is going wrong here?

Regards,

Michael


Solution

  • The syntax used for existential types in Haskell is not present in PureScript. What you've written for Cons is a data constructor with a single universally-quantified argument.

    You might like to try using purescript-exists to encode the existential type instead.

    Another option is to use a final-tagless encoding of the GADT:

    class Listy l where
      nil :: forall a. l Z a
      cons :: forall a n. a -> l n a -> l (S n) a
    

    You can write terms for any valid Listy instance:

    myList :: forall l. (Listy l) => l (S (S Z)) Int
    myList = cons 1 (cons 2 nil)
    

    and interpret them by writing instances

    newtype Length n a = Length Int
    
    instance lengthListy :: Listy Length where
      nil = Length 0
      cons _ (Length n) = Length (n + 1)
    
    newtype AsList n a = AsList (List a)
    
    instance asListListy :: Listy AsList where
      nil = AsList Nil
      cons x (AsList xs) = AsList (Cons x xs)