在学术计算机科学领域中,“untyped”是否也意味着“动态类型”?

179
我正在阅读一份幻灯片,上面说“JavaScript是无类型的。”这与我以前的认识相矛盾,因此我开始深入了解。
每个回答 JavaScript是否为无类型语言?的答案都说JavaScript不是无类型,并提供了各种静态、动态、强类型和弱类型的示例,而我对此非常熟悉和满意。所以这不是正确的方法。
所以我问了JavaScript的创造者Brendan Eich,他说:

学术界使用“无类型”来表示“没有静态类型”。他们聪明到足以看出值具有类型(呃!)。上下文很重要。

学术界的计算机科学专业人士是否将“无类型”用作“动态类型”的同义词(并且这是有效的)或者这里还有更深层次的问题我没有理解?我同意Brendan的看法,即上下文很重要,但如果有任何解释的引用,那将是极好的,因为我当前的“首选”书籍在这个主题上没有涉及。
我想弄清楚这一点,以便改善我的理解,因为即使维基百科也没有提到这种替代用法(至少我没有找到)。如果我错了,我不想在将来使用该术语或质疑该术语的使用时出错 :-)
(我还看到一位顶级的Smalltalker也说Smalltalk也是“无类型”的,所以这不是一个偶然事件! :-))

6
他人对术语的误用不必影响你的习惯,只需使用(更)正确的短语。显然,你需要意识到这个问题以便阅读。 - Raphael
2
我一直认为,在编程中的变量是无类型的,任何变量都可以保存任何类型的数据。变量中所存储的数据类型可以动态地改变。 - Izkata
https://en.wikipedia.org/wiki/Type_system - noobninja
1
此外(再次强调自然语言而非计算机语言),“未类型化”=“单类型化”(所有值都有一个类型)。 - philipxy
9个回答

159

是的,在学术文献中这是标准做法。要理解它,有助于知道“类型”的概念是在20世纪30年代发明的,在Lambda演算的背景下(事实上,在集合论的背景下甚至更早)。自那时以来,出现了一整个计算逻辑的分支,即“类型理论”。编程语言理论基于这些基础。在所有这些数学背景中,“类型”都具有特定且成熟的含义。

术语“动态类型”是后来才发明的——它与常见的数学用法相反。

例如,在他的标准教材类型与编程语言中,本杰明·皮尔斯使用以下“类型系统”的定义:

类型系统是一种可行的句法方法,通过分类短语计算出的值的种类,以证明某些程序行为的不存在。

他还指出:

“静态”这个词有时会被明确地添加——例如我们说“静态类型的编程语言”,以区别于在Scheme(Sussman和Steele,1975;Kelsey、Clinger和Rees,1998;Dybvig,1996)等语言中发现的动态或潜在类型,其中运行时类型标签用于区分堆中不同类型的结构。像“动态类型”的术语可以说是误称,应该用“动态检查”代替,但使用习惯已经形成。

大多数从事此领域工作的人似乎都持有这种观点。

请注意,这并不意味着“未经类型化”的和“动态类型”的是同义词。相反,后者是前者的一种(技术上具有误导性的)名称。

附:就此类型系统的学术研究者和JavaScript的非学术实现者而言,我必须应对这种分歧。 :)


5
非常有帮助,甚至可能是我最喜欢的答案之一,因为它提供了一些背景历史。 - Peter Cooper
2
@PeterCooper 顺便说一下,你可能会感兴趣知道正式语义学的一个主要分支是基于类型化λ演算的,例如蒙塔古语义学。但作为一种声明性、非生成性系统,我个人总是将像蒙塔古语义学这样的系统中的类型化与编程语言中的类型化分开处理。 - knowtheory
1
+1. "[动态类型]是[未类型化]的一个特定情况(技术上有误导性)的名称" - Steve
1
这段关于“类型”历史的表述并不完全正确。它们甚至比丘奇的λ演算更古老。罗素在集合论构建中使用类型来避免悖论。 - Sam Tobin-Hochstadt
1
@SamTobin-Hochstadt,是的,没错。我只谈到了与编程语言基础直接相关的历史部分。我会澄清一下。 - Andreas Rossberg
我不太明白为什么说“动态类型”在这里是一个错误的术语。当然,在运行时进行动态检查,但它也是动态类型的。变量x(在JS中)可以被标记为数字,然后稍后标记为字符串。为什么术语“动态类型”不能很好地描述这种行为?是因为x只是对某些东西的引用,这些东西可以是数字或字符串吗?类型检查器检查那些被引用的东西而不是x本身?而x本身没有类型?哦...我想我明白了。 - Yousif Al-Y

74

我是一名专攻编程语言的学术计算机科学家,是的,“untyped”这个词经常被(误)用于这种情况。保留该词以用于不带动态类型标签的语言(例如Forth和汇编代码)将是很好的,但这些语言很少使用,甚至更少研究。使用“untyped”比“dynamically typed”更容易。

Bob Harper喜欢说像Scheme、Javascript等语言应该被认为是只有一个类型的类型化语言:值。我倾向于这种观点,因为它使得可以使用一个类型形式主义构建一个一致的世界观。

P.S. 在纯λ演算中,唯一的“值”是正规形式下的项,而正规形式下的唯一闭项是函数。但大多数使用λ演算的科学家都会添加基本类型和常量,然后你要么包括一个静态类型系统来处理λ,要么就退回到了动态类型标签。

P.P.S. 至原帖发布者:在编程语言,尤其是类型系统方面,维基百科上的信息质量很差。不要相信它。


14
我认为计算机科学(包括学术界)存在一个更广泛的问题,即名称被使用时缺乏严谨的定义。这与数学和(大多数?)科学形成了鲜明的对比。许多争议(例如关于面向对象编程的争论)似乎源于缺乏适当的定义。非常令人恼火。 - Konrad Rudolph
2
@KonradRudolph:我想知道,缺乏正确定义所引起的争议,是否部分源于缺乏正确定义。有些术语会获得情感价值(无论是积极还是消极),然后特定语言的支持者会以包含或排除其语言(并排除或包含他们最喜欢的“敌对”语言)的方式定义这些术语。以数学为例——如果仍有人支持天真集合论而不是公理集合论,你可以肯定他们会称自己的观点为“公理集合论”,并定义“天真集合论” - ruakh
4
@Norman,我很惊讶你称其为误用--正如你所知,类型的概念比所谓的动态类型语言早了几十年,而后者所谓的“类型”与前者几乎没有关系。因此,我认为公平地说,误用是相反的。 - Andreas Rossberg
5
关于编程语言,尤其是类型系统,如果你觉得维基百科上的信息质量不高,那就别坐视不管,自己动手改善它。(只是卖萌) - Gyom
3
@Gyom 我放弃改进维基百科的那一天,是当我意识到维基百科的流程奖励的是“变化”,而不是“专业知识”时。我的时间更好地用于改进SO :-) - Norman Ramsey
显示剩余8条评论

44
我已经调查过了,发现你的问题的答案很简单,也很令人惊讶:“是的”,学术计算机科学家(至少其中一些)使用“untyped”来表示“动态类型”。例如,《编程语言:原理与实践》第三版(作者Kenneth C. Louden和Kenneth A. Lambert,2012年出版)就这样说:
“没有静态类型系统的语言通常被称为未类型化语言(或动态类型化语言)。这些语言包括Scheme和其他Lisp方言、Smalltalk以及大多数脚本语言,如Perl、Python和Ruby。但请注意,未类型化语言不一定允许程序破坏数据——这只是意味着所有安全检查都在执行时进行。[...]”
[link] (注意:原文中加粗)并继续以这种方式使用“未类型化”。
我发现这令人惊讶(与afrischke和Adam Mihalcin提供的原因差不多),但就是这样。:-)
编辑补充:您可以通过在Google书籍搜索中输入“未类型化语言”来找到更多示例。例如:

[...] 这是许多未类型化语言中的主要信息隐藏机制。例如,PLT Scheme [4] 使用生成的struct,[...]

— Jacob Matthews 和 Amal Ahmed,2008 [link]

[...] 我们为一种未类型化的函数式语言提供了一个绑定时间分析。[...] 它已经被实现并用于Scheme的无副作用方言的部分求值器。然而,该分析足够通用,可适用于非严格类型的函数式语言,如Haskell。[...]

— Charles Consel,1990 [link]
顺便提一下,我看完这些搜索结果后的印象是,如果一个研究者写了一个"未类型化"的函数式语言,他很可能认为它在与Adam Mihalcin提到的未类型化lambda演算中具有相同的"未类型化"意义。至少,有几位研究者同时提到Scheme和lambda演算。

当然,搜索没有说的是是否有研究者拒绝这种认同,并且不认为这些语言是"未类型化"的。好吧,我找到了这个:

然后我意识到,没有循环性,因为动态类型语言不是未类型化语言——只是类型通常不能从程序文本中立即明显地看出来。

——某人(我无法确定是谁),1998 [link]

但显然,大多数拒绝这种认同的人不会觉得有必要明确地这样说。


这正是我正在寻找的引用,谢谢!不过让我有点担心的是这本书在亚马逊上的平均评分只有2颗星,因此我还需要更多的引用,但这是一个很好的开端。 - Peter Cooper
@PeterCooper: 我编辑了我的答案,添加了更多的引用。它们通过来自已发表的论文绕过了亚马逊评分问题:我所知道的是,它们仍然可能是垃圾,但至少我们不必担心亚马逊告诉我们这一点。:-P - ruakh
将“未类型化”与“动态类型化”混淆是不合逻辑的。开发人员不应该是不合逻辑的。 - rotman

10

Untyped和动态类型绝对不是同义词。最常被称为“无类型”的语言是λ演算,它实际上是一种单类型语言——一切都是函数,因此我们可以静态地证明所有内容的类型都是函数。动态类型语言具有多个类型,但没有添加一种方法让编译器静态检查它们,因此强制编译器在变量类型上插入运行时检查。

JavaScript是一种动态类型语言:可以使用JavaScript编写程序,使某些变量x可以是数字、函数、字符串或其他内容(确定其类型需要解决停机问题或一些困难的数学问题),因此可以将x应用于参数,而浏览器必须在运行时检查x是否为函数。


据我所知,Lambda表达式也存在同样的问题。虽然您可以将一个函数作为“我的当前位置”传递给计算“我与目标的距离”的函数,但您也可以将相同的“我的当前位置”函数传递给计算“带维克斯的泡芙对您的鼻子更好吗”的函数。仅仅因为您可以这样做并不意味着它是有效的 - 这就像任何动态系统一样。 - Steve
5
“垃圾进入,垃圾输出”虽然适用于任何编程语言,但与类型的概念无关。即使在像OCaml或SML这样的强类型语言中,我也可以将北极传递到计算“目标距离”的函数中(不,我的当前位置不是北极)。 - Adam Mihalcin
1
只是想补充一下,对于学术界以外的人来说:像汇编这样的东西也算是无类型的。 - CoffeeTableEspresso
最初的λ演算是无类型的,因为它的形式化没有涉及到类型。虽然将该系统表述为单类型也是一种有效的观点,但最初的表述完全没有涉及到类型。 - Keith Pinson

9
两个陈述都是正确的,具体取决于你所讨论的是值还是变量。JavaScript 变量是无类型的,JavaScript 值有类型,并且变量在运行时可以涵盖任何值类型(即“动态”)。
在 JavaScript 和许多其他语言中,值而不是变量具有类型。所有变量都可以涵盖所有类型的值,可以被视为“动态类型”或“无类型” - 从变量没有/未知类型和变量可以采用任何类型的类型检查的角度来看,它们在逻辑上和实践上是等价的。当类型理论家谈论语言和类型时,他们通常是指这一点 - 变量携带类型 - 因为他们对编写类型检查器、编译器等感兴趣,这些工具处理程序文本(即变量),而不是运行中的程序在内存中的值。
相比之下,在其他语言中,比如 C 语言,变量携带类型而值则不携带类型。在像 Java 这样的语言中,变量和值都携带类型。在 C++ 中,一些值(那些带有虚函数的值)携带类型,而另一些则不携带类型。在某些语言中,甚至可能会出现值更改类型的情况,尽管这通常被认为是不良设计。

6
我认为关键区别并不是值和变量,而是值和表达式:在静态类型语言中,表达式有类型,而在动态类型语言中只有值有类型(当然,变量名也是一种表达式)。 - ruakh

6
尽管大多数探讨类型的计算机科学研究人员仅考虑具有可以语法推导出类型的语言作为类型化语言,但还有很多像我们一样使用动态/潜在类型语言的人对此表示不满。
我认为有3种语言类型:
未经分类-仅运算符确定值的解释方式-并且通常适用于任何内容。例如:汇编,BCPL
静态类型-表达式/变量与它们关联的类型,在编译时确定运算符的解释方式/有效性。例如:C、Java、C++、ML、Haskell
动态类型-值与它们关联的类型,在运行时确定运算符的解释方式/有效性。例如:LISP、Scheme、Smalltalk、Ruby、Python、Javascript
据我所知,所有动态类型语言都是类型安全的,即只有有效的运算符可以操作值。但对于静态类型的语言来说并非如此。根据所使用的类型系统的功能,某些运算符可能仅在运行时进行检查,或者根本不进行检查。例如,大多数静态类型语言不能正确处理整数溢出(将2个正整数相加可能会产生负整数),并且越界的数组引用要么根本不进行检查(C、C++),要么仅在运行时进行检查。此外,一些类型系统太弱了,使用起来不方便(C及其类别中的转换)需要逃脱口子来更改表达式的编译时类型。
所有这些都导致荒谬的说法,例如C++比Python更安全,因为它是(静态类型语言),而事实上,Python本质上是安全的,而您可以用C++射击自己的腿。

很少有语言能够静态类型安全,如果你将类型(大多数人至少将强制类型转换错误视为一种类型错误)包括在范围内,例如转换失败、下标越界或空指针异常。例如,与Java相比,Rust更加具备静态类型安全性,因为你无法拥有空指针。Java是动态类型安全的,即对于任何非法操作都会得到异常,并且每个操作都有规定(除了本机方法)。同样,Rust也是如此(不包括“不安全”代码,这些代码已经被明确定义)。 - Dave Mason
@DaveMason:未定义。在C语言中,“未指定”和“未定义”之间存在着巨大的区别。未定义行为基本上是非法的事情,可以做你所描述的事情---而未指定则基本上意味着“实现定义,实现不必记录它”(这里有一些细微差别,所以这是一个简化)。因此,调用未指定行为是完全合法的(事实上,每当一个人编写函数调用时就会这样做)。 - Tim Čas
我不同意所引用的观点,即Javascript是弱类型语言。所有值的操作都有定义。不存在核心转储的可能性。如果您想了解更多信息,我建议阅读这篇文章,它并不完美,但更加细致和全面。@TimCas 您是正确的,C99标准将未指定/未定义/实现定义分别定义了出来,但它们都属于我上面所说的“未指定”。例如编写依赖于参数评估顺序的函数调用。 - Dave Mason
@DaveMason:你把语言的安全性和类型系统的“强度”混淆了。看看你自己的链接吧,该死的。 - Tim Čas
值得注意的是,你列出的一些“静态类型”语言也满足你对“动态类型”的定义;例如,在Java中,每个对象都有Java所称的“运行时类型”,意味着它属于的特定类。因此,你真正的“动态类型”的定义似乎是你所述的定义加上语言不是“静态类型”的约束。因此,动态类型是缺乏静态类型的特例;当你从这个角度看它时,学术-CS的观点突然变得更有意义了。 - ruakh
显示剩余3条评论

4
这个问题与语义学有关。
如果我给你这个数据:12,它的类型是什么?你无法确定。可能是整数,可能是浮点数,也可能是字符串。从这个意义上说,它是非常“无类型”的数据。
如果我给你一种想象中的语言,让你在这个数据和另一个任意的数据上使用“加”、“减”和“连接”等运算符,那么“类型”就有些无关紧要了(对于我的想象语言而言)。例如:可能add(12, a)得到的结果是109,即12加上a的ASCII值。
现在来谈谈C语言。C语言几乎让你可以对任意的数据做任何你想做的事情。如果你使用一个需要两个uint参数的函数,你可以将任何你想要的东西进行强制转换并传递进去,这些值将被简单地解释为uint。从这个意义上说,C是“无类型”的(如果你以这种方式对待它的话)。
然而 - 正如Brendan所说 - 如果我告诉你“My age is 12”,那么12就有了类型 - 至少我们知道它是数字。在上下文中,无论使用什么语言,一切都有类型。
这就是为什么我一开始就说 - 你的问题涉及语义学。 “未经分类”是什么意思?当他说“没有静态类型”时,我认为Brendan说得很对 - 因为这是唯一可能的含义。人类天生会将事物分类。我们直观地知道汽车和猴子之间有根本性的不同 - 即使从未被教过如何做出这些区分。
回到我一开始的例子 - 一个“不关心类型”(本质上)的语言可能会让您在不产生语法错误的情况下“添加”“年龄”和“姓名”……但这并不意味着这是一个逻辑上合理的操作。
Javascript可能会让您做各种疯狂的事情,而不考虑它们是否为“错误”。这并不意味着你所做的事情在逻辑上是正确的。这是开发人员需要解决的问题。
一个在编译/构建/解释时不强制进行类型安全的系统/语言是“未分类”还是“动态类型”?
语义学。

编辑

我想在这里补充一些内容,因为有些人似乎被卡在了“是的,但Javascript确实有一些“类型””上。

在我的评论中,我说:

在Javascript中,我可以有我已经构建成“猴子”的对象和我已经构建成“人类”的对象,一些函数可以设计成只对“人类”操作,另一些函数只对“猴子”操作,而其他函数只对“有手臂的东西”操作。无论语言是否被告知“有手臂的东西”这样的对象类别与汇编(“未分类”)或Javascript(“动态”)有何关系都是无关紧要的。这完全是逻辑完整性的问题——唯一的错误就是在该方法中使用没有手臂的东西。

因此,如果你认为Javascript在内部具有某种“类型概念”,因此具有“动态类型”,并认为这与“未分类系统”有所不同,那么从上面的例子中可以看出,它在内部具有的任何“类型概念”都是不相关的。

为了在C#中执行相同的操作,例如,我需要一个名为ICreatureWithArms或类似名称的接口。但在JavaScript中不需要,在C或ASM中也不需要。
很明显,JavaScript是否理解“类型”是无关紧要的。

5
这个问题询问某个词语是否在某些圈子中以某种含义使用,而你的回答是解释这个词语是否具有那个含义?真的吗,我认为你已经很好地解释了OP似乎已经知道的一切,没有添加任何新内容... - ruakh
@ruakh - 我能理解你的观点。然而,OP问道:“我是否错过了更深层次的东西”,我认为,未能理解这是一个语义问题是更深层次的事情 - 所以我尽力提供任何我能提供的小贴士。 - Steve
@PeterCooper - 看看我的编辑,告诉我是否对你有所帮助(因为你在回复中说“JavaScript有类型”)。 - Steve
2
很明显,无论JavaScript是否真正理解“类型”都是无关紧要的。这并不正确。正如我在答案中暗示的那样,在任何情况下,您都不能将12视为函数。由于JavaScript确实具有函数类型(或者,根据您的观点,多个函数类型),因此这是一个重要的区别。 - Adam Mihalcin
在我之前的评论中补充一点:仅因为JavaScript没有接口或C++/Java/.NET风格的类,并不意味着它没有类型。 - Adam Mihalcin
显示剩余3条评论

3
我不是一名计算机科学家,但如果在计算机科学界(至少在科学出版物中)“未标记”真的被用作“动态类型”的同义词,那么我会感到相当惊讶,因为在我看来,这两个术语描述了不同的概念。动态类型语言具有类型概念,并在运行时强制执行类型约束(例如,在Lisp中不能将整数除以字符串而不出错),而未标记语言根本没有任何类型概念(例如汇编语言)。即使维基百科关于编程语言的文章(http://en.m.wikipedia.org/wiki/Programming_language#Typed_versus_untyped_languages)也进行了区分。
更新:可能混淆来自于某些文本中说到的“Javascript中变量未标记”(这是正确的)。但这并不意味着该语言自动成为未标记语言(这是错误的)。

1
汇编语言确实有一些非常弱的类型概念。至少在x86中,所有东西都是整数,但某些整数与其他整数的大小不同。这甚至还没有涉及到MMX、x87和其他对原始x86规范的扩展。 - Adam Mihalcin
我必须表示不同意。在Javascript中,我可以创建一些对象并将它们命名为“猴子”,另一些对象则命名为“人类”,有些函数只能用于“人类”,另一些函数只能用于“猴子”,还有一些函数只能用于“有手臂的物体”。无论这种对象类别是否被编程语言明确指定,在汇编语言(“无类型”)和Javascript(“动态类型”)中都是无关紧要的。这完全取决于逻辑的完整性——唯一的错误就是使用没有手臂的对象来调用该方法。 - Steve
@Adam Mihalcin 确实。[宏]汇编语言当然也可以有各种程度的类型概念。(例如,看看“类型化汇编语言”)。不过我仍然认为我的观点成立。 - afrischke
3
@Steve,我不确定我能跟上你的思路。OP问的是在计算机科学领域是否没有区分“动态类型”和“无类型”。这与你是否认为JavaScript与汇编如何处理内存位没有实际区别无关。根据我所了解的(我必须特意查阅资料,因为我不是JavaScript程序员),ECMAScript/Javascript标准定义了6种数据类型(布尔、字符串、未定义、数字、空值和对象),任何时刻一个值都是一个具体的类型... - afrischke
1
现在这是一个概念(大多数)汇编语言没有的(所有的都只是位和字节),所以在我看来,这标志着JavaScript和汇编之间的明显区别。 - afrischke

2
同意 Brendan 的观点 - 上下文决定一切。
我认为:
我记得在2004年左右很困惑,因为有人争辩Ruby是无类型还是动态类型。老派的C/C++人(我当时也是其中之一)在考虑编译器并说Ruby是无类型。
请记住,在C中,没有运行时类型,只有地址,如果正在执行的代码决定将位于该地址的任何东西视为其不应该是的东西,那就出了问题。这绝对是无类型的,并且与动态类型非常不同。
在那个世界里,“打字”全部都关乎编译器。C++具有“强类型”,因为编译器的检查更严格。Java和C则更加“弱类型”(甚至有争论Java是强类型还是弱类型)。在那个连续体中,动态语言被认为是“无类型”因为它们没有编译器类型检查。
今天,对于实践程序员来说,我们习惯于使用动态语言,我们显然认为无类型意味着没有编译器或解释器类型检查,这将非常难以调试。但是在那段时间里,这并不明显,而在计算机科学的更理论的世界中,这甚至可能没有意义。
某种深层意义上,没有任何东西可以是无类型的(或几乎没有),因为你必须在操作值时有一些意图才能编写有效的算法。这是理论计算机科学的世界,它不涉及特定语言的编译器或解释器的实现细节。因此,“无类型”在那种情况下(可能)是完全没有意义的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接