I'm trying to enforce a type-level constraint that a type-level list must be the same length as a type-level Nat being carried around. For example, using Length from singletons [1] package:
data (n ~ Length ls) => NumList (n :: Nat) (ls :: [*])
test :: Proxy (NumList 2 '[Bool, String, Int])
test = Proxy
I would not expect this code to compile, since there is a mismatch.
EDIT: As dfeuer mentioned Datatype contexts aren't a good idea. I can do the comparison at the value level, but I want to be able to do this at the type level:
class NumListLen a
sameLen :: Proxy a -> Bool
instance (KnownNat n, KnownNat (Length m)) => NumListLen (NumList n m) where
sameLen = const $ (natVal (Proxy :: Proxy n)) == (natVal (Proxy :: Proxy (Length m)))
~~~~
EDIT: Sorta answered my own question, simply add the constraint to the instance:
class NumListLen a
sameLen :: Proxy a -> Bool
instance (KnownNat n, KnownNat (Length m), n ~ Length m) => NumListLen (NumList n m) where
sameLen = const $ (natVal (Proxy :: Proxy n)) == (natVal (Proxy :: Proxy (Length m)))
/home/aistis/Projects/SingTest/SingTest/app/Main.hs:333:13:
Couldn't match type ‘3’ with ‘2’
In the second argument of ‘($)’, namely ‘sameLen test’
In a stmt of a 'do' block: print $ sameLen test
In the expression:
do { print $ sameLen test;
putStrLn "done!" }
Data.Singletons.Prelude.Eq? I don't know what to make of that, but there might well be a simpler way. - dfeuer