[インデックス 1672] ファイルの概要
このコミットは、Go言語における複合リテラル(composite literals)の構文を {}
から ()
へと変更するものです。これはGo言語の初期段階における重要な構文変更であり、言語の設計思想や将来の発展に影響を与えるものでした。
コミット
commit 9f8f2e6130d7899b0f689232d173ea2e4441285c
Author: Russ Cox <rsc@golang.org>
Date: Fri Feb 13 14:48:32 2009 -0800
convert composite literals from { } to ( ).
only non-trivial changes are in
convlit1.go
golden.out
R=gri
OCL=25019
CL=25024
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/9f8f2e6130d7899b0f689232d173ea2e4441285c
元コミット内容
複合リテラルを {}
から ()
に変換する。
非自明な変更は convlit1.go
と golden.out
のみ。
変更の背景
Go言語の初期開発段階において、複合リテラルの構文は {}
を使用していました。しかし、この構文はブロックステートメント(例えば if
や for
のブロック)と視覚的に類似しており、特にパーサーにとって曖昧さを生じさせる可能性がありました。
例えば、以下のようなコードを考えます。
func() {
// 何らかの処理
}
これは関数リテラルですが、もし複合リテラルも {}
を使用していた場合、以下のようなコードが問題を引き起こす可能性がありました。
MyStruct{
Field1: value1,
Field2: value2,
}
この構文は、文脈によってはコードブロックと誤解される可能性があり、パーサーの複雑性を増大させたり、開発者が意図しない解釈をされたりするリスクがありました。このような曖昧さを解消し、言語の構文解析をより明確かつ堅牢にするために、複合リテラルの区切り文字を {}
から ()
に変更するという決定がなされました。これにより、コードの可読性とパーサーの実装の簡素化が図られました。
前提知識の解説
複合リテラル (Composite Literals)
Go言語における複合リテラルは、構造体(struct)、配列(array)、スライス(slice)、マップ(map)などの複合データ型を初期化するための構文です。これにより、変数の宣言と同時にその初期値を簡潔に記述することができます。
変更前の構文(このコミット以前):
- 構造体リテラル:
type MyStruct struct { Field1 int Field2 string } s := MyStruct{Field1: 10, Field2: "hello"}
- 配列/スライスリテラル:
arr := []int{1, 2, 3} slice := []string{"a", "b", "c"}
- マップリテラル:
m := map[string]int{"key1": 1, "key2": 2}
このコミットでは、上記の {}
で囲まれた部分が ()
に変更されます。
Go言語の構文解析 (Parsing)
Goコンパイラは、ソースコードを解析して抽象構文木(AST)を構築します。この過程で、トークン列が特定の構文規則に合致するかどうかを判断します。構文に曖昧さがあると、コンパイラは複数の解釈を考慮する必要があり、パーサーの複雑性が増したり、エラーハンドリングが難しくなったりします。
この変更は、特にGo言語の初期段階において、言語設計者が構文の明確性とコンパイラの効率性を追求した結果と言えます。
技術的詳細
このコミットの技術的な核心は、Go言語のパーサーと字句解析器(lexer)が複合リテラルを認識する方法の変更にあります。具体的には、複合リテラルの開始と終了を示すデリミタが波括弧 {}
から丸括弧 ()
に変更されました。
この変更は、主に以下の理由から行われたと考えられます。
- 構文の曖昧さの解消: Go言語では、コードブロック(
if
文、for
文、関数本体など)も波括弧{}
を使用します。複合リテラルも{}
を使用すると、特に特定の文脈で、パーサーが複合リテラルとコードブロックを区別するのが難しくなる可能性がありました。例えば、型名が省略された複合リテラルや、改行の有無によって解釈が変わるようなケースです。丸括弧()
を使用することで、このような曖昧さがなくなり、パーサーの実装がよりシンプルかつ堅牢になります。 - 可読性の向上: 複合リテラルとコードブロックが異なるデリミタを持つことで、コードの視覚的な区別が明確になり、開発者にとってもコードの意図をより迅速に理解できるようになります。
- 言語の一貫性: Go言語では、関数呼び出しや型変換など、他の多くの場所で丸括弧
()
が引数や要素のリストを囲むために使用されます。複合リテラルも()
を使用することで、言語全体での括弧の使用方法に一貫性が生まれます。
この変更は、コンパイラのフロントエンド、特に字句解析器と構文解析器に大きな影響を与えます。字句解析器は、ソースコードをトークンに分割する際に、{
と }
を複合リテラルの開始/終了としてではなく、コードブロックの開始/終了として認識するように変更されます。一方、構文解析器は、複合リテラルを解析する際に (
と )
を期待するように変更されます。
また、この変更はGo言語の標準ライブラリやテストコード全体にわたる大規模なコードベースの修正を伴います。コミットログに示されているように、doc/progs
、src/lib
、test
、usr
などの多数のファイルが影響を受けており、これは複合リテラルがGo言語の基本的な構文要素として広く使用されていたことを示しています。
コアとなるコードの変更箇所
このコミットでは、Go言語のソースコード全体にわたって複合リテラルの構文が {}
から ()
に変更されています。特に、コミットメッセージで「非自明な変更」として挙げられている test/convlit1.go
と test/golden.out
は、この構文変更がコンパイラのテストスイートにどのように影響したかを示しています。
具体的な変更例をいくつか示します。
doc/progs/cat_rot13.go
の変更例:
--- a/doc/progs/cat_rot13.go
+++ b/doc/progs/cat_rot13.go
@@ -32,7 +32,7 @@ type rotate13 struct {
}
func newRotate13(source reader) *rotate13 {
- return &rotate13{source}
+ return &rotate13(source)
}
ここでは、構造体 rotate13
の複合リテラルが {source}
から (source)
に変更されています。
doc/progs/helloworld3.go
の変更例:
--- a/doc/progs/helloworld3.go
+++ b/doc/progs/helloworld3.go
@@ -7,7 +7,7 @@ package main
import fd "fd"
func main() {
- hello := []byte{'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '\n'};
+ hello := []byte('h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '\n');
fd.Stdout.Write(hello);
file, err := fd.Open("/does/not/exist", 0, 0);
if file == nil {
ここでは、バイトスライス []byte
の複合リテラルが {'h', ...}
から ('h', ...)
に変更されています。
src/lib/bignum.go
の変更例:
--- a/src/lib/bignum.go
+++ b/src/lib/bignum.go
@@ -114,10 +114,10 @@ func Dump(x []Digit) {
type Natural []Digit;
var (
- natZero Natural = Natural{};
- natOne Natural = Natural{1};
- natTwo Natural = Natural{2};
- natTen Natural = Natural{10};
+ natZero Natural = Natural();
+ natOne Natural = Natural(1);
+ natTwo Natural = Natural(2);
+ natTen Natural = Natural(10);
)
ここでは、Natural
型(スライス型)の複合リテラルが {}
から ()
に変更されています。
これらの変更は、Go言語の構文規則がこのコミットによって根本的に変更されたことを明確に示しています。
コアとなるコードの解説
このコミットにおける「コアとなるコード」は、Goコンパイラの字句解析器(lexer)と構文解析器(parser)の実装そのものにあります。これらのツールは、ソースコードを読み込み、トークン化し、そのトークン列が言語の文法規則に合致するかどうかを検証します。
変更前は、複合リテラルを認識するために字句解析器が {
と }
を特別な意味を持つトークンとして扱い、構文解析器がこれらのトークンを複合リテラルの開始と終了として解釈していました。このコミットでは、この認識ロジックが変更され、字句解析器は (
と )
を複合リテラルのデリミタとして扱うようになり、構文解析器もそれに合わせて期待するトークンを変更しました。
具体的な変更は、Goコンパイラの内部実装(特に go/parser
および go/scanner
パッケージに相当する初期のコード)で行われました。これらの変更は、Go言語の文法定義(EBNFなど)にも反映され、言語仕様レベルでの変更となりました。
この変更は、Go言語の設計における初期の試行錯誤と、構文の明確性およびコンパイラの効率性を追求する姿勢を示しています。複合リテラルの構文は、Go言語のコードベース全体で頻繁に使用されるため、この変更は広範囲にわたる影響を及ぼしました。
関連リンク
- Go言語の公式ドキュメント(現在の複合リテラルの構文について):https://go.dev/ref/spec#Composite_literals
参考にした情報源リンク
- Go言語のコミット履歴 (GitHub): https://github.com/golang/go/commits/master
- Go言語の初期の設計に関する議論(Go言語のメーリングリストやデザインドキュメントなど、当時の情報源)
- Go言語の初期の設計に関する情報は、現在の公式ドキュメントやブログ記事では直接的に言及されていないことが多いですが、当時のメーリングリストのアーカイブや、Go言語の歴史に関する書籍・記事に詳細が残されている可能性があります。
- 特に、Go言語の設計思想や構文の決定プロセスについては、Russ CoxやRobert Griesemer、Ken Thompsonといった主要な開発者の講演や論文が参考になります。
- The Evolution of Go (Russ Cox, 2015) - Go言語の進化について語られていますが、初期の設計判断についても触れられている可能性があります。
- Go at Google: Language Design in the Service of Software Engineering (Rob Pike, 2012) - Go言語の設計哲学について。
- Go Programming Language Specification - 現在のGo言語の仕様。過去の変更点を直接的に記述しているわけではないが、現在の構文を理解する上で不可欠。
これらの情報源は、このコミットが行われた当時のGo言語の設計思想や、なぜこのような構文変更が必要とされたのかを深く理解する上で役立ちます。