为什么类型在现代编程语言中紧随变量名之后?


57

为什么在几乎所有现代编程语言(Go,Rust,Kotlin,Swift,Scala,Nim甚至是Python的最新版本)中,类型总是排在变量声明中的变量名之后,而不是在变量名之后?

为什么x: int = 42int x = 42呢?
后者不比前者更具可读性吗?
这只是趋势,还是该解决方案背后有任何真正有意义的原因?



3
是的,这是一个骗子。尽管该问题专门针对Kotlin语言,但该问题(及其答案)适用于所有具有这种习惯的语言。
罗伯特·哈维


2
抱歉,当我在Google上搜索时确实看到了有关Kotlin的问题,但我再次提出问题是因为没有得到诸如“因为Kotlin(Go,Rust等)开发团队这样决定”的答案。我对一个更常见的答案很感兴趣:为什么它会流行起来?
安德烈·波利坎宁

2
Hihi,因此,Delphi(是Pascal对象),自成立以来就变量名后使用类型,这种方式在上个世纪的黑暗时代可以追溯到很现代;)顺便说一句,可读性在很大程度上受到您的影响过去。来自Delphi的C#,其类型在var名称之前,让我th了很长时间。
Marjan Venema

Answers:


64

您提到的所有语言都支持类型推断,这意味着这些类型是这些语言中声明的可选部分,因为当您提供具有易于确定的类型的初始化表达式时,它们足够聪明,可以将其自己填充。

这很重要,因为将表达式的可选部分放在最右边可以减少解析的歧义,并可以提高使用该部分的表达式和不使用该部分的表达式之间的一致性。当您知道var关键字和变量名称都是必需的,然后再获得可选内容时,解析声明就更简单了。从理论上讲,所有这些使计算机更易于解析的事物也提高人类的整体可读性,但这值得商de。

当你考虑所有的可选类型修饰符,像C ++“非现代”语言都有,比如这种说法得到特别强*的三分球,&为引用constvolatile等等。一旦为多个声明输入逗号,就会开始出现一些非常奇怪的歧义,例如int* a, b;不创建b指针。

甚至C ++现在都以的形式支持“正确的类型”声明auto x = int{ 4 };它确实具有一些优势


2
+1表示存在类似var这样的强制性关键字,这些关键字可以简化大多数解析过程。
8bittree '16

2
var关键字的存在是否违反名称优先表示法的目的?我没有看到书面的优势var userInput: String = getUserInput()String userInput = getUserInput()。优点只有在userInput = getUserInput()允许的情况下才有意义,但这将隐含声明作用域变量,我觉得这很可恶。
kdb

2
@kdb在C#和C ++之类的语言中可能是var userInput = getUserInput()or auto userInput = getUserInput(),编译器知道getUserInput()返回值,String因此您不必使用type deduction关键字来指定它。userInput = getUserInput()当然是在声明之前使用。
Wes Toleman

32

为什么在几乎所有现代编程语言(Go,Rust,Kotlin,Swift,Scala,Nim甚至是Python的最新版本)中,类型总是排在变量声明之后,而不是在变量声明之后?

您的前提在两个方面存在缺陷:

  • 有一些新的编程语言,其类型标识符之前。例如C,D或Ceylon。
  • 在标识符后再加上类型并不是新现象,它至少可以追溯到Pascal(设计于1968–1969,于1970年发布),但实际上已用于数学类型论中,始于〜1902。它也用于ML(1973),CLU(1974),Hope(1970s),Modula-2(1977-1985),Ada(1980),Miranda(1985),Caml(1985),Eiffel(1985),Oberon (1986),Modula-3(1986-1988)和Haskell(1989)。

Pascal,ML,CLU,Modula-2和Miranda都是非常有影响力的语言,因此这种类型的声明仍然流行起来也就不足为奇了。

为什么x: int = 42int x = 42呢?后者不比前者更具可读性吗?

可读性是一个熟悉的问题。就我个人而言,我发现中文是不可读的,但显然中国人不会。在学校学习过Pascal后,与Eiffel,F♯,Haskell和Scala涉猎,并看过TypeScript,Fortress,Go,Rust,Kotlin,Idris,Frege,Agda,ML,Ocaml等……,对我来说,这看起来很自然。

这种解决方案仅仅是趋势吗?还是有任何真正有意义的原因?

如果是趋势,那么它会持续存在:正如我提到的,它可以追溯到一百年前的数学。

在标识符后加上类型的主要优点之一是,如果要推断类型,则很容易将其省略。如果您的声明如下所示:

val i: Int = 10

然后忽略类型并按如下方式推断是很简单的:

val i = 10

而如果类型在标识符之前是这样的:

Int i = 10

然后,解析器很难将表达式与声明区分开来:

i = 10

语言设计人员通常会想到的解决方案是引入一个“我不想写一个类型”关键字,该关键字必须代替类型:

var  i = 10; // C♯
auto i = 10; // C++

但这实际上并没有多大意义:基本上,您必须显式编写一个类型,表明您没有编写类型。??省略它会更容易和更明智,但这会使语法更加复杂。

(而且,我们什至不谈论C语言中的函数指针类型。)

前面提到的几种语言的设计者都考虑了这个问题:

  • Go常见问题解答(另请参阅:Go的声明语法):

    为什么声明向后?

    如果您习惯使用C,它们只会倒退。在C中,概念是声明变量就像表示其类型的表达式一样,这是一个不错的主意,但是类型和表达式语法混合得并不好,并且结果可能令人困惑;考虑函数指针。Go大多将表达式和类型语法分开,并简化了事情(使用前缀*作为指针是证明规则的例外)。在C中,声明

    int* a, b;
    

    声明a是一个指针,但不是b;在围棋

    var a, b *int
    

    声明两个都是指针。这更清晰,更规则。此外,:=短申报表认为,一个完整的变量声明应呈现相同的顺序:=,从而

    var a uint64 = 1
    

    与...具有相同的效果

    a := uint64(1)
    

    通过对类型具有独特的语法(不仅是表达语法),还可以简化解析。之类的关键字,funcchan保持清晰。

  • Kotlin常见问题解答

    为什么在右边有类型声明?

    我们相信它使代码更具可读性。此外,它还启用了一些不错的语法功能。例如,很容易省略类型注释。Scala也已经很好地证明了这不是问题。

  • 在Scala中编程

    与Java的主要区别在于类型注释的语法- 在Java中是“ variable: Type”而不是“ Type variable”。Scala的后缀类型语法类似于Pascal,Modula-2或Eiffel。产生这种偏差的主要原因与类型推断有关,后者通常使您可以忽略变量的类型或方法的返回类型。使用“ variable: Type”语法很容易-只需省略冒号和类型。但是,使用C样式的“ Type variable”语法,您不能简单地放弃类型-将不再有标记可以开始定义。您需要一些替代关键字作为缺失类型的占位符(C♯3.0,它会进行某种类型推断,var用于此目的)。与Scala的方法相比,这种替代关键字感觉更特别,规则性更差。

注意:Ceylon的设计人员还记录了为什么他们使用前缀类型语法

前缀而不是后缀类型注释

为什么要遵循C和Java将类型注释放在首位,而不是将Pascal和ML放在声明名称后?

因为我们认为:

shared Float e = ....
shared Float log(Float b, Float x) { ... }

比这更容易阅读:

shared value e: Float = .... 
shared function log(b: Float, x: Float): Float { ... }

而且我们根本不了解任何人怎么可能以其他方式思考!

就我个人而言,我发现他们的“论据”比其他人缺乏说服力。


4
似乎离开了类型的能力,仍然有简单的解析是通过添加授权varautolet,或类似的,而不是由变量的类型声明是在哪一方。var Int x = 5甚至Int var x = 5都可以缩短为var x = 5
8bittree '16

5
当我习惯了它并且可读性强并且可选的type参数很强时,便喜欢它,但我想指出IDE不能根据类型自动提议变量名。我写TypeScript时想念它。
皮埃尔·亨利

"Your premise is flawed on two fronts" 所有这些都是比C#或D.更新
挪威

3
“但这实际上没有多大意义:您基本上必须显式地编写一个类型,表明您没有编写类型。” 好吧,正确的版本与您的示例完全相同...我也不同意它没有任何意义。它与funcGo中的意义完全一样。
Sopel

4

Pascal也是这样做的,它不是一种新语言。这是一门学术语言,从头开始设计。

我会说从变量名开始在语义上更加清晰。类型只是技术细节。如果您想像现实模型一样阅读类,则将实体的名称放在首位并将其技术实现放在最后是有意义的。

C#和Java源于C,因此他们必须尊重“现有技术”,以免混淆有经验的程序员。


3
艾达(Ada)也是这样做的,但是艾达(Ada)无疑不是一种“学术语言”。
John R. Strohm

阿达这样做是因为它的Pascal和Modula 2.重那儿剽窃
戈特机器人

2
@StevenBurnap,快速搜索显示,Wirth的第一本有关Modula-2的书于1982年中期问世。Ada在此之前几年就在开发中(我记得,DoD1在1977年左右),并在1983年被标准化为ANSI标准和MIL-STD。提交Ada候选人的所有四个团队都采用了PASCAL。作为他们的出发点。(国防部邀请贝尔实验室提交基于C的候选语言。他们持异议,说C当时还没有,并且对于DoD关键任务软件来说永远不够强大。)
John R. Strohm

我一定记错了。我以为Wirth参与了Ada。
Gort Robot

Pascal最初是一种学术语言,但到90年代末,它已成为通过Delphi编写Windows应用程序语言的边缘,直到Borland以高昂的价格跃跃欲试,并为Java和C#敞开了大门。来偷奖。不过,您仍然会在旧版Windows应用程序世界中看到一吨Delphi。
谢恩

1

为什么x: int = 42int x = 42呢?后者不比前者更具可读性吗?

在这样一个简单的示例中,并没有太大的区别,但是让我们稍微复杂一点: int* a, b;

这是C语言中的实际有效声明,但它并没有实现直观上应有的功能。看起来我们在声明两个类型的变量int*,但实际上我们在声明一个int*和一个int

如果您的语言在声明中将类型放在变量名之后,则该问题将无法解决。


4
我不确定为什么之后移动类型声明会影响这一点。是x, y *int;两个*int还是一个*int和一个int?制造int**int一个令牌而不是两个会解决这个问题,或使用类似于额外语法元素:,其可以在任一方向工作。
8bittree '16

@ 8bittree我所熟悉的每种使用名称优先声明的语言在名称和类型之间都使用一个附加标记,例如:as
梅森惠勒


2
在Go中,x, y *int意思是“声明类型为pointer-to-int的两个变量x和y”。
Vatine

10
C#使用前缀语法,但是将其int[] x, y视为两个数组。只是愚蠢的C语法将那些修饰符当作变量的一元运算符(以及前缀和后缀的混合,不少)引起问题。
CodesInChaos
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.