Go's Declaration Syntax - The Go Blog
Go言語の構文のデザインについて説明している「Go’s Declaration Syntax - The Go Blog」の読書メモです。
Source
Go’s Declaration Syntax - The Go Blog
Introduction
Goの型宣言が伝統的なCと違う理由を説明します。
C syntax
Cでは、型宣言用の特別なキーワードは必要ありません。
int x;
このコードは、x
がint
型を持つことを宣言しています。
int *p;
int a[3];
このコードは、int
が前にあるので、int
へのポインタと、int
の配列を宣言することになります。
Cの関数は伝統的には次のように宣言していました。
int main(argc, argv)
int argc;
char *argv[];
{ /* ... */ }
int
が前にあるので、main(argc, argv)
はint
を返す関数となります。
以上の宣言は型が単純であれば問題ありませんが、すぐに複雑になってしまいます。
int (*fp)(int a, int b);
(*fp)(a, b)
という表現を書くと、int
を返す関数になるため、fp
は関数ポインタになります。
int (*fp)(int (*ff)(int x, int y), int b)
このようになると読むのが難しくなってきます。
int main(int, char *[])
関数の宣言では変数が省略できるので、main
はこのようにも宣言できます。
int (*fp)(int (*)(int, int), int)
関数ポンンタから名前を取るとこのようになります。
int (*(*fp)(int (*)(int, int), int))(int, int)
関数ポインタを返す型になると、fp
の宣言であるように読むことすら困難です。
また、型名と宣言が同じであることの副作用として、キャストをカッコで括らなくてはならなくなっています。
(int)M_PI
Go syntax
C以外の言語では、このように型を変数名の後ろで宣言します。
x: int
p: pointer to int
a: array[3] of int
こうすると、自然に左から右に読めるため、Goではさらに簡潔さを加えた次のような表現をします。
x int
p *int
a [3]int
[3]int
という型の表現と配列の使用方法は区別されます。
Goのmain
は実際には引数を取りませんが、次のように表現できます。
func main(argc int, argv []string) int
Cとの違いは大きくはありませんが、次のように自然に読めます。
「function main takes an int and a slice of strings and returns an int.」
引数名を消すと、混乱がなくなることが明らかになります。
func main(int, []string) int
左から右のスタイルのメリットは、型が複雑になってもうまく表現できることです。
f func(func(int,int) int, int) int
関数を返す関数も同様です。
f func(func(int,int) int, int) func(int, int) int
型と式の構文が区別されるため、Goではクロージャの定義と呼び出しが簡単になります。
sum := func(a, b int) int { return a+b } (3, 4)
Pointer
ポインタはルールの例外です。
var a []int
x = a[1]
配列では、ブラケットは型の左に書き、式では右に書きます。
var p *int
x = *p
慣習により、GoのポインタはCの表記を使い、配列のような逆表記は採用しません。
var p *int
x = p*
と後置しないのは、積演算記号と衝突するからです。
var p ^int
x = p^
Pascalのように^
を使うべきだったかもしれません。
[]int("hi")
と書くことはできますが、
(*int)(nil)
のように*
で始まる型はカッコで括らなければなりませんが、*
を諦めていたらカッコが不要になるからです。
Goのポインタ構文はCに類似しているため、型と式の構文の曖昧さを完全に消すことができません。
それでも、全体としてGoの構文は、特に複雑になった時、Cよりも理解しやすいと信じています。
Notes
Goの宣言は左から右に読むのに対して、Cの読み方は、David AndersonのClockwise/Spiral Ruleとして知られてきました。