Consider this Vect
type:
{-# LANGUAGE GADTs, DataKinds, KindSignatures, RankNTypes #-}
import Data.Kind (Type)
data Nat = Zero | Succ Nat
data Vect :: Nat -> Type -> Type where
Nil :: Vect 'Zero a
Cons :: a -> Vect n a -> Vect ('Succ n) a
I'd like to write a function that takes a list and lazily transforms it to this type. Since the length obviously isn't known, we need to use an existential. My first attempt at this used CPS and a rank-2 type to emulate one:
listToVect1 :: [a] -> (forall n. Vect n a -> b) -> b
listToVect1 [] f = f Nil
listToVect1 (x:xs) f = listToVect1 xs (f . Cons x)
However, this isn't lazy. This is just a foldl (though written as explicit recursion because actually using foldl
would require impredicativity), so f
won't start running until it's gone all the way to the end of the list.
I can't think of a way to do CPS in a right-associative manner, so I tried again, this time with a wrapper type instead:
data ArbitraryVect a = forall n. ArbitraryVect (Vect n a)
nil :: ArbitraryVect a
nil = ArbitraryVect Nil
cons :: a -> ArbitraryVect a -> ArbitraryVect a
cons x (ArbitraryVect xs) = ArbitraryVect (Cons x xs)
listToVect2 :: [a] -> ArbitraryVect a
listToVect2 = foldr cons nil
The problem with this attempt is that I have to use data
rather than newtype
(or I get A newtype constructor cannot have existential type variables
), and I need to pattern-match strictly in cons
(or I get An existential or GADT data constructor cannot be used inside a lazy (~) pattern
), so it doesn't return anything until it's through the entire list.
I don't have any other ideas for how to do this. Is this even possible? If so, how? If not, why not?