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 系语言时就总想把这些玩意扫进历史的垃圾堆(: