理解`GHC.TypeLits`

时间:2015-06-01 21:24:10

标签: haskell ghc

我试图围绕GHC扩展KindSignaturesDataKinds。查看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 - 如何才能知道类型级别数?不是编译时 知道的类型级别编号的全部内容吗?

1 个答案:

答案 0 :(得分:3)

这个问题相当广泛 - 我只会解决几个问题。

proxy类型变量只是种类* -> *的类型变量,即类型构造函数的类型。实际上,如果你有一个功能

foo :: proxy a -> ...

你可以传递给它的类型值,例如Maybe Int,选择proxy = Maybea = 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;
}

假设SZ没有公共构造函数:它们的运行时值不重要,只有它们的编译时信息很重要。