为什么在几乎所有现代编程语言(Go,Rust,Kotlin,Swift,Scala,Nim甚至是Python的最新版本)中,类型总是排在变量声明中的变量名之后,而不是在变量名之后?
为什么x: int = 42
不int x = 42
呢?
后者不比前者更具可读性吗?
这只是趋势,还是该解决方案背后有任何真正有意义的原因?
为什么在几乎所有现代编程语言(Go,Rust,Kotlin,Swift,Scala,Nim甚至是Python的最新版本)中,类型总是排在变量声明中的变量名之后,而不是在变量名之后?
为什么x: int = 42
不int x = 42
呢?
后者不比前者更具可读性吗?
这只是趋势,还是该解决方案背后有任何真正有意义的原因?
Answers:
您提到的所有语言都支持类型推断,这意味着这些类型是这些语言中声明的可选部分,因为当您提供具有易于确定的类型的初始化表达式时,它们足够聪明,可以将其自己填充。
这很重要,因为将表达式的可选部分放在最右边可以减少解析的歧义,并可以提高使用该部分的表达式和不使用该部分的表达式之间的一致性。当您知道var
关键字和变量名称都是必需的,然后再获得可选内容时,解析声明就更简单了。从理论上讲,所有这些使计算机更易于解析的事物也应提高人类的整体可读性,但这值得商de。
当你考虑所有的可选类型修饰符,像C ++“非现代”语言都有,比如这种说法得到特别强*
的三分球,&
为引用const
,volatile
等等。一旦为多个声明输入逗号,就会开始出现一些非常奇怪的歧义,例如int* a, b;
不创建b
指针。
甚至C ++现在都以的形式支持“正确的类型”声明auto x = int{ 4 };
,它确实具有一些优势。
var
这样的强制性关键字,这些关键字可以简化大多数解析过程。
var
关键字的存在是否违反名称优先表示法的目的?我没有看到书面的优势var userInput: String = getUserInput()
了String userInput = getUserInput()
。优点只有在userInput = getUserInput()
允许的情况下才有意义,但这将隐含声明作用域变量,我觉得这很可恶。
var userInput = getUserInput()
or auto userInput = getUserInput()
,编译器知道getUserInput()
返回值,String
因此您不必使用type deduction关键字来指定它。userInput = getUserInput()
当然是在声明之前使用。
为什么在几乎所有现代编程语言(Go,Rust,Kotlin,Swift,Scala,Nim甚至是Python的最新版本)中,类型总是排在变量声明之后,而不是在变量声明之后?
您的前提在两个方面存在缺陷:
Pascal,ML,CLU,Modula-2和Miranda都是非常有影响力的语言,因此这种类型的声明仍然流行起来也就不足为奇了。
为什么
x: int = 42
不int 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语言中的函数指针类型。)
前面提到的几种语言的设计者都考虑了这个问题:
为什么声明向后?
如果您习惯使用C,它们只会倒退。在C中,概念是声明变量就像表示其类型的表达式一样,这是一个不错的主意,但是类型和表达式语法混合得并不好,并且结果可能令人困惑;考虑函数指针。Go大多将表达式和类型语法分开,并简化了事情(使用前缀
*
作为指针是证明规则的例外)。在C中,声明int* a, b;
声明
a
是一个指针,但不是b
;在围棋var a, b *int
声明两个都是指针。这更清晰,更规则。此外,
:=
短申报表认为,一个完整的变量声明应呈现相同的顺序:=
,从而var a uint64 = 1
与...具有相同的效果
a := uint64(1)
通过对类型具有独特的语法(不仅是表达语法),还可以简化解析。之类的关键字,
func
并chan
保持清晰。
为什么在右边有类型声明?
我们相信它使代码更具可读性。此外,它还启用了一些不错的语法功能。例如,很容易省略类型注释。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 { ... }
而且我们根本不了解任何人怎么可能以其他方式思考!
就我个人而言,我发现他们的“论据”比其他人缺乏说服力。
var
,auto
,let
,或类似的,而不是由变量的类型声明是在哪一方。var Int x = 5
甚至Int var x = 5
都可以缩短为var x = 5
。
"Your premise is flawed on two fronts"
所有这些都是比C#或D.更新
func
Go中的意义完全一样。
Pascal也是这样做的,它不是一种新语言。这是一门学术语言,从头开始设计。
我会说从变量名开始在语义上更加清晰。类型只是技术细节。如果您想像现实模型一样阅读类,则将实体的名称放在首位并将其技术实现放在最后是有意义的。
C#和Java源于C,因此他们必须尊重“现有技术”,以免混淆有经验的程序员。
为什么
x: int = 42
不int x = 42
呢?后者不比前者更具可读性吗?
在这样一个简单的示例中,并没有太大的区别,但是让我们稍微复杂一点: int* a, b;
这是C语言中的实际有效声明,但它并没有实现直观上应有的功能。看起来我们在声明两个类型的变量int*
,但实际上我们在声明一个int*
和一个int
。
如果您的语言在声明中将类型放在变量名之后,则该问题将无法解决。
x, y *int;
两个*int
还是一个*int
和一个int
?制造int*
或*int
一个令牌而不是两个会解决这个问题,或使用类似于额外语法元素:
,其可以在任一方向工作。
:
或as
。
x, y *int
意思是“声明类型为pointer-to-int的两个变量x和y”。
int[] x, y
视为两个数组。只是愚蠢的C语法将那些修饰符当作变量的一元运算符(以及前缀和后缀的混合,不少)引起问题。