As part of my journey in understanding singletons
I have tried to bridge the gap between compile time safety, and lifting runtime values into that dependent typ
You can do this with an existentially quantified type variable, as in @Alec’s answer, or equivalently by rewriting in continuation-passing style. The trick is to give fromList
a continuation (function) that’s polymorphic in the size of the Vec
; then, within the continuation, you have access to a type variable representing the size:
data Vec n a where
Nil :: Vec Z a
Cons :: a -> Vec n a -> Vec (S n) a
deriving instance (Show a) => Show (Vec n a)
fromList :: [a] -> (forall n. Vec n a -> r) -> r
fromList [] k = k Nil
fromList (x : xs) k = fromList xs $ \ xs' -> k (Cons x xs')
-- fromList [1, 2, 3] show == "Cons 1 (Cons 2 (Cons 3 Nil))"
You can’t know the actual value of n
, because it’s not available at compile time.
If you replace your Nat
with the one from GHC.TypeLits
, I think you can get a KnownNat
constraint for n
by creating a SomeNat
from the runtime length using fromJust (someNatVal (fromIntegral (length xs)))
, then get back the actual length value at runtime with natVal
. I’m not really familiar with how to do it, and it might require the ghc-typelits-natnormalise plugin, but it might be a starting point.