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

Edw*_*rzo 176 c# python java perl weakly-typed

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

例如,在本文中名为Typing:Strong vs. Weak,Static vs. Dynamic表示Python是强类型的,因为如果你试图:

蟒蛇

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

但是,在Java和C#中这样的事情是可能的,我们并不认为它们只是因为弱类型.

Java的

  int a = 10;
  String b = "b";
  String result = a + b;
  System.out.println(result);
Run Code Online (Sandbox Code Playgroud)

C#

int a = 10;
string b = "b";
string c = a + b;
Console.WriteLine(c);
Run Code Online (Sandbox Code Playgroud)

在另一篇名为Weakly Type Languages的文章中,作者说Perl是弱类型的,因为我可以将字符串连接到数字而反之亦然,而不进行任何显式转换.

Perl的

$a=10;
$b="a";
$c=$a.$b;
print $c; #10a
Run Code Online (Sandbox Code Playgroud)

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

哎呀,这很令人困惑 在此输入图像描述

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

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

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

所以,我的问题是:

  • 对于一种真正弱类型的语言来说,它真正意味着什么?
  • 你能否提一下与语言自动转换/自动强制无关的弱键入的好例子?
  • 一种语言可以同时弱类型和强类型吗?

Eri*_*ert 209

更新:这个问题是我的博客在2012年10月15日的主题.感谢您提出的好问题!


对于一种"弱类型"的语言来说,它真正意味着什么?

这意味着"这种语言使用了一种我觉得令人反感的类型系统".相比之下,"强类型"语言是一种具有我觉得愉快的类型系统的语言.

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

所有你能确切地说的是,正在讨论的"强类型"语言在类型系统中有一些额外的限制,无论是在运行时还是在编译时,讨论中的"弱类型"语言都缺乏.如果没有进一步的背景,就无法确定这种限制.

您应该详细描述您所说的类型安全性,而不是使用"强类型"和"弱类型".例如,C#是一种静态类型语言,大多数情况下类型安全语言和内存安全语言.C#允许违反所有这三种"强"类型.强制类型操作符违反静态类型; 它对编译器说"我比你更了解这个表达式的运行时类型".如果开发人员错了,那么运行时将抛出异常以保护类型安全.如果开发人员希望打破类型安全或内存安全,他们可以通过制作"不安全"块来关闭类型安全系统.在不安全的块中,您可以使用指针魔法将int视为浮点(违反类型安全)或写入您不拥有的内存.(违反记忆安全.)

C#强加了在编译时和运行时检查的类型限制,从而使其成为一种"强类型"语言,与编译时检查较少或运行时检查较少的语言相比.C#还允许您在特殊情况下对这些限制进行最终运行,使其成为一种"弱类型"语言,而不允许您进行此类终止运行的语言.

这到底是哪个?不可能说; 这取决于发言者的观点以及他们对各种语言特征的态度.

  • @edalorzo:它基于品味和个人观点:(1)类型理论的哪些方面是相关的,哪些是无关紧要的;(2)语言是否需要*强制*或仅仅*鼓励*类型限制.正如我所指出的,可以合理地说C#是强类型的,因为它*允许并鼓励*静态类型,并且可以合理地说它是弱类型的,因为它*允许*违反类型安全的可能性. (14认同)
  • @edalorzo:正确.例如,无类型的lambda演算大约就像你可以获得的弱类型一样.每个函数都是从函数到函数的函数; 任何数据都可以无限制地传递给任何函数,因为所有数据都是"相同的类型".无类型lambda演算中表达式的有效性仅取决于其句法形式,而不取决于将某些表达式分类为具有某些类型的语义分析. (7认同)
  • @edalorzo我应该说,虽然我同意Eric的回答,但他的第一句话就得到了+1! (6认同)
  • @edalorzo:至于集会,这也是一个意见问题.汇编语言编译器不允许您将64位双精度从堆栈移到32位寄存器中; 它允许您将32位指针从堆栈移动到64位双精度到32位寄存器.从这个意义上讲,语言是"类型安全的" - 它基于数据的类型分类对程序的合法性施加了限制.这种限制是"强"还是"弱"是一个意见问题,但它显然是一种限制. (3认同)
  • 我认为我现在看到了你的观点,一种真正弱类型的语言必须完全无类型或单一类型,这在现实生活中几乎是不可能的.因此,任何语言都具有某些类型的定义,这些定义是安全的,并且根据语言提供的用于违反或操纵其数据或数据类型的漏洞数量,您可能最终会认为它或多或少弱类型,甚至可能在只有某些情况. (2认同)
  • @Mark我会给他另外+1来预测每个人都会对这个主题提供不同的解释.这种"弱打字"似乎是一个"神话概念"或"都市传奇",每个人都看过它,但没人能证明它存在:-) (2认同)

Ilm*_*nen 64

正如其他人所指出的那样,"强类型"和"弱类型"这两个术语有很多不同的含义,因此对你的问题没有单一的答案.但是,既然您在问题中特别提到了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
Run Code Online (Sandbox Code Playgroud)

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

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

因此,例如,Perl具有并且需要数字加法运算符(+)和字符串连接运算符(.):如上所述,添加字符串("1" + "2" == "3")或连接数字(1 . 2 == 12)是完全正确的.同样,数字比较操作符==,!=,<,>,<=,>=<=>比较它们的参数的数值,而字符串比较操作符eq,ne,lt,gt,le,gecmp字典顺序比较它们为字符串.所以2 < 10,但是2 gt 10(但是"02" lt 10,同时"02" == 2).(请注意,某些其他语言,如JavaScript,尝试适应类似Perl的弱类型,同时还要进行操作符重载.这通常会导致丑陋,就像失去关联性一样+.)

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

总而言之,人们可以说Perl 确实有很强的类型; 它们不是你所期望的那种类型.具体来说,除了上面讨论的"标量"类型之外,Perl还有两种结构化类型:"数组"和"哈希".这些与标量非常不同,直到Perl变量具有不同的符号来指示它们的类型($对于标量,@对于数组,%对于散列)1.有这些类型之间的强制规则,这样你就可以写例如%foo = @bar,但其中不少是相当有损耗:例如,$foo = @bar分配长度的数组 @bar$foo,而不是其内容.(另外,还有一些其他奇怪的类型,比如typeglobs和I/O句柄,你经常看不到它们.)

此外,这个漂亮设计的一个微小的缺点是引用类型的存在,它是一种特殊的标量(并且可以使用ref运算符区分普通标量).可以将引用用作普通标量,但它们的字符串/数值不是特别有用,如果使用常规标量操作修改它们,它们往往会丢失它们的特殊引用.此外,任何Perl变量2都可以bless编辑为一个类,将其转换为该类的对象; Perl中的OO类系统与上述的原始类型(或无类型)系统有些正交,尽管在遵循鸭子打字范例的意义上它也是"弱"的.一般的观点是,如果您发现自己在Perl中检查对象的类,那么您做错了.


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

2 Perl中的对象(通常)通过对它们的引用来访问,但实际获得的bless是引用所指向的(可能是匿名的)变量.然而,祝福确实是变量的属性,而不是它的值,所以例如,将实际的祝福变量分配给另一个只是给你一个浅的,未经过复制的变量.有关详细信息,请参阅perlobj.

  • +1关于perl的精彩讲座.我希望我能不止一次地向你投票.你对perl弱键入的解释是我读过的最好的. (11认同)

Kon*_*lph 19

除了Eric所说的,还要考虑以下C代码:

void f(void* x);

f(42);
f("hello");
Run Code Online (Sandbox Code Playgroud)

与Python,C#,Java或诸如此类的语言相比,上面的类型很弱,因为我们丢失了类型信息.Eric正确地指出,在C#中,我们可以通过强制转换来绕过编译器,有效地告诉它"我知道这个变量的类型比你更多".

但即便如此,运行时仍将检查类型!如果强制转换无效,运行时系统将捕获它并抛出异常.

对于类型擦除,这不会发生 - 类型信息被丢弃.强制转换为void*用C正是这么做的.在这方面,上述内容与C#方法声明根本不同,例如void f(Object x).

(从技术上讲,C#还允许通过不安全的代码或编组进行类型擦除.)

就是它的弱类型.其他的一切只是一个静态与动态类型检查的事情,即时间,当一个类型被选中.


Sau*_*ack 14

一个完美的例子来自维基百科强力打字文章:

通常,强类型意味着编程语言对允许发生的混合施加严格的限制.

打字薄弱

a = 2
b = "2"

concatenate(a, b) # returns "22"
add(a, b) # returns 4
Run Code Online (Sandbox Code Playgroud)

强大的打字

a = 2
b = "2"

concatenate(a, b) # Type Error
add(a, b) # Type Error
concatenate(str(a), b) #Returns "22"
add(a, int(b)) # Returns 4
Run Code Online (Sandbox Code Playgroud)

请注意,弱类型语言可以混合使用不同类型而不会出错.强类型语言要求输入类型为预期类型.在强类型语言中,可以转换类型(str(a)将整数转换为字符串)或转换(int(b)).

这一切都取决于打字的解释.

  • 真正.我想你可以说有不同程度的强打字和弱打字.隐式转换可能意味着语言的输入强度低于不进行隐式转换的语言. (3认同)
  • 但这导致了问题中提供的矛盾的例子.强类型语言可能包括隐式强制,这意味着您的两个"类型错误"示例中的任何一个(或两个)都会自动转换为后两个示例的相关内容,但通常该语言仍然是强类型的. (2认同)