寻求澄清弱类型语言的明显矛盾

2022-08-31 06:42:37

我认为我理解强类型,但是每次我寻找弱类型的例子时,我最终都会找到编程语言的例子,这些语言只是自动强制/转换类型。

例如,在这篇名为“类型:强与弱,静态与动态”的文章中,Python是强类型的,因为如果您尝试执行以下操作,则会收到异常:

1 + "1"
Traceback (most recent call last):
File "", line 1, in ? 
TypeError: unsupported operand type(s) for +: 'int' and 'str'

但是,在Java和C#中可以做到这一点,我们并不认为它们只是弱类型。

爪哇岛

  int a = 10;
  String b = "b";
  String result = a + b;
  System.out.println(result);

C#

int a = 10;
string b = "b";
string c = a + b;
Console.WriteLine(c);

在另一篇名为“弱类型语言”的文章中,作者说Perl是弱类型的,仅仅是因为我可以将字符串连接到数字,反之亦然,而无需任何显式转换。

佩尔

$a=10;
$b="a";
$c=$a.$b;
print $c; #10a

所以同样的例子使Perl弱类型化,但不是Java和C#?。

哎呀,这令人困惑enter image description here

作者似乎暗示,阻止对不同类型的值应用某些操作的语言是强类型化的,相反的意思是弱类型化的。

因此,在某些时候,我感到提示相信,如果一种语言提供了大量的自动转换或类型之间的强制转换(如perl),最终可能会被认为是弱类型,而其他只提供少量转换的语言可能最终被认为是强类型。

不过,我倾向于相信,在这种探究中,我一定是错的,我只是不知道为什么或如何解释它。

所以,我的问题是:

  • 对于一种语言来说,真正弱类型化的真正含义是什么?
  • 你能提到任何与语言完成的自动转换/自动强制无关的弱类型的良好例子吗?
  • 一种语言可以同时进行弱类型和强类型化吗?

答案 1

更新:这个问题是我在2012年10月15日博客的主题。谢谢你的好问题!


一种语言被“弱类型化”到底意味着什么?

这意味着“这门语言使用了一种我觉得令人讨厌的类型系统”。相比之下,“强类型”语言是一种具有类型系统的语言,我觉得很愉快。

这些术语基本上是没有意义的,你应该避免使用它们。维基百科列出了“强类型”的十一种不同含义,其中有几个是矛盾的。这表明,在任何涉及术语“强类型”或“弱类型”的对话中,产生混淆的可能性都很高。

你真正可以肯定地说,正在讨论的“强类型”语言在类型系统中有一些额外的限制,无论是在运行时还是在编译时,讨论中“弱类型”语言所缺乏的。如果没有进一步的背景,就无法确定这种限制可能是什么。

与其使用“强类型”和“弱类型”,不如详细描述您指的是哪种类型的安全。例如,C# 是一种静态类型语言,并且在大多数情况下是一种类型安全语言和内存安全语言。C#允许违反所有这三种形式的“强”类型。强制转换运算符违反了静态类型;它对编译器说“我比你更了解这个表达式的运行时类型”。如果开发人员错了,则运行时将引发异常以保护类型安全。如果开发人员希望破坏类型安全或内存安全,他们可以通过创建“不安全”块来关闭类型安全系统来做到这一点。在不安全的块中,您可以使用指针魔术将int视为浮点数(违反类型安全)或写入您不拥有的内存。(违反内存安全。

C# 施加了在编译时和运行时检查的类型限制,因此与执行较少编译时检查或较少运行时检查的语言相比,C# 使其成为一种“强类型”语言。C# 还允许您在特殊情况下围绕这些限制执行最终运行,与不允许执行此类最终运行的语言相比,使其成为“弱类型”语言。

它到底是什么?这是不可能说的;这取决于说话者的观点和他们对各种语言特征的态度。


答案 2

正如其他人所指出的那样,术语“强类型”和“弱类型”具有许多不同的含义,因此您的问题没有单一的答案。但是,既然您在问题中特别提到了Perl,那么让我试着解释一下Perl在什么意义上是弱类型的。

关键是,在Perl中,没有“整数变量”,“浮点变量”,“字符串变量”或“布尔变量”这样的东西。事实上,就用户(通常)所能分辨的而言,甚至没有整数,浮点数,字符串或布尔:你所拥有的只是“标量”,它们同时是所有这些东西。因此,例如,您可以编写:

$foo = "123" + "456";           # $foo = 579
$bar = substr($foo, 2, 1);      # $bar = 9
$bar .= " lives";               # $bar = "9 lives"
$foo -= $bar;                   # $foo = 579 - 9 = 570

当然,正如您正确指出的那样,所有这些都可以被视为只是类型强制。但关键是,在Perl中,类型总是被强制的。事实上,用户很难分辨变量的内部“类型”可能是什么:在上面的示例的第2行,询问的值是字符串还是数字几乎毫无意义,因为就Perl而言,这些是一回事。事实上,Perl 标量甚至可能在内部同时具有字符串和数值,例如上面第 2 行之后的情况。$bar"9"9$foo

所有这一切的另一面是,由于Perl变量是非类型化的(或者更确切地说,不向用户公开其内部类型),因此运算符不能重载以对不同类型的参数执行不同的事情;你不能只是说“这个运算符将对数字执行X,对字符串执行Y”,因为运算符不能(不会)告诉其参数是哪种值。

因此,例如,Perl 具有并且需要一个数字加法运算符 () 和一个字符串串联运算符 ():如您上面所见,添加字符串 () 或连接数字 () 是完全可以的。类似地,数值比较运算符 、 、 、 和 比较其参数的数值,而字符串比较运算符 、 、 、 和 将它们作为字符串进行字典比较。所以,但是(但是,虽然)。(请注意,某些其他语言,如JavaScript,尝试适应类似Perl的弱类型,同时进行运算符重载。这通常会导致丑陋,就像失去联想性一样。+."1" + "2" == "3"1 . 2 == 12==!=<><=>=<=>eqneltgtlegecmp2 < 102 gt 10"02" lt 10"02" == 2+

(这里美中不足的是,由于历史原因,Perl 5确实有一些极端情况,比如按位逻辑运算符,其行为取决于其参数的内部表示。这些通常被认为是一个令人讨厌的设计缺陷,因为内部表示可能会因为令人惊讶的原因而改变,因此预测这些运算符在给定情况下的作用可能很棘手。

综上所述,有人可能会说Perl确实有强类型;它们只是不是你所期望的那种类型。具体来说,除了上面讨论的“标量”类型之外,Perl还有两种结构化类型:“数组”和“哈希”。这些与标量非常不同,以至于Perl变量具有不同的sigil来指示它们的类型(对于标量,对于数组,对于哈希)1。这些类型之间强制规则,因此您可以编写例如 ,但其中许多都是非常有损的:例如,将数组的长度分配给 ,而不是其内容。(此外,还有其他一些奇怪的类型,如typeglobs和I / O句柄,您通常不会看到暴露。$@%%foo = @bar$foo = @bar@bar$foo

此外,在这个漂亮的设计中,一个轻微的缺点是存在引用类型,它们是一种特殊的标量(并且可以使用运算符与普通标量区分开来)。可以将引用用作普通标量,但它们的字符串/数值并不是特别有用,如果使用正常的标量操作修改它们,它们往往会失去其特殊的引用性。此外,任何Perl变量2都可以被转换为一个类,将其转换为该类的对象;Perl中的OO类系统与上面描述的原始类型(或无类型)系统有些正交,尽管它在遵循鸭子类型范式的意义上也是“弱”的。普遍的看法是,如果你发现自己在Perl中检查了一个对象的类,那么你做错了什么。refbless


1 实际上,sigil表示被访问的值的类型,因此例如,数组中的第一个标量表示。有关更多详细信息,请参阅 perlfaq4@foo$foo[0]

Perl中的2个对象(通常)是通过对它们的引用来访问的,但实际上得到的是引用指向的(可能是匿名的)变量。然而,祝福确实是变量的属性,而不是它的价值,所以例如,将实际的祝福变量分配给另一个变量只会给你一个浅薄的,不祝福的副本。有关更多详细信息,请参阅 perlobjbless