[インデックス 1724] ファイルの概要
このコミットは、Go言語の仕様書 (doc/go_spec.html
) における2つの重要な変更を反映しています。一つは、未実装であった select-on-type
機能に関する記述の削除です。もう一つは、複合リテラル(Composite Literals)の構文において、要素を囲む括弧を ()
から {}
へと変更するものです。これにより、Go言語の構文がより一貫性のあるものになり、将来の言語設計の方向性が示唆されています。
コミット
commit 426335f87bbcdb8390ac9730bbc96661a9e5a551
Author: Rob Pike <r@golang.org>
Date: Mon Mar 2 17:52:52 2009 -0800
delete paragraph about unimplemented select-on-type feature.
change () to {} in all composite literals
DELTA=20 (0 added, 7 deleted, 13 changed)
OCL=25604
CL=25606
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/426335f87bbcdb8390ac9730bbc96661a9e5a551
元コミット内容
このコミットは、Go言語の初期段階における仕様変更を記録しています。具体的には、以下の2点です。
- 未実装の
select-on-type
機能に関する段落の削除: Go言語の並行処理の根幹をなすselect
ステートメントには、当初、チャネルの型に基づいて通信を選択するselect-on-type
という機能が検討されていました。しかし、この機能は最終的に実装されず、仕様書からその記述が削除されました。これは、言語の複雑性を軽減し、よりシンプルで強力な並行処理モデルを追求するGo言語の設計思想を反映しています。 - 複合リテラルにおける
()
から{}
への変更: 構造体、配列、スライス、マップなどの複合型を初期化する際に使用される複合リテラルの構文が変更されました。以前は要素を()
で囲んでいましたが、このコミットにより{}
を使用するようになりました。これは、他のプログラミング言語におけるブロックや初期化リストの慣習に合わせ、Go言語の構文の一貫性と可読性を向上させるための変更です。
変更の背景
このコミットが行われた2009年3月は、Go言語がまだ一般に公開される前の開発初期段階にあたります。この時期は、言語の主要な設計決定が活発に行われていた時期であり、多くの実験的な機能が検討され、その中から最終的な仕様が固まっていきました。
select-on-type
機能の削除の背景:
Go言語の select
ステートメントは、複数のチャネル操作を同時に待ち受けるための強力な機能です。初期の設計では、チャネルを通じて送受信される値の「動的な型」に基づいて select
のケースを分岐させる select-on-type
のような機能が検討されていた可能性があります。しかし、このような機能は、言語の型システムを複雑にし、コンパイル時の最適化を困難にする可能性があります。Go言語の設計哲学は「シンプルさ」と「実用性」に重きを置いているため、最終的にこの機能は採用されず、より予測可能で効率的な select
の動作が選択されました。これにより、Goの並行処理モデルは、型に依存しないチャネル通信と、シンプルな select
構文によって、高いパフォーマンスと安全性を実現しています。
複合リテラル構文変更の背景:
プログラミング言語において、複合データ構造の初期化構文は非常に重要です。初期のGo言語では、複合リテラルに ()
を使用していましたが、これは関数呼び出しの構文と視覚的に類似しており、混乱を招く可能性がありました。多くのC系の言語や、JavaScriptなどのスクリプト言語では、配列やオブジェクトの初期化に {}
を使用する慣習があります。Go言語がこれらの言語の経験を取り入れ、より直感的で一般的な慣習に合わせることで、プログラマーがGo言語を学習する際の障壁を低減し、コードの可読性を向上させることを目指したと考えられます。{}
を使用することで、複合リテラルが明確にデータ構造の初期化であることを示し、コードの意図がより明確になります。
前提知識の解説
Go言語の複合リテラル (Composite Literals)
Go言語における複合リテラルは、構造体 (struct)、配列 (array)、スライス (slice)、マップ (map) といった複合データ型を、その場で値を指定して初期化するための構文です。これにより、変数を宣言し、後から個々の要素に値を代入する手間を省き、簡潔にデータ構造を生成できます。
例(変更前 - ()
を使用):
// 構造体
type Point struct { X, Y int }
p := Point(1, 2) // Point{X:1, Y:2} と同じ意味
// 配列
primes := [6]int(2, 3, 5, 7, 9, 11)
// スライス
s := []int(1, 2, 3)
// マップ
m := map[string]int("good": 0, "bad": 1)
例(変更後 - {}
を使用):
// 構造体
type Point struct { X, Y int }
p := Point{1, 2} // または Point{X:1, Y:2}
// 配列
primes := [6]int{2, 3, 5, 7, 9, 11}
// スライス
s := []int{1, 2, 3}
// マップ
m := map[string]int{"good": 0, "bad": 1}
この変更により、Go言語の複合リテラルは、他の多くの言語における配列やオブジェクトの初期化構文と視覚的に整合性が取れるようになりました。
Go言語の select
ステートメントとチャネル (Channels)
Go言語は、CSP (Communicating Sequential Processes) に基づく並行処理モデルを採用しており、ゴルーチン (goroutine) とチャネル (channel) を用いて並行処理を実現します。
- ゴルーチン: 軽量なスレッドのようなもので、Goランタイムによって管理されます。
- チャネル: ゴルーチン間で値を安全に送受信するための通信路です。チャネルは型付けされており、特定の型の値のみを送受信できます。
select
ステートメントは、複数のチャネル操作(送受信)を同時に待ち受けるための構文です。いずれかのチャネル操作が可能になった時点で、その操作を実行します。複数の操作が可能な場合は、Goランタイムがランダムかつ公平に一つを選択します。
select
ステートメントの基本構文:
select {
case <-ch1:
// ch1 から値を受信した場合の処理
case val := <-ch2:
// ch2 から値を受信した場合の処理
case ch3 <- msg:
// ch3 に値を送信した場合の処理
default:
// どのチャネル操作もすぐに実行できない場合の処理(オプション)
}
select-on-type
(未実装機能)
このコミットで削除された select-on-type
は、チャネルを通じて送受信される値の「動的な型」に基づいて select
のケースを分岐させるという、Go言語の初期に検討された機能です。
例えば、以下のような構文が想定されていたかもしれません(これはあくまで想像であり、実際の提案構文ではありません):
select {
case val.(int) := <-ch: // ch から int 型の値を受信した場合
// int 型の値を処理
case val.(string) := <-ch: // ch から string 型の値を受信した場合
// string 型の値を処理
}
このような機能は、インターフェース型を介して様々な型の値を送受信する際に、受信側で動的に型を判別して処理を分岐させるのに便利そうに見えます。しかし、Go言語の設計者は、この機能が言語の複雑性を増し、コンパイル時の最適化を妨げる可能性があると判断し、最終的に採用しませんでした。代わりに、Go言語では型アサーションや型スイッチ (switch v := val.(type)
) を用いて、受信したインターフェース値の動的な型を判別します。select
ステートメントは、あくまでチャネルの準備状況(送受信可能かどうか)に基づいて動作し、値の型による分岐は select
の内部で行われる通常のGoコードで行うという設計が採用されました。
技術的詳細
複合リテラルの構文変更
この変更は、Go言語の構文解析器と、言語仕様を記述するドキュメントの両方に影響を与えます。
- 構文解析器への影響: 実際のコンパイラの実装では、複合リテラルを解析する際に
(
と)
を期待していた部分を{
と}
に変更する必要があります。これは、言語の字句解析(Lexical Analysis)と構文解析(Parsing)のルールに直接的な変更をもたらします。 - 仕様書への影響:
doc/go_spec.html
の変更は、この構文変更を公式に文書化するものです。特に、CompositeLit
の文法定義がLiteralType "(" ... ")"
からLiteralType "{" ... "}"
に変更されています。また、具体的なコード例もすべて新しい{}
構文に更新されています。
この変更は、Go言語の構文の一貫性を高める上で重要です。Go言語では、コードブロックや構造体の定義、マップの初期化など、多くの場所で {}
が使用されます。複合リテラルも {}
を使用することで、視覚的な統一感が生まれ、コードのパターン認識が容易になります。
select-on-type
記述の削除
doc/go_spec.html
から削除された段落は、以下の内容を含んでいました。
<p>
If the channel sends or receives an interface type, its
communication can proceed only if the type of the communication
clause matches that of the dynamic value to be exchanged.
</p>
この記述は、select
ステートメントがチャネルを通じてインターフェース型を送受信する際に、その通信が「通信句の型が交換される動的な値の型と一致する場合にのみ進行できる」という、未実装の select-on-type
機能の概念を説明していました。
この段落の削除は、Go言語の select
ステートメントが、チャネルの準備状況(値の送受信が可能かどうか)のみに基づいて動作し、送受信される値の動的な型には依存しないという最終的な設計決定を明確に示しています。これにより、select
の動作はよりシンプルで予測可能になり、ランタイムの複雑性も軽減されます。Go言語の型システムは静的型付けを基本としており、動的な型チェックは必要に応じて明示的に行う(型アサーションや型スイッチ)という原則が守られています。
コアとなるコードの変更箇所
このコミットは、doc/go_spec.html
ファイルのみを変更しています。以下に、主要な変更箇所の差分を示します。
--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -1854,12 +1854,12 @@ mypackage.Math.Sin // if Math is declared in an intervening scope
Composite literals construct values for structs, arrays, slices, and maps
and create a new value each time they are evaluated.
They consist of the type of the value
-followed by a parenthesized list of expressions,
+followed by a brace-bound list of expressions,
or a list of expression pairs for map literals.
</p>
<pre class="grammar">
-CompositeLit = LiteralType "(" [ ( ExpressionList | ExprPairList ) [ "," ] ] ")" .
+CompositeLit = LiteralType "{" [ ( ExpressionList | ExprPairList ) [ "," ] ] "}" .
LiteralType = StructType | ArrayType | "[" "..." "]" ElementType |
SliceType | MapType | TypeName .
ExprPairList = ExprPair { "," ExprPair } .
@@ -1884,7 +1884,7 @@ one may write
</p>
<pre>
-pi := Num(Rat(22, 7), 3.14159, "pi");
+pi := Num{Rat{22, 7}, 3.14159, "pi"};
</pre>
<p>
@@ -1897,9 +1897,9 @@ to the number of elements in the literal.
</p>
<pre>
-buffer := [10]string(); // len(buffer) == 10
-primes := [6]int(2, 3, 5, 7, 9, 11); // len(primes) == 6
-days := [...]string("Sat", "Sun"); // len(days) == 2
+buffer := [10]string{}; // len(buffer) == 10
+primes := [6]int{2, 3, 5, 7, 9, 11}; // len(primes) == 6
+days := [...]string{"Sat", "Sun"}; // len(days) == 2
</pre>
<p>
@@ -1909,7 +1909,7 @@ Thus, the length and capacity of a slice literal is the number of elements
</p>
<pre>
-[]T(x1, x2, ... xn)
+[]T{x1, x2, ... xn}
</pre>
<p>
@@ -1917,7 +1917,7 @@ and is a shortcut for a slice operation applied to an array literal:
</p>
<pre>
-[n]T(x1, x2, ... xn)[0 : n]
+[n]T{x1, x2, ... xn}[0 : n]
</pre>
<p>
@@ -1926,7 +1926,7 @@ key-value pairs separated by a colon:
</p>
<pre>
-m := map[string]int("good": 0, "bad": 1, "indifferent": 7);
+m := map[string]int{"good": 0, "bad": 1, "indifferent": 7};
</pre>
<h3>Function literals</h3>
@@ -1986,7 +1986,7 @@ x
2
(s + ".txt")
f(3.1415, true)
-Point(1, 2)
+Point{1, 2}
m["foo"]
s[i : j + 1]
obj.color
@@ -2198,7 +2198,7 @@ difference in the index values in the slice. After slicing the array <code>a</c
</p>
<pre>
-a := [4]int(1, 2, 3, 4);
+a := [4]int{1, 2, 3, 4};
s := a[1:3];
</pre>
@@ -3227,7 +3227,7 @@ after execution their values will be those of the last iteration.\n \n <pre>\n var a [10]string;\n-m := map[string]int("mon":0, "tue":1, "wed":2, "thu":3, "fri":4, "sat":5, "sun":6);\n+m := map[string]int{"mon":0, "tue":1, "wed":2, "thu":3, "fri":4, "sat":5, "sun":6};\n \n for i, s := range a {\n \t// type of i is int\n@@ -3317,11 +3317,6 @@ effects in that evaluation will occur for all the communications\n in the "select" statement.\n </p>\n <p>\n-If the channel sends or receives an interface type, its\n-communication can proceed only if the type of the communication\n-clause matches that of the dynamic value to be exchanged.\n-</p>\n-<p>\n If multiple cases can proceed, a uniform fair choice is made to decide\n which single communication will execute.\n <p>\n@@ -3646,7 +3641,7 @@ string(0x65e5) // "日"\n bytes are those of the array/slice.\n \n <pre>\n-string([]byte('h', 'e', 'l', 'l', 'o')) // "hello"\n+string([]byte{'h', 'e', 'l', 'l', 'o'}) // "hello"\n </pre>\n </li>\n </ul>\n@@ -4141,8 +4136,6 @@ cap() does not work on maps or chans.\n <br/>\n len() does not work on chans.\n <br/>\n-select doesn't check dynamic type of interfaces.\n-<br/>\n Conversions work for any type; doc says only arithmetic types and strings.\n </font>\n </p>\n```
## コアとなるコードの解説
上記の差分は、Go言語の仕様書 (`doc/go_spec.html`) における以下の変更を示しています。
1. **複合リテラルの構文定義の変更**:
* `CompositeLit = LiteralType "(" [ ( ExpressionList | ExprPairList ) [ "," ] ] ")" .`
* から
* `CompositeLit = LiteralType "{" [ ( ExpressionList | ExprPairList ) [ "," ] ] "}" .`
* へと変更されています。これは、複合リテラルの要素を囲む記号が `()` から `{}` に変わったことを文法的に定義しています。
2. **複合リテラルのコード例の更新**:
* `pi := Num(Rat(22, 7), 3.14159, "pi");` が `pi := Num{Rat{22, 7}, 3.14159, "pi"};` に。
* `buffer := [10]string();` が `buffer := [10]string{};` に。
* `primes := [6]int(2, 3, 5, 7, 9, 11);` が `primes := [6]int{2, 3, 5, 7, 9, 11};` に。
* `days := [...]string("Sat", "Sun");` が `days := [...]string{"Sat", "Sun"};` に。
* `[]T(x1, x2, ... xn)` が `[]T{x1, x2, ... xn}` に。
* `[n]T(x1, x2, ... xn)[0 : n]` が `[n]T{x1, x2, ... xn}[0 : n]` に。
* `m := map[string]int("good": 0, "bad": 1, "indifferent": 7);` が `m := map[string]int{"good": 0, "bad": 1, "indifferent": 7};` に。
* `Point(1, 2)` が `Point{1, 2}` に。
* `a := [4]int(1, 2, 3, 4);` が `a := [4]int{1, 2, 3, 4};` に。
* `m := map[string]int("mon":0, "tue":1, "wed":2, "thu":3, "fri":4, "sat":5, "sun":6);` が `m := map[string]int{"mon":0, "tue":1, "wed":2, "thu":3, "fri":4, "sat":5, "sun":6};` に。
* `string([]byte('h', 'e', 'l', 'l', 'o'))` が `string([]byte{'h', 'e', 'l', 'l', 'o'})` に。
これらの変更は、複合リテラルの構文が `()` から `{}` に統一されたことを示す具体的な例です。
3. **`select-on-type` 機能に関する記述の削除**:
* `If the channel sends or receives an interface type, its communication can proceed only if the type of the communication clause matches that of the dynamic value to be exchanged.` という段落が削除されています。
* また、`select doesn't check dynamic type of interfaces.` という記述も削除されています。
これらの削除は、Go言語の `select` ステートメントが、チャネルを通じて送受信される値の動的な型に基づいて通信を分岐させる機能を持たないことを明確にしています。これは、Go言語の設計がシンプルさと予測可能性を重視していることの表れです。
これらの変更は、Go言語の初期段階における重要な構文とセマンティクスの決定を反映しており、現在のGo言語の姿を形成する上で不可欠なステップでした。
## 関連リンク
* Go言語の公式ドキュメント: [https://go.dev/doc/](https://go.dev/doc/)
* Go言語の仕様書: [https://go.dev/ref/spec](https://go.dev/ref/spec) (このコミットが変更したドキュメントの現在のバージョン)
* Go言語の歴史に関するブログ記事やプレゼンテーション(Go言語の初期設計に関する情報源を探す際に役立ちます)
## 参考にした情報源リンク
* Go言語の公式リポジトリのコミット履歴: [https://github.com/golang/go/commits/master](https://github.com/golang/go/commits/master)
* Go言語の設計に関する議論やメーリングリストのアーカイブ(Go言語の初期の設計決定に関する詳細な情報が含まれている可能性があります)
* Go言語の複合リテラルに関する解説記事(例: [https://go.dev/blog/go-slices-usage-and-internals](https://go.dev/blog/go-slices-usage-and-internals) など、複合リテラルの使用法を説明しているもの)
* Go言語の `select` ステートメントに関する解説記事(例: [https://go.dev/blog/go-concurrency-patterns-pipelines](https://go.dev/blog/go-concurrency-patterns-pipelines) など、並行処理パターンを説明しているもの)
* Go言語の初期の設計に関するプレゼンテーションや論文(例: "Go: a new type of language" by Rob Pike, Robert Griesemer, Ken Thompson など)