您可以使用DataKinds 做到这一点。不过,这可能过于复杂:
{-# LANGUAGE DataKinds, KindSignatures, GADTs #-}
-- requires 7.4.1, I think
data Nat = S Nat | Z
infixr 0 :.
data R (n :: Nat) where
Nil :: R Z -- like []
(:.) :: Bool -> R n -> R (S n) -- and (:)
data T (n :: Nat) = T [R n]
-- OK
test1 = T [(True :. True :. Nil), (True :. False :. Nil)]
-- will fail
test2 = T [(True :. True :. Nil), (False :. Nil)]
我宁愿推荐@MathematicalOrchids 使用智能构造函数的替代方法。
编辑:DataKinds 做什么。
DataKinds 扩展允许编译器为每个写入的数据类型自动创建一个新类型,而不是 *,以及来自构造函数的这种类型的新类型。
所以Nat,除了是一个简单的ADT,还产生了一种Nat,以及类型构造函数Z :: Nat和S :: Nat -> Nat。这个S 与Maybe :: * -> * 相当——它只是不使用所有类型的类型,而是你的新类型Nat,只包含自然数的表示。
重点是,现在您还可以定义 mixed 种类的类型构造函数。这方面的经典例子是Vec:
data Vec (n :: Nat) (a :: *) where {-...-}
有一种Vec :: Nat -> * -> *。类似地,T 具有类型 T :: Nat -> *。这让您可以将它与类型编码的常量长度一起使用,如果将两行不同长度的行放在一起,则会导致类型错误。
虽然这看起来非常强大,但实际上是受到限制的。要从这种表示中获取所有内容,应该使用依赖类型的语言,例如 Agda。