我试图围绕GHC扩展KindSignatures
和DataKinds
。查看Data.Modular包,我大致了解
newtype i `Mod` (n :: Nat) = Mod i deriving (Eq, Ord)
等同于声明c ++模板<typename T, int N>
(构造函数只接受一个T
类型的参数)。但是,看看GHC.TypeLits包,我不了解大部分情况。关于这个包的任何一般性解释都会有所帮助。在此问题被标记为非主题之前,以下是一些特定的子问题:
KnownNat
类是有意义的,使用必需的函数让你从类型中提取类型变量,但是natVal
做什么,proxy
类型变量是什么?someNatVal
?SomeNat
- 如何才能知道类型级别数?不是编译时 知道的类型级别编号的全部内容吗?答案 0 :(得分:3)
这个问题相当广泛 - 我只会解决几个问题。
proxy
类型变量只是种类* -> *
的类型变量,即类型构造函数的类型。实际上,如果你有一个功能
foo :: proxy a -> ...
你可以传递给它的类型值,例如Maybe Int
,选择proxy = Maybe
和a = Int
。您也可以传递[] Char
类型的值(也写为[Char]
)。或者,更常见的是Proxy Int
类型的值,其中Proxy
是定义为
data Proxy a = Proxy
即。一个数据类型,它不携带任何运行时信息(其中包含单个值!),但它带有编译时信息(幻像类型变量a
)
假设N
是一种类型Nat
- 编译时自然。我们可以写一个函数
bar :: N -> ...
但是调用它会要求我们构建类型N
的值 - 这是无关紧要的。类型N
的目的是仅承载编译时信息,其运行时值不是我们真正想要使用的东西。实际上,N
根本没有任何值,除了底部。我们可以调用
bar (undefined :: N)
但这看起来很奇怪。读到这一点,我们必须意识到bar
在第一个参数中是懒惰的,并且它不会导致尝试使用它的分歧。问题是bar :: N -> ...
类型签名具有误导性:它声称结果可能取决于类型N
的参数的值,而实际情况并非如此。相反,如果我们使用
baz :: Proxy N -> ...
意图很明确 - 只有一个运行时值:Proxy :: Proxy N
。同样清楚的是,N
值仅在编译时出现。
有时,代码不是使用特定的Proxy N
,而是略微推广到
foo :: proxy N -> ...
实现了相同的目标,但也允许不同的Proxy
类型。 (就个人而言,我对这种概括并不十分兴奋。)
回到问题:natVal
是一个将编译时自然转换为运行时值的函数。即它将Proxy N
转换为Int
,只返回常量。
如果您使用类型模板参数来模拟编译时自然,那么您与C ++模板的比较可能会更接近。 E.g。
template <typename N> struct S { using pred = N; };
struct Z {};
template <typename N> int natVal();
template <typename N> int natVal() { return 1 + natVal<typename N::pred>(); }
template <> int natVal<Z>() { return 0; }
int main() {
cout << natVal<S<S<Z>>>() << endl; // outputs 2
return 0;
}
假设S
和Z
没有公共构造函数:它们的运行时值不重要,只有它们的编译时信息很重要。