【问题标题】:Confused on DataKinds extension对 DataKinds 扩展感到困惑
【发布时间】:2018-12-17 09:52:48
【问题描述】:

我从 Basic Type Level Programming in Haskell 学习 Haskell 的类型编程,但是当它引入 DataKinds 扩展时,从示例中似乎有些混乱:

{-# LANGUAGE DataKinds #-}
data Nat = Zero | Succ Nat

现在,Nat 升级为Kind,就可以了。但是ZeroSucc 呢?

我尝试从 GHCi 获取一些信息,所以我输入:

:kind Zero

它给了

Zero :: Nat

没关系,Zero 是一种类型有一种Nat,对吧?我尝试:

:type Zero

它仍然给出:

Zero :: Nat

这意味着ZeroNat 类型,这是不可能的,因为Nat 是一种非类型,对吧? Nat 既是类型又是种类??

另外一个令人困惑的是,上面的博客还提到,在创建Nat 种类时,有两种新的类型:'Zero'Succ 是自动创建的。当我再次从 GHCi 尝试时:

:kind 'Zero

给予

'Zero :: Nat

:type 'Zero

给予

 Syntax error on 'Zero

好的,证明'Zero是一个类型。但是创建'Zero和'Succ'的目的是什么??

【问题讨论】:

    标签: haskell types data-kinds


    【解决方案1】:

    在未扩展的 Haskell 中,声明

    data A = B
    

    定义两个新实体,在计算和类型级别各一个:

    1. 在类型级别,一个名为A(种类为*)的新基类型,以及
    2. 在计算级别,一个名为 BA 类型)的新基础计算。

    当你打开DataKinds时,声明

    data A = B
    

    现在定义了四个新实体,一个在计算级别,两个在类型级别,一个在种类级别:

    1. 在种类级别,新的基本种类A
    2. 在类型级别,一个新的基本类型'B(类型为A),
    3. 在类型级别,新的基本类型 A(类型为 *),以及
    4. 在计算级别,新的基础计算 BA 类型)。

    这是我们之前的严格超集:旧的 (1) 现在是 (3),旧的 (2) 现在是 (4)。

    这些新实体解释了您描述的以下交互:

    :type Zero
    Zero :: Nat
    :kind 'Zero
    'Zero :: Nat
    :type 'Zero
    Syntax error on 'Zero
    

    我认为它是如何解释前两个的很清楚。它解释了最后一个,因为'Zero 是一个类型级别的东西——你不能要求类型的类型,只能要求计算的类型!

    现在,在 Haskell 中,每个出现名称的地方,从周围的语法中可以清楚地看出该名称是计算级名称、类型级名称还是种类级名称。出于这个原因,必须在类型级别包含 'B 中的刻度线有点烦人——毕竟,编译器知道我们处于类型级别,因此不能指的是未提升的计算级别B。因此,为方便起见,您可以在明确的情况下不打勾。

    从现在开始,我将区分“后端”——只有上面描述的四个实体,它们总是明确的——和“表面语法”,即你在文件中键入的内容并传递给包含歧义但更方便的 GHC。使用这个术语,在表面语法中,人们可以写出以下内容,具有以下含义:

    Surface syntax    Level           Back end
    Name              computation     Name
    Name              type            Name if that exists; 'Name otherwise
    'Name             type            'Name
    Name              kind            Name
    ---- all other combinations ----  error
    

    这解释了您的第一次互动(也是上面唯一没有解释的互动):

    :kind Zero
    Zero :: Nat
    

    因为:kind 必须应用于类型级别的事物,编译器知道表面语法名称Zero 必须是类型级别的事物。由于没有类型级别的后端名称Zero,它会尝试'Zero,并获得成功。

    怎么会模棱两可呢?好吧,请注意上面我们用一个声明在类型级别定义了 两个 新实体。为简单起见,我将等式左侧和右侧的新实体命名为不同的事物。但是让我们看看如果我们稍微调整一下声明会发生什么:

    data A = A
    

    我们仍然引入了四个新的后端实体:

    1. 亲切的A,
    2. 输入'A(类似A),
    3. 输入A(类型为*),然后
    4. 计算AA 类型)。

    哎呀!现在在类型级别有 'AA。如果您在表面语法中去掉刻度线,它将使用 (3),而不是 (2) - 您可以使用表面语法 'A 显式选择 (2)。

    更重要的是,这不必在一个声明中全部发生。一个声明可能会产生打勾版本,而另一个声明可能会产生未打勾版本;例如

    data A = B
    data C = A
    

    将从第一个声明中引入类型级后端名称 A,并从第二个声明中引入类型级后端名称 'A

    【讨论】:

    • 谢谢你的回答,这里的棘手是编译器在类型级别尝试:kind 'Zero 而不是:kind Zero?任何时候名称Zero 出现在类型级别,编译器将其替换为'Zero
    • @JoeChoi 不,不是任何时候,正如我在回答中解释的那样:编译器首先检查Zero 是否存在,只有不存在时才会检查是否'Zero 存在。
    • @JoeChoi 我已经为这个问题添加了一些精确度,区分了名称总是明确的后端和我们用来与编译器通信的表面语法。请特别参阅以“Surface syntax Level Back end”开头的新 ASCII-art 表。在此术语中,表面语法Zero 可能对应于后端Zero'Zero,具体取决于范围内的声明。
    • 非常感谢!很清楚,现在我终于明白为什么在 leftaroundabout 的答案中添加 data Zero 声明会导致名称冲突,如果不使用 'Zeor 而不是 Zero
    【解决方案2】:

    {-# LANGUAGE DataKinds #-} 不会改变 data 关键字通常所做的事情:它仍然创建一个类型 Nat 和两个值构造函数 ZeroSucc。这个扩展的作用是,它允许你像使用种类一样使用类型,像使用类型一样使用值。

    因此,如果您在类型级表达式中使用Nat,它只会将其用作无聊的 Haskell98 类型。只有在明确的 kind 级表达式中使用它时,才会得到 kind 版本。

    这种自动提升有时会导致名称冲突,我认为这就是 ' 语法的原因:

    {-# LANGUAGE DataKinds #-}
    data Nat = Zero | Succ Nat
    data Zero
    

    现在,Zero 本身就是普通(空)数据类型Zero :: *,所以

    *Main> :k Zero
    Zero :: *
    

    原则上,感谢DataKindsZero 现在也是从值构造函数Zero :: Nat 提升的类型,但这被data Zero 遮蔽了。因此,引用语法总是引用提升的类型,而不是直接定义的类型:

    *Main> :k 'Zero
    'Zero :: Nat
    

    【讨论】:

    • 冲突在(a,b) :: *'(a,b) :: (*, *) 中也特别明显。 [a] :: *'[a] :: [*] 同上(即使这些是特殊的句法情况)。
    • @leftaroundabout 感谢您的回答,这是否意味着当上下文需要 Nat 作为类型时,Nat 是类型,ZeroSucc 是构造函数。而当上下文需要Nat作为一种类型,Nat成为一种类型,ZeroSucc成为一种类型?否则,我仍然不明白为什么我们需要'Zero。您在回答中提到的冲突会在什么情况下发生?
    • @JoeChoi 是的,你断言的一切都是正确的。但是你的问题让我很困惑;答案不是已经给出了可能发生名称冲突的明确示例吗?
    • @DanielWagner 我不明白答案中的示例为什么名称冲突。
    • @JoeChoi 发生冲突是因为我同时定义了一个名为 Zero 的数据类型和一个名为 Zero 的值构造函数。在正常的 Haskell2010 中,这些不会发生冲突,因为类型级语言和值级语言是完全分开的,但是 -XDataKinds 将值级 Zero “复制”到类型级语言中,所以 Zero 在类型级上下文现在是模棱两可的。为了防止 DataKinds 破坏任何现有代码,他们将其默认为 Haskell2010 含义,即 Zero :: *。因此,为了获得 promoted-to-type value-level constructor 的新含义,我们需要 ' 语法。
    猜你喜欢
    • 1970-01-01
    • 2013-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-06
    相关资源
    最近更新 更多