什么是类型系统?


50

背景

我正在设计一种语言,作为辅助项目。我有一个工作正常的汇编器,静态分析器和虚拟机。由于我已经可以使用构建的基础结构来编译和运行非平凡的程序,因此我考虑在大学里做一个演讲。

在我的演讲中,我提到VM提供了一种类型系统,有人问“ 您的类型系统用于什么? ”。回答后,我被问这个问题的人嘲笑。

因此,即使我几乎肯定会因提出这个问题而声名狼藉,但我还是去找程序员。

我的理解

据我了解,类型系统用于提供有关程序中实体的附加信息层,以便运行时,编译器或任何其他机器知道如何处理其所操作的位字符串。它们还有助于维护合同-编译器(或代码分析器,运行时或任何其他程序)可以验证程序在任何给定的点上以程序员期望其操作的值运行。

类型也可以用于向那些人类程序员提供信息。例如,我找到以下声明:

function sqrt(double n) -> double;

比这更有用

sqrt(n)

前者提供了大量信息:sqrt标识符是一个函数,以一个double作为输入,并产生另一个double作为输出。后者告诉您它可能是一个带有单个参数的函数。

我的答案

因此,在被问到“您的类型系统是干什么的?”之后 我回答如下:

类型系统是动态的(类型被分配给值,而不是分配给包含它们的变量),但是强大,没有令人惊讶的强制规则(您不能将字符串添加到整数,因为它们表示不兼容的类型,但是您可以将整数添加到浮点数) 。

VM使用类型系统来确保指令的操作数有效。可供程序员使用,以确保传递给其函数的参数有效(即类型正确)。
类型系统支持子类型化和多重继承(这两种功能均可供程序员使用),并且在对对象使用动态方法分配时会考虑类型-VM使用类型来检查针对给定类型实现的给定消息的功能是什么。

后续问题是“如何将类型分配给值?”。因此,我解释了所有值都装在框内,并有一个指向类型定义结构的指针,该结构提供有关类型名称,其响应的消息以及其继承的类型的信息。

在那之后,我被嘲笑了,我的回答被“那不是一个真正的类型系统”打断了。

所以-如果我所描述的不符合“真实类型系统”的条件,那会是什么?那个人对我提供的内容不能视为类型系统是正确的吗?


19
人们谈论类型系统时,通常是在谈论静态类型。对于关心类型系统的人来说,动态类型并不是很有趣,因为它几乎不能保证任何事情。例如,变量x可以保持哪种值?没事
2013年

7
我很想知道他们为捍卫/解释他们的反应不得不说些什么。
Newtopian

18
@Doval动态类型可以通过将5加到猫身上来确保您不会进入无意义的状态。当然,它不会阻止您尝试,但至少可以阻止它的实际发生,并为您提供机会找出错误所在并采取纠正措施,这是真正无类型的语言无法做到的。
8bittree '16

10
该人对您的回答“如何将类型分配给值?”表示质疑。他们想了解有关键入规则的信息,而不是方框图和指针图。笑是绝对不礼貌的。
gardenhead

10
笑着的人很可能是某种特定语言(家庭)的狂热者,拥有强大的类型系统(哈斯克尔似乎很受欢迎),并且会嘲笑任何强度不强(因而是玩具)的东西,或者更强壮(因此不切实际)的东西,或只是不同。与狂热分子进行讨论既危险又徒劳。像这样大笑无礼,以至于表明了这种更深层次的问题。您很幸运,他们没有开始宣讲……
海德

Answers:


30

所有这些似乎都很好地描述了系统提供的类型。您的实现听起来很合理。

对于某些语言,您将不需要运行时信息,因为您的语言不会执行运行时分派(或者您可以通过vtables或其他机制进行单次分派,因此不需要类型信息)。对于某些语言,仅使用符号/占位符就足够了,因为您只在乎类型相等,而不在乎其名称或继承。

根据您的环境,此人可能想要在您的类型系统中使用更多形式主义。他们想知道您可以用它证明什么,而不是程序员可以用它什么。不幸的是,这在学术界很普遍。尽管学者之所以这样做,是因为在类型系统中存在容易使事情逃避正确性的缺陷。他们有可能发现其中之一。

如果您还有其他问题,“ 类型和编程语言”是一本有关该主题的经典书籍,可以帮助您学习学者所需要的一些严格知识,以及一些有助于描述事物的术语。


3
“根据您的环境,此人可能想要在您的类型系统中使用更多形式主义。” 可能就是这样。我没有专心于使用类型系统可以证明的内容,而是将其视为一种工具。感谢您的推荐书!
Mael

1
@Mael某些类型的系统用作逻辑(请参阅逻辑框架)。因此,基本上,类型给出了公式,而程序就是这些公式的证明(例如,函数类型a -> b可以看作是一个隐含b,即,如果您给我一个类型值,那么a我就可以得到类型值b)。但是,为了保持一致,语言必须是完整的,因此非图灵完整。因此,所有现实生活中的类型系统实际上都定义了不一致的逻辑。
巴库里

20

我喜欢@Telastyn的答案,尤其是因为提到了形式主义方面的学术兴趣。

请允许我加入讨论。

什么是类型系统?

类型系统是用于定义,检测和防止非法程序状态的机制。它通过定义和应用约束来工作。约束定义是类型,约束应用是类型的用法,例如在变量声明中。

类型定义通常支持组合运算符(例如,各种形式的连接,如结构,子类,以及析取,如枚举,联合)。

约束,类型的用法有时还允许组合运算符(例如,至少要提供此条件,或者恰好可以做到这一点,只要满足其他条件,就可以)。

如果类型系统在该语言中可用并且在编译时应用于能够发出编译时错误的目标,则它是静态类型系统;这些阻止了许多非法程序的编译,更不用说运行了,因此可以防止非法程序状态。

(静态类型系统会阻止程序运行,无论是否知道(或无法确定)该程序是否会到达其抱怨的不正确代码。静态类型系统会检测到某些废话(违反声明的约束)并在运行之前判断该程序有错误。)

如果在运行时应用类型系统,则它是一种动态类型系统,可以防止程序处于非法状态:但是要在运行中停止程序,而不是首先阻止程序运行。

一个相当常见的类型系统产品是提供静态和动态功能。


我认为所谓的混合类型系统根本不常见。您想使用什么语言?
gardenhead

2
@gardenhead,向下转换的功能不是静态类型的系统功能,因此通常在运行时动态检查它。
Erik Eidt

1
@gardenhead:大多数静态类型化的语言都允许您将键入推迟到运行时,只需使用C的void *指针(非常弱),C#的动态对象或Haskell的存在量化的GADT(相对于大多数其他静态类型的值,您可以得到更强大的保证)语言)。
左右左转

没错,我忘记了“广播”。但是,铸造只是弱型系统的拐杖。
gardenhead

@gardenhead除了提供动态选项的静态语言之外,许多动态语言还提供一些静态类型。例如,Dart,Python和Hack都有基于“渐进式打字”概念执行静态分析的模式或工具。
IMSoP

14

噢,老兄,我很高兴能尽我所能回答这个问题。希望我能正确整理思路。

正如@Doval所提到的,以及发问者指出的(尽管很粗鲁),您实际上没有类型系统。您有一个使用标签进行动态检查的系统,该系统通常较弱,并且也不太有趣。

“什么是类型系统”这个问题可以从哲学上讲,我们可以在这本书上充斥不同的观点。但是,由于这是一个面向程序员的网站,因此我将尽量保持答案的实用性(实际上,尽管有人会想到,但类型在编程中非常实用)。

总览

在深入探讨更正式的基础之前,让我们先来了解一下类型系统的优点。类型系统在我们的程序上加上结构。他们告诉我们如何将各种功能和表达方式结合在一起。没有结构,程序就站不住脚,极其复杂,很容易在程序员的丝毫错误下造成伤害。

使用类型系统编写程序就像在薄荷状态下驾驶汽车-制动器起作用,车门安全关闭,发动机已上油等。没有类型系统编写程序就像骑没有头盔的摩托车,并制造轮子没有意大利面。您绝对无法控制自己。

为了使讨论更基础,我们假设我们有一种语言具有文字表达num[n]str[s]并且分别表示数字n和字符串s,plus以及concat具有预期含义的原始函数和。显然,您不希望能够编写类似plus "hello" "world"或的内容concat 2 4。但是,如何防止这种情况呢?先验地,没有方法将数字2与字符串文字“世界”区分开。我们要说的是,这些表达应在不同的上下文中使用。他们有不同的类型。

语言和类型

让我们退后一步:什么是编程语言?通常,我们可以将编程语言分为两层:语法和语义。它们也分别称为静力学动力学。事实证明,类型系统对于调解这两个部分之间的交互作用是必需的。

句法

程序就是一棵树。不要被您在计算机上写的几行文字所迷惑;这些只是程序的人类可读表示。该程序本身是一个抽象语法树。例如,在C语言中,我们可以这样写:

int square(int x) { 
    return x * x;
 }

这是程序(片段)的具体语法。树表示为:

     function square
     /     |       \
   int   int x    return
                     |
                   times
                  /    \
                 x      x

编程语言提供了一种语法定义,语言的有效树(混凝土或抽象语法可以使用)。通常使用BNF表示法完成此操作。我认为您已经针对创建的语言完成了此操作。

语义学

好的,我们知道程序是什么,但这只是一个静态树结构。大概,我们希望我们的程序实际计算一些东西。我们需要语义。

编程语言的语义学是一个广泛的研究领域。广义上讲,有两种方法:指称语义操作语义。指称语义通过将程序映射到一些基础数学结构(例如自然数,连续函数等)中来描述程序。为我们的程序提供了意义 相反,操作语义通过详细说明程序的执行方式来定义程序。在我看来,操作语义对程序员(包括我自己)更加直观,因此,让我们坚持下去。

我不会介绍如何定义形式化的操作语义(细节涉及到一点),但是基本上,我们需要如下规则:

  1. num[n] 是一个值
  2. str[s] 是一个值
  3. 如果num[n1]num[n2]评估为整数n_1$ and $n_2$, thenplus(num [n1],num [n2])`评估为整数$ n_1 + n_2 $。
  4. 如果str[s1]str[s2]评估为字符串s1和s2,则concat(str[s1], str[s2])评估为字符串s1s2。

等等,这些规则实际上更加正式,但是您可以理解要点。但是,我们很快就会遇到问题。当我们写以下内容时会发生什么:

concat(num[5], str[hello])

嗯 这是一个难题。我们尚未在任何地方定义有关如何将数字与字符串连接的规则。我们可以尝试创建这样的规则,但是我们凭直觉知道此操作是没有意义的。我们不希望该程序有效。因此,我们不可避免地被引向类型。

种类

程序是语言语法定义的树。程序由执行规则赋予含义。但是某些程序无法执行。也就是说,某些程序是没有意义的。这些程序类型错误。因此,打字可以用一种语言表征有意义的程序。如果程序类型正确,我们可以执行它。

让我们举一些例子。再次,与评估规则一样,我将非正式地提出打字规则,但是可以使它们变得严格。以下是一些规则:

  1. 形式的令牌num[n]具有类型nat
  2. 形式的令牌str[s]具有类型str
  3. 如果expression e1具有type nat且expression e2具有type nat,则表达式plus(e1, e2)具有type nat
  4. 如果expression e1具有type str且expression e2具有type str,则expression concat(e1, e2)具有type str

因此,根据这些规则,存在plus(num[5], num[2])has type nat,但是我们不能将类型分配给plus(num[5], str["hello"])。我们说一个程序(或表达式)如果可以分配任何类型,则它是正确类型的,否则它是错误类型的。如果可以执行所有类型正确的程序,则类型系统是正确的。Haskell声音不错;C不是。

结论

关于类型还有其他观点。类型在某种意义上相当于直觉逻辑,它们也可以被视为范畴论中的对象。了解这些联系很有趣,但是如果只想编写甚至设计一种编程语言就不是必需的。然而,理解类型作为控制程序结构的工具对编程语言设计和发展至关重要。我只是摸索了什么类型可以表达。我希望您认为它们值得将其纳入您的语言。


4
+1。动态打字发烧友曾经想过的最大技巧就是说服世界,如果没有类型系统,您可能拥有“类型”。:-)
ruakh

1
由于您无法自动验证任意程序感兴趣的任何内容,因此每个类型系统都必须提供强制转换运算符(或道德等效项),否则会牺牲Turing的完整性。当然,这包括Haskell
凯文

1
@凯文我很清楚赖斯定理,但它并不像您想的那样重要。首先,大多数程序不需要无限制的递归。如果我们使用仅具有原始递归的语言(例如Godel的System T)工作,则可以使用类型系统(包括暂停)来验证有趣的属性。现实世界中的大多数程序都相当简单-我想不起来上次真正需要转换的时候了。图灵完整性被高估了。
gardenhead

9
在我看来,“动态打字不是真正的打字”就像古典音乐家所说的“流行音乐不是真正的音乐”,或者福音派的人所说的“天主教徒不是真正的基督徒”。是的,静态类型系统功能强大且引人入胜且重要,而动态类型则有所不同。但是(正如其他答案所描述的那样),除了静态类型系统(传统上称为类型化)之外,还有一系列有用的东西,它们确实具有重要的共同点。为什么需要坚持我们的打字作为一种真正的打字?
Peter LeFanu Lumsdaine,

5
@IMSoP:对于比一本书还短的内容,克里斯·史密斯(Chris Smith)的文章在讨论类型系统之前要知道东西很棒,阐明了为什么动态类型与静态类型确实有很大不同。
Peter LeFanu Lumsdaine
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.