C 系语言有一套自恰的 expression 风格的类型声明方式:
int *p;
意思是对 p 进行 dereference 后得到的表达式类型为int
,即不声明这个变量本身的类型,而是通过一个表达式来确定“这个变量是怎么使用的”进而确定其类型。
自然地,这样声明函数:
int main(int argc,char *argv[]);
表示对main(int argc,char *argv[])
进行调用后得到的表达式类型为int
。
即你需要解一个 f(x)=y 的方程,x 为你声明的变量的类型,y 为你声明的表达式类型。
但是当表达式比较复杂(尤其是变量名还可以省略)的时候,https://blog.golang.org/declaration-syntax 有这么一个例子:
int (*(fp)(int ()(int, int), int))(int, int);
这种“名称在中间,类型放两边”的中值声明方式可谓是非常丑陋,对应的 Go 的声明如下:
f func(func(int,int) int, int) func(int, int) int
可以一眼就看出来,f 是一个返回另一个函数的函数,f 的参数一是接受两个int
参数,返回一个int
的函数;参数二是一个int
;返回的函数是一个接受两个int
参数,返回一个int
的函数。
但这个可读性的提升不一定是因为后置,可能有些习惯了 C/C++,Java 的人更喜欢这样:
int func(int,int) func(int func(int,int),int) f;
但我个人觉得这样更容易引起混淆,而且还有一种头重脚轻的感觉。它跟你熟悉的东西似是而非,却又截然不同。
因为这已经不是开头说的那一种 expression 解方程风格的声明方式了,而是在这基础上的一种延伸,类似于 pattern match,更形象的说是把类型当作占位符,匹配两边的shape。这也是各种现代的语言( Go,Rust,TypeScript,Kotlin,Swift 等)采用的方式。
在这种模式下一个变量的类型就是它的 shape,foo()
的类型就是Fn()
,而不再写成void *(void)
;
(&3,true)
的类型就是(&i32,bool)
;
struct{a:1,b:2}
的类型就是struct{a:i32,b:i32}
;
返回值更不必多说,去掉 C 风格后自然放在后面更符合直觉。
后置的另一大好处就是在使用现代编程语言的 type inference 时保持一致性,
let x = 1;
let y:i32 = 1;
显然要比:
int x = 1;
var y = 1;
具有更好的一致性,编译器无需判断开头的是类型还是关键字。
总而言之,C 风格的声明方式是自恰的,也可以说是优雅的;但是却太过炫技,不够 ergonomic 。
而这套类型后置,与 C 完全迥异的风格,咋一看觉得 exotic,熟悉了之后越用越喜欢。再回头写传统的 C 系语言时就总想把这些玩意扫进历史的垃圾堆(:
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.