Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 1730] ファイルの概要

このコミットは、Go言語における複合リテラル(composite literals)の構文を T(x) から T{x} へと変更するものです。これは、以前の変更のロールバックと、それに伴う広範な手動編集によって実現されました。この変更は、Go言語の初期開発段階における構文の安定化と明確化に向けた重要な一歩を示しています。

コミット

  • コミットハッシュ: be2edb57614dd5a92294b7d29a34de754998fde5
  • Author: Russ Cox rsc@golang.org
  • Date: Tue Mar 3 08:39:12 2009 -0800

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/be2edb57614dd5a92294b7d29a34de754998fde5

元コミット内容

Automated g4 rollback of changelist 25024,
plus significant hand editing.

Back to T{x} for composite literals.

R=r
OCL=25612
CL=25632

変更の背景

このコミットは、Go言語の複合リテラル構文に関する以前の変更(changelist 25024)をロールバックし、T(x) 形式から T{x} 形式に戻すことを目的としています。Go言語の初期開発段階では、言語の構文やセマンティクスが活発に議論され、頻繁に変更されていました。複合リテラルの構文もその一つで、当初は関数呼び出しのような T(x) 形式が採用されていましたが、これは型変換(type conversion)の構文 T(expr) と曖昧さを持つ可能性がありました。

例えば、MyStruct(a, b) という記述があった場合、これが MyStruct 型の複合リテラルなのか、あるいは ab を引数として MyStruct 型に変換する関数呼び出しなのか、コンパイラが判断に迷うケースが生じ得ました。このような曖昧さを解消し、言語の構文をより明確で一貫性のあるものにするために、複合リテラルには波括弧 {} を使用する T{x} 形式が再導入されることになりました。

この変更は、Go言語の設計哲学である「明確性」と「シンプルさ」を追求する過程で生まれたものであり、後のGo言語の安定した構文の基礎を築く上で重要な役割を果たしました。

前提知識の解説

複合リテラル (Composite Literals)

Go言語における複合リテラルは、構造体(struct)、配列(array)、スライス(slice)、マップ(map)などの複合型(composite types)の値を、その場で直接初期化するための構文です。これにより、変数を宣言してから個々の要素に値を代入するのではなく、宣言と同時に値を設定できます。

構造体リテラル (Struct Literals): 構造体のフィールドに値を割り当ててインスタンスを作成します。 例: type Point struct { X, Y int } p := Point{X: 10, Y: 20} または p := Point{10, 20} (フィールドの順序通りに値を指定する場合)

配列リテラル (Array Literals): 固定長の配列を初期化します。 例: a := [3]int{1, 2, 3}

スライスリテラル (Slice Literals): 可変長のスライスを初期化します。配列リテラルと似ていますが、長さを指定しないか、... を使用します。 例: s := []int{1, 2, 3, 4, 5}

マップリテラル (Map Literals): キーと値のペアを指定してマップを初期化します。 例: m := map[string]int{"apple": 1, "banana": 2}

型変換 (Type Conversion)

Go言語では、ある型の値を別の型に明示的に変換することができます。これは通常、Type(expression) の形式で記述されます。 例: var i int = 10 var f float64 = float64(i) // int型のiをfloat64型に変換

構文の曖昧さ

このコミット以前のGo言語の初期バージョンでは、複合リテラルも Type(values) の形式で記述されていました。この構文は、型変換の構文 Type(expression) と非常に似ており、特定の状況下でコンパイラがどちらの意図であるかを判断するのが困難になるという問題がありました。

例えば、MyType(value) というコードがあった場合、MyType が構造体で value がそのフィールドの初期値である複合リテラルなのか、それとも MyType が型で value がその型に変換される式なのか、という曖昧さです。この曖昧さは、コンパイラの複雑性を増し、開発者が意図しない挙動に遭遇する可能性がありました。

技術的詳細

このコミットの技術的な核心は、Goコンパイラとパーサーが複合リテラルをどのように解釈するかという点にあります。T(x) から T{x} への変更は、単なる構文上の変更以上の意味を持ちます。

  1. パーサーの簡素化: T(x) 構文では、パーサーは T の後に続く括弧 () の内容が、型変換の引数なのか、それとも複合リテラルの要素リストなのかを、文脈から判断する必要がありました。これは、型 T が構造体であるか、あるいは変換可能な型であるか、x が単一の式であるか、複数の要素であるかなど、複雑なルールに基づいて行われる必要がありました。 T{x} 構文に統一することで、パーサーは T の後に波括弧 {} が続く場合は常に複合リテラルとして解釈し、丸括弧 () が続く場合は型変換(または関数呼び出し)として解釈するという、より単純で明確なルールを適用できるようになります。これにより、パーサーのロジックが簡素化され、コンパイラの堅牢性が向上します。

  2. コンパイル時のエラー検出の改善: 構文の曖昧さが解消されることで、コンパイラは開発者の意図をより正確に把握できるようになります。例えば、誤って型変換の構文で複合リテラルを記述した場合、以前はコンパイルエラーにならなかったり、意図しない型変換として解釈されたりする可能性がありました。新しい構文では、このような誤用は明確な構文エラーとして検出されるため、開発者は早期に問題を特定し修正できます。

  3. コードの可読性と保守性の向上: T{x} 構文は、複合リテラルが「値の集合」を表現するという意味合いをより直感的に伝えます。波括弧は、多くのプログラミング言語で集合やブロック、初期化リストを表すために使用されるため、Go言語の複合リテラルもこの慣習に沿うことで、他の言語からの学習者にとっても理解しやすくなります。これにより、コードの可読性が向上し、将来的な保守が容易になります。

  4. 広範なコードベースへの影響: この変更は、Go言語の標準ライブラリやテストコードを含む、当時のGoコードベース全体にわたる広範な修正を伴いました。コミットログに示されているように、doc/progssrc/cmdsrc/libtestusr/gri など、多数のファイルが影響を受けています。これは、Go言語の初期段階において、このような根本的な構文変更が許容され、かつ必要とされていたことを示しています。

コアとなるコードの変更箇所

このコミットでは、Go言語の複合リテラル構文が Type(elements) から Type{elements} へと一貫して変更されています。以下に、その変更パターンを示す代表的なコードスニペットをいくつか示します。

1. 構造体リテラル

doc/progs/cat_rot13.go の変更:

--- a/doc/progs/cat_rot13.go
+++ b/doc/progs/cat_rot13.go
@@ -32,5 +32,5 @@ type rotate13 struct {
 }
 
 func newRotate13(source reader) *rotate13 {
-	return &rotate13(source)
+	return &rotate13{source}
 }

doc/progs/print_string.go の変更:

--- a/doc/progs/print_string.go
+++ b/doc/progs/print_string.go
@@ -13,5 +13,5 @@ func (t *testType) String() string {
 }
 
 func main() {
-	t := &testType(77, "Sunset Strip");
+	t := &testType{77, "Sunset Strip"};
 	fmt.Println(t)
 }

2. 配列・スライスリテラル

doc/progs/helloworld3.go の変更:

--- a/doc/progs/helloworld3.go
+++ b/doc/progs/helloworld3.go
@@ -7,5 +7,5 @@ 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);

doc/progs/print.go の変更:

--- a/doc/progs/print.go
+++ b/doc/progs/print.go
@@ -12,6 +12,6 @@ func main() {
 
 	// harder stuff
 	type T struct { a int; b string };
-	t := T(77, "Sunset Strip");
-	a := []int(1, 2, 3, 4);
+	t := T{77, "Sunset Strip"};
+	a := []int{1, 2, 3, 4};
 	fmt.Printf("%v %v %v\n", u64, t, a);
 	fmt.Print(u64, " ", t, " ", a, "\n");
 	fmt.Println(u64, t, a);

doc/progs/sortmain.go の変更:

--- a/doc/progs/sortmain.go
+++ b/doc/progs/sortmain.go
@@ -7,12 +7,12 @@ package main
 import "sort"
 
 func ints() {
-	data := []int(74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586);
+	data := []int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586};
 	a := sort.IntArray(data);
 	sort.Sort(a);
 	if !sort.IsSorted(a) {
 		panic()
 	}
 }
 
 func strings() {
-	data := []string("monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday");
+	data := []string{"monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"};
 	a := sort.StringArray(data);
 	sort.Sort(a);
 	if !sort.IsSorted(a) {
@@ -39,15 +39,15 @@ func (p *dayArray) Less(i, j int) bool  { return p.data[i].num < p.data[j].num; }
 func (p *dayArray) Swap(i, j int)       { p.data[i], p.data[j] = p.data[j], p.data[i]; }
 
 func days() {
-	Sunday :=    day( 0, "SUN", "Sunday" );
-	Monday :=    day( 1, "MON", "Monday" );
-	Tuesday :=   day( 2, "TUE", "Tuesday" );
-	Wednesday := day( 3, "WED", "Wednesday" );
-	Thursday :=  day( 4, "THU", "Thursday" );
-	Friday :=    day( 5, "FRI", "Friday" );
-	Saturday :=  day( 6, "SAT", "Saturday" );
-	data := []*day(&Tuesday, &Thursday, &Sunday, &Monday, &Friday);
-	a := dayArray(data);
+	Sunday :=    day{ 0, "SUN", "Sunday" };
+	Monday :=    day{ 1, "MON", "Monday" };
+	Tuesday :=   day{ 2, "TUE", "Tuesday" };
+	Wednesday := day{ 3, "WED", "Wednesday" };
+	Thursday :=  day{ 4, "THU", "Thursday" };
+	Friday :=    day{ 5, "FRI", "Friday" };
+	Saturday :=  day{ 6, "SAT", "Saturday" };
+	data := []*day{&Tuesday, &Thursday, &Sunday, &Monday, &Friday};
+	a := dayArray{data};
 	sort.Sort(&a);
 	if !sort.IsSorted(&a) {
 		panic()

doc/progs/sum.go の変更:

--- a/doc/progs/sum.go
+++ b/doc/progs/sum.go
@@ -14,5 +14,5 @@ func sum(a []int) int {   // returns an int
 
 
 func main() {
-	s := sum([3]int(1,2,3));  // a slice of the array is passed to sum
+	s := sum([3]int{1,2,3});  // a slice of the array is passed to sum
 	print(s, "\n");
 }

3. マップリテラル

src/lib/http/status.go の変更:

--- a/src/lib/http/status.go
+++ b/src/lib/http/status.go
@@ -53,12 +53,12 @@ const (
 	StatusHTTPVersionNotSupported = 505;
 )
 
-var statusText = map[int]string (
+var statusText = map[int]string {
 	StatusContinue:			"Continue",
 	StatusSwitchingProtocols:	"Switching Protocols",
 
 	// ... (中略) ...
 
 	StatusServiceUnavailable:	"Service Unavailable",
 	StatusGatewayTimeout:		"Gateway Timeout",
 	StatusHTTPVersionNotSupported:	"HTTP Version Not Supported",
-)
+}

コアとなるコードの解説

上記の変更箇所は、Go言語の複合リテラルが、型名の後に丸括弧 () ではなく波括弧 {} を使用して要素を初期化するように統一されたことを示しています。

例えば、doc/progs/cat_rot13.gonewRotate13 関数では、&rotate13(source) という構造体リテラルの記述が &rotate13{source} に変更されています。これは、rotate13 型の新しいインスタンスを初期化する際に、フィールドの値を波括弧で囲んで指定するという、現在のGo言語の標準的な構文に合致しています。

同様に、配列やスライスの初期化においても、[]byte('h', 'e', 'l', ...) のような丸括弧を使った形式が []byte{'h', 'e', 'l', ...} のように波括弧を使った形式に統一されています。マップリテラルも同様に、map[int]string (...) から map[int]string {...} へと変更されています。

この変更は、Go言語の構文解析を簡素化し、型変換と複合リテラルの間の曖昧さを排除することを目的としています。丸括弧 () は関数呼び出しや型変換に、波括弧 {} は複合リテラルやコードブロックに、という明確な役割分担を導入することで、言語の構文がより一貫性のあるものになりました。これにより、コンパイラはコードをより効率的かつ正確に解析できるようになり、開発者にとってもコードの意図がより明確に伝わるようになりました。

関連リンク

参考にした情報源リンク

  • GitHub Go Repository: https://github.com/golang/go/
  • Go言語の公式ドキュメント: https://go.dev/
  • Go言語の初期の設計に関する議論 (Go Mailing List archivesなど)
    • 複合リテラルの構文に関する具体的な議論のアーカイブを特定することは困難でしたが、Go言語の初期の設計はメーリングリストや設計ドキュメントで活発に議論されていました。この変更は、その議論の結果として構文の明確化が図られたものと推測されます。
    • 関連する可能性のある議論のキーワード: "Go composite literal syntax", "Go type conversion vs composite literal", "Go language design history"
    • Go言語の進化に関する一般的な情報源: "The Go Programming Language" (Alan A. A. Donovan, Brian W. Kernighan), "Go in Action" (William Kennedy, Brian Ketelsen, Erik St. Martin) など。
    • 特に、Go言語の設計思想に関するRuss Cox氏やRob Pike氏の講演や記事は、このような構文変更の背景を理解する上で非常に参考になります。