在GHC.TypeLits中,someNatVal的优点是什么(我们无法用natVal完成)?

时间:2015-09-08 03:35:32

标签: haskell types numbers type-level-computation

我试图理解GHC.TypeLits,特别是someNatVal。我了解它在此博客post here中使用的方式,但如上所述,可以使用natVal实现相同的示例,例如:

isLength :: forall len a. KnownNat len => Integer -> List len a -> Bool
isLength n _ = n == natVal (Proxy :: Proxy len)

someNatVal是否有natVal无法使用{{1}}进行重写?

2 个答案:

答案 0 :(得分:4)

SomeNat是存在量化的:

data SomeNat = forall n. KnownNat n => SomeNat (Proxy n)

类似SomeNat的内容包含,#34;某些n :: Nat"。 Proxy是一个允许将n提升到类型级别以满足类型系统的单例 - 在依赖类型的语言中,我们很少需要这样的构造。我们可以使用SomeNat更明确地定义GADTs

data SomeNat where
    SomeNat :: forall n. KnownNat n => Proxy n -> SomeNat

所以SomeNat包含Nat,这是静态不知道的。

然后

someNatVal :: Integer -> Maybe SomeNat

读取" someNatVal收到IntegerMaybe返回隐藏Nat"。我们无法返回KnownNat,因为Known表示"在类型级别"已知,但我们对于任意Integer没有任何了解类型级别。

SomeNat的逆(模proxy包装和Proxy而不是someNatVal

natVal :: forall n proxy. KnownNat n => proxy n -> Integer

类似" natVal的内容会收到类型为Nat的内容,并返回Nat转换为Integer"。

存在量化的数据类型通常为used,用于包装运行时值。假设您要从Vec读取STDIN(具有静态已知长度的列表),但您将如何静态计算输入的长度?没有办法。因此,您在相应的存在量化数据类型中包装一个您已阅读的列表,因此说“我不知道长度,但是存在一个"。

然而,在许多情况下它过度杀伤。要对存在量化数据类型执行某些操作,您需要具有一般性:例如:如果您需要找到SomeVec的元素总和,则必须为任意长度的sumVec定义Vec。然后你打开SomeVec并应用sumVec,然后说"我不知道被包裹的Vec的长度,但是sumVec没有&#39}关心"。但是,您可以directly使用CPS而不是这种包装解缠。

但是,由于这种普遍性,您需要启用ImpredicativeTypes才能定义具有此类延续的列表。在这种情况下,具有存在量化数据的列表是common pattern,允许绕过ImpredicativeTypes

对于您的示例,使用正确定义的Nat,比较Nat的版本为lazier,而不是比较Integer的版本。

答案 1 :(得分:3)

someNatVal的主要用途是在运行时使用一个值,就好像它是一种在编译时不知道的类型。

我对Can I have an unknown KnownNat?的回答提供了一个非常愚蠢的例子。有一百万种更好的方法来编写一个与那个程序完全相同的程序。但它显示了someNatVal的作用。它本质上是一个从价值到类型的函数 - 存在性将该类型的范围限制在它不是静态知道的地方。