我正在尝试强制执行类型级别约束,类型级别列表的长度必须与携带的类型级别Nat的长度相同。例如,使用单身长度[1]包:
data (n ~ Length ls) => NumList (n :: Nat) (ls :: [*])
test :: Proxy (NumList 2 '[Bool, String, Int])
test = Proxy
我不希望编译这个代码,因为存在不匹配。
编辑:正如dfeuer提到的数据类型上下文不是一个好主意。我可以在值级别进行比较,但我希望能够在类型级别执行此操作: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)))
~~~~
编辑:Sorta回答了我自己的问题,只需将约束添加到实例:
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!" }
答案 0 :(得分:4)
如果这类似于一个不变量(它似乎是这样),你应该将证据存储在数据类型中:
{-# LANGUAGE PolyKinds, UndecidableInstances #-}
import GHC.TypeLits
type family Length (xs :: [k]) :: Nat where
Length '[] = 0
Length (x ': xs) = 1 + Length xs
data TList n l where
TList :: (Length xs ~ n) => TList n xs
请注意,虽然证明在类型级别仍然可用,但它在数据构造函数后面是“隐藏”的。您只需通过模式匹配即可恢复证明:
data (:~:) a b where Refl :: a :~: a
test :: TList n l -> Length l :~: n
test TList = Refl
现在,两个参数之间的不匹配是一个类型错误:
bad :: TList 3 '[Int, Bool]
bad = TList
good :: TList 2 '[Int, Bool]
good = TList
当然,这仍然会受到底价值的影响,所以
uh_oh :: TList 10 '[]
uh_oh = undefined
要避免这种情况,只需确保始终在TList
构造函数上进行模式匹配。
答案 1 :(得分:3)
一种选择可能是使用类型系列:
data Nat = Z | S Nat
type family LengthIs (n :: Nat) (xs :: [*]) :: Bool where
LengthIs 'Z '[] = 'True
LengthIs ('S n) (x ': xs) = LengthIs n xs
LengthIs n xs = 'False
test :: LengthIs ('S ('S 'Z)) '[Bool,String,Int] ~ 'True => ()
test = ()
这不会通过类型检查器;使其通过的唯一方法是使类型列表具有两个元素。我不知道Nat
在单身人士图书馆中是如何运作的,但我想你可能会做类似的事情。