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

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

このコミットは、Go言語の仕様書ドラフトである doc/go_spec.txt の変更に関するものです。このファイルは、Go言語の構文、セマンティクス、および組み込み機能の詳細を定義しており、言語設計の基礎となる重要なドキュメントです。

コミット

commit 7354b864b52fc577aa18a3a289514bc2ce58d4de
Author: Robert Griesemer <gri@golang.org>
Date:   Thu Dec 4 17:33:37 2008 -0800

    Revised proposal for const/var cleanup, with
    Ken's suggestion for the "iota" extension to tuples.
    
    DELTA=171  (88 added, 57 deleted, 26 changed)
    OCL=20460
    CL=20544

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

https://github.com/golang/go/commit/7354b864b52fc577aa18a3a289514bc2ce58d4de

元コミット内容

このコミットは、Go言語における定数(const)と変数(var)の宣言に関する仕様の改訂提案を反映しています。特に、複数の定数や変数を一度に宣言する際の構文の整理("cleanup")と、定数ジェネレータである iota をタプル(複数の値)に拡張して適用するKen Thompson氏の提案が盛り込まれています。これにより、宣言の柔軟性と一貫性が向上し、特に列挙型のような定数群の定義がより簡潔になります。

変更の背景

この変更の背景には、Go言語の初期の仕様における定数および変数宣言のいくつかの不整合と、より表現力豊かな定数定義メカニズムへの要望がありました。コミットメッセージと差分から、以下の「Open issues」が解決されたことが示唆されます。

  • global var decls: "var a, b, c int = 0, 0, 0" is ok, but "var a, b, c = 0, 0, 0" is not (seems inconsistent with "var a = 0", and ":=" notation)
    • これは、型指定のない複数変数宣言が許可されていなかったことに対する不整合の指摘です。単一変数宣言や短縮変数宣言(:=)との一貫性が求められていました。
  • const decls: "const a, b = 1, 2" is not allowed - why not? Should be symmetric to vars.
    • これは、複数の定数を一度に宣言する構文が許可されていなかったことに対する指摘です。変数宣言と同様に、複数の定数をまとめて宣言できる対称性が求められていました。

これらの課題を解決し、言語の宣言構文をより一貫性があり、直感的に使えるようにすることが変更の主な動機です。特に iota の拡張は、C言語の enum のような連続した定数値を生成するGoらしい強力なメカニズムを提供することを目的としています。

前提知識の解説

このコミットの変更内容を理解するためには、以下のGo言語の基本的な概念を把握しておく必要があります。

  • 定数宣言 (const): Go言語では、const キーワードを使用して定数を宣言します。定数はコンパイル時に値が決定され、実行時に変更することはできません。
    const Pi = 3.14
    const Version = "1.0"
    
  • 変数宣言 (var): var キーワードを使用して変数を宣言します。変数は実行時に値を変更できます。
    var i int = 10
    var name string = "Go"
    
  • 短縮変数宣言 (:=): 関数内で変数を宣言し、初期化する際に使用できる簡潔な構文です。型推論が行われます。
    j := 20 // var j int = 20 と同等
    
  • iota: Go言語の特別な定数ジェネレータです。const ブロック内で使用され、連続する整数値を自動的に生成します。デフォルトでは0から始まり、const ブロック内の各定数宣言ごとに1ずつ増加します。
    const (
        A = iota // A = 0
        B = iota // B = 1
        C = iota // C = 2
    )
    
  • タプル代入 (Tuple Assignment): Go言語では、複数の変数に複数の値を一度に代入することができます。これは、関数の複数の戻り値を扱う際などによく使用されます。
    x, y := 1, 2 // xに1、yに2を代入
    

技術的詳細

このコミットにおける技術的な変更の核心は、Go言語の仕様書における定数と変数の宣言構文の定義を、より柔軟で一貫性のあるものに改訂した点にあります。特に、iota の動作が大幅に拡張され、タプル代入と組み合わせることで強力な定数生成が可能になりました。

  1. ConstSpec および VarSpec の統一と拡張:

    • 以前は、const 宣言は単一の識別子と単一の式しか受け付けませんでした (ConstSpec = identifier [ CompleteType ] [ "=" Expression ] .)。
    • 今回の変更により、ConstSpecVarSpec の両方で IdentifierListExpressionList を使用できるようになりました。
      ConstSpec = IdentifierList [ CompleteType ] [ "=" ExpressionList ] .
      VarSpec = IdentifierList ( CompleteType [ "=" ExpressionList ] | "=" ExpressionList ) .
      IdentifierList = identifier { "," identifier } .
      ExpressionList = Expression { "," Expression } .
      
    • これにより、const a, b = 1, 2var x, y float = 0, 3 のような複数定数/変数の一括宣言が可能になり、変数宣言との対称性が確保されました。
    • 型が省略された場合、定数/変数の型は対応する式の型から推論されます。型が指定された場合、すべての定数/変数はその型を持ち、式はその型に代入可能である必要があります。
  2. iota の動作の明確化と拡張:

    • iotaconst 宣言ブロック内でのみ有効となり、その動作がより厳密に定義されました。
    • リセットとインクリメント: iotaconst キーワードが出現するたびに0にリセットされ、各セミコロン(または暗黙のセミコロン、つまり改行)で1ずつインクリメントされます。
      const (            // iota は 0 にリセット
          enum0 = iota;  // enum0 = 0
          enum1 = iota;  // enum1 = 1
          enum2 = iota   // enum2 = 2
      )
      
      const x = iota;  // x = 0 (新しい const なので iota はリセット)
      const y = iota;  // y = 0 (新しい const なので iota はリセット)
      
    • ExpressionList 内での iota: 最も重要な変更点の一つは、ExpressionList(タプル代入の右辺)内で iota が使用された場合、その ExpressionList 内のすべての iota の値は同じになるという点です。iota はセミコロンでのみインクリメントされるため、単一のタプル代入内では値が固定されます。
      const (
          base0, mask0 int64 = 1 << iota, iota << iota - 1; // iota=0: base0=1, mask0=0
          base1, mask1 int64 = 1 << iota, iota << iota - 1; // iota=1: base1=2, mask1=1
          base2, mask2 int64 = 1 << iota, iota << iota - 1; // iota=2: base2=4, mask2=3
      )
      
      この例では、base0mask0 の計算において iota は両方とも0として評価されます。次の行の base1mask1 の計算では、iota は1として評価されます。
  3. 定数宣言における式の暗黙的な繰り返し (Implicit Repetition):

    • 括弧で囲まれた const 宣言リスト内で、最初の宣言以外の ExpressionList が省略された場合、それは直前の非空の ExpressionList のテキスト的な置換として扱われます。つまり、前の式のリストが暗黙的に繰り返されます。
    • この機能は iota と組み合わせることで、簡潔な列挙型定義を可能にします。
      const (
          enum0 = iota; // enum0 = 0
          enum1;        // enum1 = 1 (enum0 = iota が繰り返される)
          enum2         // enum2 = 2 (enum0 = iota が繰り返される)
      )
      
      const (
          a = 1 << iota; // a = 1 (iota は 0)
          b;             // b = 2 (1 << iota が繰り返され、iota は 1)
          c;             // c = 4 (1 << iota が繰り返され、iota は 2)
      )
      
  4. 短縮変数宣言 (:=) のタプル代入への拡張:

    • SimpleVarDecl = identifier ":=" Expression . から SimpleVarDecl = IdentifierList ":=" ExpressionList . へと変更されました。
    • これにより、i, j := 0, 10; のように、短縮変数宣言でも複数の変数に複数の値を一度に代入できるようになりました。

これらの変更は、Go言語の宣言構文をより強力で、一貫性があり、そして簡潔なものにすることを目的としています。特に iota の拡張は、Goにおける列挙型やビットフラグなどの定数群の定義において、非常にGoらしいイディオムを提供することになります。

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

このコミットは、Go言語の仕様書 doc/go_spec.txt のみを変更しています。以下に、主要な変更箇所の差分を示します。

1. ConstDecl の構文変更

--- a/doc/go_spec.txt
+++ b/doc/go_spec.txt
@@ -661,22 +660,40 @@ A constant declaration binds an identifier to the value of a constant
 expression (§Constant expressions).
 
 	ConstDecl = "const" Decl<ConstSpec> .
-	ConstSpec = identifier [ CompleteType ] [ "=" Expression ] .
+	ConstSpec = IdentifierList [ CompleteType ] [ "=" ExpressionList ] .
+
+	IdentifierList = identifier { "," identifier } .
+	ExpressionList = Expression { "," Expression } .

2. ConstDecl の説明と例の追加

--- a/doc/go_spec.txt
+++ b/doc/go_spec.txt
@@ -661,22 +660,40 @@ A constant declaration binds an identifier to the value of a constant
 expression (§Constant expressions).
 
 	ConstDecl = "const" Decl<ConstSpec> .
-	ConstSpec = identifier [ CompleteType ] [ "=" Expression ] .
+	ConstSpec = IdentifierList [ CompleteType ] [ "=" ExpressionList ] .
+
+	IdentifierList = identifier { "," identifier } .
+	ExpressionList = Expression { "," Expression } .
 
-	const pi float = 3.14159265
+A constant declaration binds a list of identifiers (the names of the constants)
+to the values of a list of constant expressions. The number of identifiers must 
+be equal to the number of expressions, with the i'th identifier on the left
+corresponding to the i'th expression on the right. If CompleteType is omitted,
+the types of the constants are the types of the corresponding expressions;
+different expressions may have different types. If CompleteType is present,
+the type of all constants is the type specified, and the types of all
+expressions in ExpressionList must be assignment-compatible with the
+constant type.
+
+	const pi float64 = 3.14159265358979323846
 	const e = 2.718281828
 	const (
-		one int = 1;
-		two = 3
+		size int64 = 1024;
+		eof = -1;
 	)
+	const a, b, c = 3, 4, "foo"  // a = 3, b = 4, c = "foo"
+	const u, v float = 0, 3      // u = 0.0, v = 3.0

3. 定数宣言における式の暗黙的な繰り返しに関する説明の追加

--- a/doc/go_spec.txt
+++ b/doc/go_spec.txt
@@ -679,12 +696,6 @@ values:
 		Partyday;
 	)
 
-The initializing expression of a constant may contain only other
-constants.  This is illegal:
-
-	var i int = 10;
-	const c = i;  // error
-
 The initializing expression for a numeric constant is evaluated
 using the principles described in the section on numeric literals:
 constants are mathematical values given a size only upon assignment

4. Iota セクションの追加と詳細な説明

Iota の定義が Operands セクションから Const declarations セクションに移動し、より詳細な動作が記述されました。

--- a/doc/go_spec.txt
+++ b/doc/go_spec.txt
@@ -717,18 +728,82 @@ yields a floating point constant of value 2.5 (1.5 + 1); its
 constituent expressions are evaluated using different rules for
 division.
 
-If the type is specified, the resulting constant has the named type.
-
-If the type is missing from the constant declaration, the constant
+If the type is missing from a numeric constant declaration, the constant
 represents a value of abitrary precision, either integer or floating
 point, determined by the type of the initializing expression. Such
 a constant may be assigned to any variable that can represent its
 value accurately, regardless of type.  For instance, 3 can be
-assigned to any int variable but also to any floating point variable,\n-while 1e12 can be assigned to a float32, float64, or even int64.\n-It is erroneous to assign a value with a non-zero fractional\n-part to an integer, or if the assignment would overflow or\n-underflow.\n+assigned to any integer variable but also to any floating point variable,\n+while 1e12 can be assigned to a "float32", "float64", or even "int64".\n+It is erroneous to assign a value with a non-zero fractional part\n+to an integer, or if the assignment would overflow or underflow.\n+\n+\n+Iota\n+----\n+\n+Within a constant declaration, the predeclared operand "iota" represents\n+successive elements of an integer sequence. It is reset to 0 whenever the\n+reserved word "const" appears in the source and increments with each\n+semicolon. For instance, "iota" can be used to construct a set of related\n+constants:\n+\n+\tconst (            // iota is set to 0\n+\t\tenum0 = iota;  // sets enum0 to 0, etc.\n+\t\tenum1 = iota;\n+\t\tenum2 = iota\n+\t)\n+\n+\tconst (\n+\t\ta = 1 << iota;  // a == 1 (iota has been reset)\n+\t\tb = 1 << iota;  // b == 2\n+\t\tc = 1 << iota;  // c == 4\n+\t)\n+\n+\tconst (\n+\t\tu       = iota * 42;  // u == 0     (ideal integer)\n+\t\tv float = iota * 42;  // v == 42.0  (float)\n+\t\tw       = iota * 42;  // w == 84    (ideal integer)\n+\t)\n+\n+\tconst x = iota;  // x == 0 (iota has been reset)\n+\tconst y = iota;  // y == 0 (iota has been reset)\n+\n+Within an ExpressionList, the value of all "iota"'s is the same because "iota"\n+is only incremented at each semicolon:\n+\n+\tconst (\n+\t\tbase0, mask0 int64 = 1 << iota, iota << iota - 1;  // base0 == 1, mask0 = 0\n+\t\tbase1, mask1 int64 = 1 << iota, iota << iota - 1;  // base1 == 2, mask1 = 1\n+\t\tbase2, mask2 int64 = 1 << iota, iota << iota - 1;  // base2 == 4, mask2 = 3\n+\t)\n+\n+Since the ExpressionList in constant declarations repeats implicitly\n+if omitted, some of the examples above can be abbreviated:\n+\n+\tconst (\n+\t\tenum0 = iota;\n+\t\tenum1;\n+\t\tenum2\n+\t)\n+\n+\tconst (\n+\t\ta = 1 << iota;\n+\t\tb;\n+\t\tc;\n+\t)\n+\n+\tconst (\n+\t\tu = iota * 42;\n+\t\tv float;\n+\t\tw;\n+\t)\n+\n+\tconst (\n+\t\tbase0, mask0 int64 = 1 << iota, iota << iota - 1;\n+\t\tbase1, mask1 int64;\n+\t\tbase2, mask2 int64;\n+\t)\n```

### 5. 短縮変数宣言 (`SimpleVarDecl`) の構文変更

```diff
--- a/doc/go_spec.txt
+++ b/doc/go_spec.txt
@@ -803,13 +875,13 @@ of the variable is "int" or "float" respectively:
 
 The syntax
 
-	SimpleVarDecl = identifier ":=" Expression .
+	SimpleVarDecl = IdentifierList ":=" ExpressionList .
 
 is shorthand for
 
-	var identifier = Expression.\n+	"var" ExpressionList = ExpressionList .\n \n-\ti := 0\n+\ti, j := 0, 10;\n \tf := func() int { return 7; }\n \tch := new(chan int);\n \t

コアとなるコードの解説

上記の差分は、Go言語の定数と変数宣言の柔軟性と表現力を大幅に向上させるための重要な変更を示しています。

  1. ConstDecl の構文変更:

    • 以前は単一の定数しか宣言できませんでしたが、IdentifierListExpressionList の導入により、const a, b, c = 1, 2, 3 のように複数の定数を一度に宣言できるようになりました。これは、変数宣言の構文との一貫性を高めるものです。
    • IdentifierList はカンマ区切りの識別子のリストを、ExpressionList はカンマ区切りの式のリストを意味します。これにより、タプル代入の概念が定数宣言にも適用されるようになりました。
  2. ConstDecl の説明と例の追加:

    • 新しい説明では、識別子のリストが定数式のリストにどのようにバインドされるかが明確にされています。識別子の数と式の数は一致する必要があり、i番目の識別子がi番目の式に対応します。
    • 型が省略された場合の型推論のルール(各式の型が定数の型になる)と、型が明示された場合の型チェックのルール(すべての定数が指定された型になり、式はその型に代入可能である必要がある)が記述されています。
    • const a, b, c = 3, 4, "foo"const u, v float = 0, 3 のような新しい例は、複数定数宣言の具体的な使用法を示しています。特に後者の例では、型が明示されているため、03float 型として扱われることがわかります。
  3. 定数宣言における式の暗黙的な繰り返しに関する説明の追加:

    • このセクションは、括弧で囲まれた const ブロック内で、ExpressionList が省略された場合に、直前の ExpressionList が自動的に繰り返されるという強力な機能について説明しています。
    • この機能は、特に iota と組み合わせることで、連続した定数値を簡潔に定義するのに役立ちます。例えば、const (A = iota; B; C) と書くことで、B = iotaC = iota が暗黙的に補完され、iota の値が自動的にインクリメントされます。
  4. Iota セクションの追加と詳細な説明:

    • iota の定義が独立したセクションとして設けられ、その動作がより詳細かつ明確に記述されました。
    • iotaconst キーワードの出現時に0にリセットされ、各セミコロン(または改行)でインクリメントされるというルールが強調されています。
    • 最も重要なのは、「ExpressionList 内では、すべての iota の値は同じである」というルールです。これは、iota がセミコロンでのみインクリメントされるため、単一のタプル代入内では iota の値が固定されることを意味します。これにより、base0, mask0 int64 = 1 << iota, iota << iota - 1 のような複雑な定数定義が可能になります。
    • 暗黙的な繰り返しと iota を組み合わせた簡潔な列挙型の例が多数追加され、その強力な表現力が示されています。
  5. 短縮変数宣言 (SimpleVarDecl) の構文変更:

    • i := 0 のような単一の短縮変数宣言だけでなく、i, j := 0, 10; のように複数の変数に複数の値を一度に代入するタプル代入が := 構文でも可能になりました。これにより、変数宣言のすべての形式でタプル代入がサポートされ、一貫性が向上しました。

これらの変更は、Go言語の定数と変数宣言の構文をより洗練させ、特に iota を用いた定数定義のイディオムを確立する上で極めて重要な役割を果たしました。

関連リンク

参考にした情報源リンク

  • コミット情報: /home/orange/Project/comemo/commit_data/1283.txt
  • Go言語の仕様に関する一般的な知識。
  • Go言語の constiota に関する一般的な知識。