可以使用 Haskell 列表中的单例 Nats(如 0、1、...)制作一个大小列表吗?

Can one make a sized list using singleton Nats (like 0, 1, ...) from Lists in Haskell?

在学习单例和(半)依赖类型时,我开始尝试从一个正常的 Haskell 列表中创建一个大小列表,其中大小由 Nats 给出,例如 0, 1, 2, 3,.. . 而不是 Z、S Z、S (S Z) 等。顺便说一句,我正在使用 Data.Singletons.TypeLits 库。 (singletons-2.5.1).

这是我一直在尝试的方法:

data NatList (a :: *) (n :: Nat) where
    Nah :: NatList a 0
    Yea :: a -> NatList a n -> NatList a (n + 1)

fromList :: SNat n -> [a] -> NatList a n
fromList s []     = Nah
fromList s (x:xs) = Yea x (fromList (s %- (SNat :: SNat 1)) xs)

此代码无法编译并给我一个错误

“无法将类型‘n’与‘0’匹配 ‘n’ 是一个严格的类型变量,由 类型签名: fromList::forall(n::Nat) a. SNat n -> [a] -> NatList a n

我不知道那个库,但据我了解,问题似乎出在 fromList 的第一个子句中,它应该只在 n=0 时匹配。

--...
fromList 0 []     = Nah
--...

你对 fromList 的签名说它适用于任何类型 n :: Nat。但是第一个方程 returns Nah,它要求 n 类型为 0.

所以 fromList 需要是一个 class 的方法,它可以在 n.

的类型上发送

但是您正试图将 value 的第二个参数发送给 fromList——也就是说,列表是否为空——a依赖型。 Singletons library/technique 是在 Haskell 中伪造依赖类型。这就是我的知识耗尽的地方。所以我将 Peano Nats S Z 与实例一起使用,而不是与 GADT 一起使用。

离开这个问题一段时间后,我回来找到了一个适合我的解决方案:对大小列表类型使用存在包装器并转换运行时列表

data SizedList (a :: *) (n :: Nat) where
    Nil  :: SizedList a 0
    Cons :: a -> SizedList a n -> SizedList a (n+1)

data SomeSL (a :: *) where
    MkSomeSL :: KnownNat n => SNat n -> SizedList a n -> SomeSL a  

toList :: SizedList a n -> [a]
toList Nil         = []
toList (Cons x xs) = x : toList xs

fromList :: [a] -> SomeSL a
fromList []     = MkSomeSL (SNat @0) Nil 
fromList (x:xs) = case (fromList xs) of 
    MkSomeSL n ys -> MkSomeSL (n %+ SNat @1) (Cons x ys)

toList_ :: SomeSL a -> [a]
toList_ (MkSomeSL _ xs) = toList xs

在这里,SomeSL 将我的问题中的 SizedList 类型与单例 nat 一起包装起来,允许函数通过 SNat n 访问长度。

Justin Le 关于单例的系列博文很有帮助:https://blog.jle.im/entry/introduction-to-singletons-1.html