您不必对列表的长度进行硬编码;相反,您可以定义以下类型:
data UList a where
UList :: Nat n => List n a -> UList a
在哪里
class Nat n where
asInt :: n -> Int
instance Nat Z where
asInt _ = 0
instance Nat n => Nat (S n) where
asInt x = 1 + asInt (pred x)
where
pred = undefined :: S n -> n
我们也有
fromList :: [a] -> UList a
fromList [] = UList Nil
fromList (x:rest) =
case fromList rest of
UList xs -> UList (Cons x xs)
此设置允许您创建在编译时长度未知的列表;您可以通过执行case
模式匹配来访问长度,以从存在包装器中提取类型,然后使用Nat
该类将类型转换为整数。
您可能想知道在编译时拥有一个您不知道其值的类型有什么好处?答案是虽然你不知道类型是什么,但你仍然可以强制执行不变量。例如,以下代码保证不会改变列表的长度:
mapList :: (a -> b) -> List n a -> List n b
如果我们使用类型族进行类型添加,比如说Add
,,那么我们可以写
concatList :: List m a -> List n a -> List (Add m n) a
它强制连接两个列表为您提供一个具有两个长度总和的新列表的不变量。