[インデックス 19293] ファイルの概要
このコミットは、Go言語の仕様書 doc/go_spec.html
における評価順序に関する記述の明確化を目的としています。具体的には、パッケージレベルの初期化における評価順序と、それ以外の一般的な式、代入、return文における評価順序の間の潜在的な不整合を解消し、より正確な説明を提供しています。
コミット
commit dbe5f88804ad974a8c98d67421c9aac302873359
Author: Robert Griesemer <gri@golang.org>
Date: Wed May 7 08:50:52 2014 -0700
spec: remove evaluation order inconsistency
This is a clarification of what happens already.
Not a language change.
Fixes #7137.
LGTM=iant, r, rsc
R=r, rsc, iant, ken
CC=golang-codereviews
https://golang.org/cl/96000044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/dbe5f88804ad974a8c98d67421c9aac302873359
元コミット内容
spec: remove evaluation order inconsistency
This is a clarification of what happens already.
Not a language change.
Fixes #7137.
LGTM=iant, r, rsc
R=r, rsc, iant, ken
CC=golang-codereviews
https://golang.org/cl/96000044
変更の背景
このコミットの背景には、Go言語の仕様書における「評価順序」に関する記述が、一部のケースで曖昧さを含んでいたという問題があります。特に、パッケージレベルでの変数初期化と、関数内部での一般的な式の評価順序との間に、読者が誤解を招く可能性のある表現が存在していました。
コミットメッセージにある「This is a clarification of what happens already. Not a language change.」という記述は、この変更がGo言語自体の動作を変更するものではなく、既存の動作に対する仕様書の記述をより明確にするためのものであることを強調しています。つまり、Goコンパイラの実装は既にこの「正しい」評価順序に従っていましたが、仕様書の表現がその実態を完全に反映していなかったため、開発者が混乱する可能性がありました。
Fixes #7137
という記述から、この変更がGitHubのIssue #7137に関連していることがわかります。このIssueは、Go言語の評価順序に関する特定の曖昧さや、それによって生じる可能性のある混乱を指摘していたと考えられます。Go言語の仕様は、関数呼び出し、メソッド呼び出し、チャネル操作については左から右への評価順序を保証していますが、すべてのサブ式の評価について厳密な全順序を定義しているわけではありません。この「部分的な順序付け」が、特定の複雑な式において、コンパイラの実装によって異なる結果を生む可能性があり、それが「inconsistency(不整合)」として認識されていた可能性があります。このコミットは、そのような曖昧さを解消し、仕様書の記述をより堅牢にすることを目的としています。
前提知識の解説
このコミットを理解するためには、Go言語における以下の概念を理解しておく必要があります。
-
式の評価順序 (Order of Evaluation): Go言語では、式のオペランド、代入、return文の評価において、関数呼び出し、メソッド呼び出し、チャネル操作は「字句的に左から右へ」評価されると規定されています。これは、例えば
f() + g()
のような式では、f()
がg()
の前に評価されることを意味します。しかし、すべてのサブ式(例えば、配列リテラルの要素やマップリテラルのキーと値など)の評価順序が厳密に定義されているわけではありません。 -
パッケージレベルの初期化 (Package-level Initialization): Goプログラムは、パッケージの初期化から始まります。各パッケージは、そのパッケージレベルで宣言された変数と定数を初期化します。この初期化は、宣言された順序ではなく、「初期化の依存関係」に基づいて行われます。つまり、ある変数の初期化式が別の変数の値に依存している場合、依存される変数が先に初期化されます。この依存関係の解決は、コンパイル時に行われます。
-
doc/go_spec.html
: これはGo言語の公式仕様書であり、Go言語の構文、セマンティクス、ランタイムの動作に関するすべての公式な情報源です。Go言語の動作に関する疑問が生じた場合、最終的にはこの仕様書が参照されます。
このコミットは、上記の「式の評価順序」と「パッケージレベルの初期化」の間の相互作用、特にパッケージレベルの初期化における評価順序の特殊性について、仕様書の記述をより正確にするものです。
技術的詳細
このコミットは、doc/go_spec.html
の Order of evaluation
セクションに修正を加えています。主な変更点は以下の通りです。
-
パッケージレベルの初期化の明確化: 変更前は、式のオペランド、代入、return文の評価について一律に「字句的に左から右へ」というルールが適用されるかのような記述でした。しかし、パッケージレベルの初期化においては、このルールよりも「初期化の依存関係」が優先されます。このコミットでは、この点を明確にするために、
Order of evaluation
セクションの冒頭に以下の記述が追加されました。At package level, <a href="#Program_execution"</a>initialization dependencies</a> determine the evaluation order of individual initialization expressions in <a href="#Variable_declarations">variable declarations</a>. Otherwise, when evaluating the <a href="#Operands">operands</a> of an expression, assignment, or
これにより、パッケージレベルの初期化が特別なケースであることが明示されました。
-
既存の例の文脈の明確化: 既存の例 (
y[f()], ok = g(h(), i()+x[j()], <-c), k()
) について、「(function-local) assignment」という注釈が追加されました。これは、この例がパッケージレベルの初期化ではなく、関数内部での代入の評価順序を示していることを明確にするためです。 -
パッケージレベルの初期化の新しい例の追加: パッケージレベルの初期化における評価順序の具体的な動作を示す新しい例が追加されました。この例は、複数の変数宣言と関数呼び出しが絡む複雑な初期化において、初期化の依存関係がどのように評価順序を決定するかを示しています。
<p> At package level, initialization dependencies override the left-to-right rule for individual initialization expressions, but not for operands within each expression: </p> <pre> var a, b, c = f() + v(), g(), sqr(u()) + v() func f() int { return c } func g() int { return a } func sqr(x int) int { return x*x } // functions u and v are independent of all other variables and functions </pre> <p> The function calls happen in the order <code>u()</code>, <code>sqr()</code>, <code>v()</code>, <code>f()</code>, <code>v()</code>, and <code>g()</code>. </p>
この例は、
var a, b, c = f() + v(), g(), sqr(u()) + v()
という宣言において、f()
,g()
,sqr(u()) + v()
の各初期化式が、それぞれが依存する変数(a
,c
)の初期化が完了した後に評価されることを示しています。さらに、各初期化式内部では、引き続き左から右への評価ルールが適用されることも示唆しています。
これらの変更により、Go言語の評価順序に関する仕様書の記述がより正確になり、特にパッケージレベルの初期化における動作の理解が深まることが期待されます。
コアとなるコードの変更箇所
diff --git a/doc/go_spec.html b/doc/go_spec.html
index 496a7b2c3b..114ceed86f 100644
--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -3996,8 +3996,11 @@ precision.
<h3 id="Order_of_evaluation">Order of evaluation</h3>
<p>
-When evaluating the <a href="#Operands">operands</a> of an expression,
-<a href="#Assignments">assignment</a>, or
+At package level, <a href="#Program_execution"</a>initialization dependencies</a>
+determine the evaluation order of individual initialization expressions in
+<a href="#Variable_declarations">variable declarations</a>.
+Otherwise, when evaluating the <a href="#Operands">operands</a> of an
+expression, assignment, or
<a href="#Return_statements">return statement</a>,
all function calls, method calls, and
communication operations are evaluated in lexical left-to-right
@@ -4005,7 +4008,7 @@ order.
</p>
<p>
-For example, in the assignment
+For example, in the (function-local) assignment
</p>
<pre>
y[f()], ok = g(h(), i()+x[j()], <-c), k()
@@ -4022,11 +4025,33 @@ of <code>y</code> is not specified.\n <pre>\n a := 1\n f := func() int { a++; return a }\n-x := []int{a, f()} // x may be [1, 2] or [2, 2]: evaluation order between a and f() is not specified\n-m := map[int]int{a: 1, a: 2} // m may be {2: 1} or {2: 2}: evaluation order between the two map assignments is not specified\n-m2 := map[int]int{a: f()} // m2 may be {2: 3} or {3: 3}: evaluation order between the key and the value is not specified\n+x := []int{a, f()} // x may be [1, 2] or [2, 2]: evaluation order between a and f() is not specified\n+m := map[int]int{a: 1, a: 2} // m may be {2: 1} or {2: 2}: evaluation order between the two map assignments is not specified\n+n := map[int]int{a: f()} // n may be {2: 3} or {3: 3}: evaluation order between the key and the value is not specified\n </pre>\n \n+<p>\n+At package level, initialization dependencies override the left-to-right rule\n+for individual initialization expressions, but not for operands within each\n+expression: \n+</p>\n+\n+<pre>\n+var a, b, c = f() + v(), g(), sqr(u()) + v()\n+\n+func f() int { return c }\n+func g() int { return a }\n+func sqr(x int) int { return x*x }\n+\n+// functions u and v are independent of all other variables and functions\n+</pre>\n+\n+<p>\n+The function calls happen in the order\n+<code>u()</code>, <code>sqr()</code>, <code>v()</code>,\n+<code>f()</code>, <code>v()</code>, and <code>g()</code>.\n+</p>
コアとなるコードの解説
このコミットにおける主要な変更は、Go言語の仕様書 doc/go_spec.html
の Order of evaluation
セクションに集中しています。
-
評価順序の冒頭への追加:
When evaluating the operands of an expression, assignment, or return statement, all function calls, method calls, and communication operations are evaluated in lexical left-to-right order.
という既存の文の前に、以下の3行が追加されました。At package level, <a href="#Program_execution"</a>initialization dependencies</a> determine the evaluation order of individual initialization expressions in <a href="#Variable_declarations">variable declarations</a>. Otherwise, when evaluating the <a href="#Operands">operands</a> of an expression, assignment, or
この追加により、パッケージレベルでの初期化が、通常の式、代入、return文とは異なる評価順序のルール(初期化の依存関係に基づく)を持つことが明確に示されました。これにより、仕様の曖昧さが解消され、読者がパッケージレベルの初期化と関数内部の評価順序を混同する可能性が低減されます。
-
既存の例への注釈追加:
For example, in the assignment
という行がFor example, in the (function-local) assignment
に変更されました。これは、その後に続く複雑な代入の例が、パッケージレベルの初期化ではなく、関数スコープ内での代入の評価順序を示していることを明示するためです。これにより、読者は例の適用範囲を正しく理解できます。 -
新しいパッケージレベルの初期化の例の追加: 既存の評価順序の例の後に、パッケージレベルの初期化における評価順序を示す新しいコードブロックと説明が追加されました。
<p> At package level, initialization dependencies override the left-to-right rule for individual initialization expressions, but not for operands within each expression: </p> <pre> var a, b, c = f() + v(), g(), sqr(u()) + v() func f() int { return c } func g() int { return a } func sqr(x int) int { return x*x } // functions u and v are independent of all other variables and functions </pre> <p> The function calls happen in the order <code>u()</code>, <code>sqr()</code>, <code>v()</code>, <code>f()</code>, <code>v()</code>, and <code>g()</code>. </p>
この新しい例は、
var a, b, c = f() + v(), g(), sqr(u()) + v()
のようなパッケージレベルの変数宣言において、各初期化式(f() + v()
,g()
,sqr(u()) + v()
)が、それらが依存する他の変数(a
やc
)の初期化が完了した後に評価されることを具体的に示しています。さらに、各初期化式内部では、依然として「字句的に左から右へ」のルールが適用されることも示唆しています(例:sqr(u()) + v()
ではu()
がv()
の前に評価される)。この例は、パッケージレベルの初期化の複雑な動作を明確にし、仕様の理解を深める上で非常に重要です。
これらの変更は、Go言語の評価順序に関する仕様書の記述をより正確で包括的なものにし、開発者がGoプログラムの動作をより深く理解するのに役立ちます。
関連リンク
- Go言語の公式仕様書: https://golang.org/ref/spec
- Go CL (Code Review): https://golang.org/cl/96000044
- GitHub Issue #7137 (関連する可能性のあるIssue): https://github.com/golang/go/issues/7137
参考にした情報源リンク
- Go language spec evaluation order inconsistency #7137に関するWeb検索結果 (Google Search)
- Go言語の公式ドキュメントおよび仕様書
- Go言語の評価順序に関する一般的な解説記事 (例: go101.orgなど)