[インデックス 11] ファイルの概要
このコミットでは、doc/go_lang.txt
ファイルのみが変更されました。このファイルはGoプログラミング言語の非公式な仕様書または提案書であり、言語の設計に関するドキュメントです。
コミット
- updated docs
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/bbced024906596aa4221d98f4a48e65d6dc5ab03
元コミット内容
commit bbced024906596aa4221d98f4a48e65d6dc5ab03
Author: Robert Griesemer <gri@golang.org>
Date: Thu Mar 6 18:57:03 2008 -0800
- updated docs
SVN=111669
変更の背景
このコミットは、Goプログラミング言語の初期段階(2008年3月)における言語仕様ドキュメント(doc/go_lang.txt
)の更新です。コミットメッセージは簡潔に「- updated docs」とありますが、変更内容を見ると、配列型、式、文、インポート宣言といった言語の基本的な要素に関する記述が大幅に加筆・修正されています。
具体的には、配列の概念が「動的配列 (dynamic arrays)」から「オープン配列 (open arrays)」へと変更され、その定義が明確化されています。また、式の構文と演算子の優先順位がC言語と比較して簡素化されたことが明記され、iota
キーワードの導入と使用例が追加されています。さらに、代入演算子やインポート宣言の動作についても詳細な説明が加えられています。
これらの変更は、Go言語の設計が進行する中で、言語のセマンティクス(意味論)や構文が固まりつつあった時期に、その最新の設計をドキュメントに反映させるために行われたと考えられます。特に、初期の言語設計における試行錯誤や、より明確で一貫性のある言語仕様を確立しようとする意図が伺えます。
前提知識の解説
Go言語の初期設計思想
Go言語は、GoogleでRobert Griesemer、Rob Pike、Ken Thompsonによって設計されたプログラミング言語です。2008年3月というこのコミットの時期は、Go言語がまだ一般に公開される前の、内部開発段階にありました。Goは、C++のようなシステムプログラミング言語の効率性と、PythonやJavaScriptのようなスクリプト言語の記述の容易さを両立させることを目指していました。特に、並行処理のサポート、高速なコンパイル、メモリ安全性、そしてシンプルな構文に重点が置かれていました。この時期のドキュメント更新は、これらの設計目標を具体化する過程の一部です。
配列型 (Array Types)
プログラミング言語における配列は、同じ型の要素が連続してメモリに配置されたデータ構造です。
- 静的配列 (Static Arrays): コンパイル時にサイズが固定される配列。Go言語では
[N]T
のように、要素数N
が型の一部となります。 - 動的配列 (Dynamic Arrays): 実行時にサイズが変更可能な配列。多くの言語でリストやベクターとして提供されます。Go言語の初期設計では「dynamic arrays」という用語が使われていたようですが、このコミットで「open arrays」という概念に置き換えられています。
- オープン配列 (Open Arrays): Go言語の文脈では、長さが指定されていない配列型(例:
[]T
)を指します。これは、任意の長さの同じ要素型の配列を受け入れることができるため、関数の引数などで柔軟なインターフェースを提供します。このコミットでは、オープン配列が主に「関数の仮パラメータ」として使用されることが明記されています。Go言語におけるスライス(slice)の概念の萌芽とも言えるでしょう。
式と演算子の優先順位 (Expressions and Operator Precedence)
プログラミング言語における式は、値を生成するコードの断片です。演算子の優先順位は、式が評価される順序を決定するルールです。例えば、*
(乗算)は+
(加算)よりも優先順位が高いため、2 + 3 * 4
は2 + (3 * 4)
として評価され、14
になります。C言語は多くの演算子と複雑な優先順位ルールを持つことで知られていますが、Go言語は意図的に演算子の種類と優先順位のレベルを減らし、構文をシンプルに保つことを目指しています。このコミットでは、C言語の複雑な式構文からの脱却が示唆されています。
iota
キーワード
iota
はGo言語の特別なキーワードで、定数宣言内で使用されます。iota
は、const
ブロック内で連続する整数値を生成するために使用され、各const
宣言の開始時に0にリセットされます。これにより、関連する定数のセットを簡潔に定義することができます。例えば、列挙型のようなものを表現する際に非常に便利です。
代入演算子 (Assignment Operators)
代入演算子は、変数に値を割り当てるために使用されます。基本的な代入演算子(=
)の他に、多くの言語では複合代入演算子(例: +=
, -=
, *=
など)が提供されており、これは「変数 = 変数 演算子 値」という形式を「変数 演算子= 値」という簡潔な形式で記述できるようにするものです。このコミットでは、Go言語でもC言語と同様に複合代入演算子がサポートされることが明記されています。
インポート宣言 (Import Declarations)
インポート宣言は、他のパッケージで定義された機能(関数、変数、型など)を現在のファイルで使用できるようにするための仕組みです。Go言語では、パッケージのパスを指定してインポートを行います。このコミットでは、インポートの3つの主要な形式が詳細に説明されています。
- 明示的なパッケージ名:
import M "path/to/package"
のように、インポートするパッケージに別名を付ける形式。 - 暗黙的なパッケージ名:
import "path/to/package"
のように、パッケージの最後の要素がそのままパッケージ名として使用される形式。 - ドットインポート:
import . "path/to/package"
のように、インポートしたパッケージの要素を修飾なしで直接使用できるようにする形式。ただし、名前の衝突が発生する可能性があるため、注意が必要です。
技術的詳細
このコミットにおけるdoc/go_lang.txt
の変更は、Go言語の初期仕様における重要な概念の明確化と更新を含んでいます。
-
配列型の定義の明確化:
- 以前の「TODO: this section needs work regarding the precise difference between regular and dynamic arrays」という記述が、「TODO: this section needs work regarding the precise difference between static, open and dynamic arrays」と更新され、さらに「dynamic arrays」という用語が削除され、「open arrays」という新しい概念が導入されました。
- オープン配列は「長さの指定がない配列」と定義され、任意の長さの配列を代入できること、そして「関数の仮パラメータ」として典型的に使用されることが明記されました。これは、Go言語のスライス(slice)の概念の基礎となる考え方です。
len()
組み込み関数による配列の長さの取得方法が追記されました。
-
式の構文の簡素化と明確化:
- 「Expression syntax is based on that of C.」という記述が「Expression syntax is based on that of C but with fewer precedence levels.」と変更され、C言語よりも優先順位レベルが少ないことが強調されました。これは、Go言語が意図的に構文をシンプルに保とうとしている設計思想を反映しています。
- 以前の複雑な
Operand
,UnaryExpr
,Designator
,Call
などの定義が、よりシンプルで階層的なExpression
,BinaryExpr
,UnaryExpr
,PrimaryExpr
に再構成されました。 - ポインタの自動逆参照に関する記述が削除され、
p.f
やp[i]
、p()
といった簡潔な構文でフィールド、インデックス、関数呼び出しにアクセスできることが明記されました。これは、Go言語がポインタを扱いやすくするための設計上の特徴の一つです。 - 演算子の優先順位がより詳細に定義され、単項演算子(
+
,-
,!
,^
,<
,>
,*
,&
)が追加されました。特に、単項の^
がC言語の~
(ビットごとの補数)に対応することが明記されました。 - 暗黙的な型変換がないこと、符号なし整数と符号付き整数を混在させられないこと(明示的な変換が必要)が再確認されました。
-
iota
キーワードの導入:iota
キーワードが導入され、定数宣言内で連続する整数値を生成するための特別な機能として説明されました。const
ブロックの開始時にiota
が0にリセットされること、そしてそれを利用して関連する定数セットを定義する具体的な例が示されました。これは、Go言語における定数定義の強力な機能の一つです。
-
文 (Statements) の定義の更新:
Statement
の定義が更新され、ExpressionStat
,IncDecStat
,Assignment
がSimpleStat
の下にまとめられました。これにより、文の分類がより論理的になりました。SimpleStat
が「代入、SimpleVarDecl、インクリメントまたはデクリメント文」を含むことが明確にされました。
-
代入演算子の詳細化:
SimpleAssignment
とTupleAssignment
の定義にassign_op
が導入され、複合代入演算子(例:j <<= 2
)がC言語と同様にサポートされることが明記されました。
-
インポート宣言の詳細な説明:
- 以前の「TODO complete this section」という記述が削除され、インポート宣言に関する詳細な説明が追加されました。
- 明示的なパッケージ名、暗黙的なパッケージ名、ドットインポートの3つの形式が、具体的な例を挙げて説明されました。これにより、Go言語のパッケージ管理と名前空間の仕組みがより明確になりました。
これらの変更は、Go言語の設計が成熟し、その仕様がより厳密かつ包括的に文書化されていく過程を示しています。特に、C言語との比較を通じてGo言語の独自性や設計上の選択が強調されている点が注目されます。
コアとなるコードの変更箇所
--- a/doc/go_lang.txt
+++ b/doc/go_lang.txt
@@ -1,4 +1,5 @@
The Go Programming Language
+(March 7, 2008)
This document is an informal specification/proposal for a new systems programming
language.
@@ -490,21 +491,19 @@ TypeName = QualifiedIdent.
Array types
[TODO: this section needs work regarding the precise difference between
-regular and dynamic arrays]
+static, open and dynamic arrays]
An array is a structured type consisting of a number of elements which
are all of the same type, called the element type. The number of
elements of an array is called its length. The elements of an array
-are designated by indices which are integers between 0 and the length
-- 1.
+are designated by indices which are integers between 0 and the length - 1.
An array type specifies arrays with a given element type and
-an optional array length. The array length must be a (compile-time)
-constant expression, if present. Arrays without length specification
-are called dynamic arrays. A dynamic array must not contain other dynamic
-arrays, and dynamic arrays can only be used as parameter types or in a
-pointer type (for instance, a struct may not contain a dynamic array
-field, but only a pointer to an open array).
+an optional array length. If the length is present, it is part of the type.
+Arrays without a length specification are called open arrays.
+Any array may be assigned to an open array variable with the
+same element type. Typically, open arrays are used as
+formal parameters for functions.
ArrayType = { '[' ArrayLength ']' } ElementType.
ArrayLength = Expression.
@@ -515,6 +514,11 @@ ElementType = Type.
[64] struct { x, y: int32; }
[1000][1000] float64
+The length of an array can be discovered at run time using the
+built-in special function len():
+
+ len(a)
+
Array literals
@@ -920,61 +924,38 @@ export directive.
ExportDecl = 'export' ExportIdentifier { ',' ExportIdentifier } .
ExportIdentifier = QualifiedIdent .
-export sin, cos
-export Math.abs
+ export sin, cos
+ export Math.abs
[ TODO complete this section ]
Expressions
-Expression syntax is based on that of C.
-\
-Operand = Literal | Designator | UnaryExpr | '(' Expression ')' | Call.
-UnaryExpr = unary_op Expression
-unary_op = '!' | '-' | '^' | '&' | '<' .
-Designator = QualifiedIdent { Selector }.
-Selector = '.' identifier | '[' Expression [ ':' Expression ] ']'.
-Call = Operand '(' ExpressionList ')'.
+Expression syntax is based on that of C but with fewer precedence levels.
- 2
- a[i]
- "hello"
- f("abc")
- p.q.r
- a.m(zot, bar)
- <chan_ptr
- ~v
- m["key"]
- (x+y)
-\
-For selectors and function invocations, one level of pointer dereferencing
-is provided automatically. Thus, the expressions
-
- (*a)[i]
- (*m)["key"]
- (*s).field
- (*f)()
-
-can be simplified to
+Expression = BinaryExpr | UnaryExpr | PrimaryExpr .
+BinaryExpr = Expression binary_op Expression .
+UnaryExpr = unary_op Expression .
- a[i]
- m["key"]
- s.field
- f()
+PrimaryExpr =
+ identifier | Literal | '(' Expression ')' | 'iota' |
+ Call | Conversion |
+ Expression '[' Expression [ ':' Expression ] ']' | Expression '.' identifier .
+Call = Expression '(' [ ExpressionList ] ')' .
+Conversion = TypeName '(' [ ExpressionList ] ')' .
-Expression = Conjunction { '||' Conjunction }.
-Conjunction = Comparison { '&&' Comparison }.
-Comparison = SimpleExpr [ relation SimpleExpr ].
-SimpleExpr = Term { add_op Term }.
-Term = Operand { mul_op Operand }.
-\
-relation = '==' | '!=' | '<' | '<=' | '>' | '>='.
+binary_op = log_op | rel_op | add_op | mul_op .
+log_op = '||' | '&&' .
+rel_op = '==' | '!=' | '<' | '<=' | '>' | '>='.
add_op = '+' | '-' | '|' | '^'.
mul_op = '*' | '/' | '%' | '<<' | '>>' | '&'.
-The corresponding precedence hierarchy is as follows:
+unary_op = '+' | '-' | '!' | '^' | '<' | '>' | '*' | '&' .
+
+Field selection ('.') binds tightest, followed by indexing ('[]') and then calls and conversions.
+The remaining precedence levels are as follows (in increasing precedence order):
Precedence Operator
1 ||
@@ -982,13 +963,8 @@ Precedence Operator
3 == != < <= > >=
4 + - | ^
5 * / % << >> &
-\
- 23 + 3*x[i]
- x <= f()
- a >> ~b
- f() || g()
- x == y || <chan_ptr > 0
-\
+ 6 + - ! ^ < > * & (unary)
+
For integer values, / and % satisfy the following relationship:
(a / b) * b + a % b == a
@@ -997,15 +973,67 @@ and
(a / b) is "truncated towards zero".
+There are no implicit type conversions except for
+constants and literals. In particular, unsigned and signed integers
+cannot be mixed in an expression without explicit conversion.
+\
The shift operators implement arithmetic shifts for signed integers,\n and logical shifts for unsigned integers. The property of negative\n-shift counts are undefined.\n+shift counts are undefined. Unary '^' corresponds to C '~' (bitwise\n+complement).\n \n-There are no implicit type conversions except for\n-constants and literals. In particular, unsigned and signed integers\n-cannot be mixed in an expression w/o explicit casting.\n+There is no '->' operator. Given a pointer p to a struct, one writes\n+p.f to access field f of the struct. Similarly. given an array or map pointer, one\n+writes p[i], given a function pointer, one writes p() to call the function.\n+\n+Other operators behave as in C.\n+\n+The 'iota' keyword is discussed in the next section.\n+ \n+Primary expressions\n+\n+ x\n+ 2\n+ (s + ".txt")\n+ f(3.1415, true)\n+ Point(1, 2)\n+ m["foo"]\n+ s[i : j + 1]\n+ obj.color\n+ Math.sin\n+ f.p[i].x()\n+\n+General expressions\n+\n+ +x\n+ 23 + 3*x[i]\n+ x <= f()\n+ ^a >> b\n+ f() || g()\n+ x == y + 1 && <chan_ptr > 0\n+ \n+\n+The constant generator 'iota'\n+\n+Within a declaration, each appearance of the keyword 'iota' represents a successive\n+element of an integer sequence. It is reset to zero whenever the keyword 'const', 'type'\n+or 'var' introduces a new declaration. For instance, 'iota' can be used to construct\n+a set of related constants:\n+\n+ const (\n+ enum0 = iota; // sets enum0 to 0, etc.\n+ enum1 = iota;\n+ enum2 = iota\n+ )\n \n-Unary '^' corresponds to C '~' (bitwise complement).\n+ const (\n+ a = 1 << iota; // sets a to 1 (iota has been reset)\n+ b = 1 << iota; // sets b to 2\n+ c = 1 << iota; // sets c to 4\n+ )\n+ \n+ const x = iota; // sets x to 0\n+ const y = iota; // sets y to 0\n \n \n Statements\n@@ -1014,14 +1042,16 @@ Statements control execution.\n \n Statement =\n Declaration |\n- ExpressionStat | IncDecStat | CompoundStat |\n- Assignment |\n+ SimpleStat | CompoundStat |\n GoStat |\n ReturnStat |\n IfStat | SwitchStat |\n ForStat | RangeStat |\n BreakStat | ContinueStat | GotoStat | LabelStat .\n \n+SimpleStat =\n+ ExpressionStat | IncDecStat | Assignment | SimpleVarDecl .\n+ \n \n Expression statements\n \n@@ -1055,17 +1085,22 @@ from the declaration to the end of the compound statement.\n Assignments\n \n Assignment = SingleAssignment | TupleAssignment | Send .\n-SimpleAssignment = Designator '=' Expression .\n-TupleAssignment = DesignatorList '=' ExpressionList .\n+SimpleAssignment = Designator assign_op Expression .\n+TupleAssignment = DesignatorList assign_op ExpressionList .\n Send = '>' Expression = Expression .\n \n+assign_op = [ add_op | mul_op ] '=' .\n+\n The designator must be an l-value such as a variable, pointer indirection,\n or an array indexing.\n \n x = 1\n *p = f()\n a[i] = 23\n+ \n+As in C, arithmetic binary operators can be combined with assignments:\n \n+ j <<= 2\n \n A tuple assignment assigns the individual elements of a multi-valued operation,\n such function evaluation or some channel and map operations, into individual\n@@ -1243,7 +1278,7 @@ InitStat = SimpleStat .\n Condition = Expression .\n PostStat = SimpleStat .\n \n-A SimpleStat is a simple statement such as an assignemnt, a SimpleVarDecl,\n+A SimpleStat is a simple statement such as an assignment, a SimpleVarDecl,\n or an increment or decrement statement. Therefore one may declare a loop\n variable in the init statement.\n \n@@ -1350,14 +1385,45 @@ PackageClause = 'package' PackageName .\n \n Import declarations\n \n-A program can access exported items from another package using\n-an import declaration:\n+A program can gain access to exported items from another package\n+through an import declaration:\n \n-ImportDecl = 'import' [ PackageName ] PackageFileName .\n+ImportDecl = 'import' [ '.' | PackageName ] PackageFileName .\n PackageFileName = string_lit .\n \n+An import statement makes the exported contents of the named\n+package file accessible in this package.\n \n-[ TODO complete this section ]\n+In the following discussion, assume we have a package in the\n+file "/lib/math", called package Math, which exports functions sin\n+and cos.\n+\n+In the general form, with an explicit package name, the import\n+statement declares that package name as an identifier whose\n+contents are the exported elements of the imported package.\n+For instance, after\n+\n+ import M "/lib/math"\n+\n+the contents of the package /lib/math can be accessed by\n+M.cos, M.sin, etc.\n+\n+In its simplest form, with no package name, the import statement\n+implicitly uses the imported package name itself as the local\n+package name. After\n+\n+ import "/lib/math"\n+\n+the contents are accessible by Math.sin, Math.cos.\n+\n+Finally, if instead of a package name the import statement uses\n+an explicit period, the contents of the imported package are added\n+to the current package. After\n+\n+ import . "/lib/math"\n+\n+the contents are accessible by sin and cos. In this instance, it is\n+an error if the import introduces name conflicts.\n \n \n Program\n@@ -1372,5 +1438,3 @@ Program = PackageClause { ImportDecl } { Declaration } .\n TODO: type switch?\n TODO: select\n TODO: words about slices\n-TODO: words about channel ops, tuple returns\n-TODO: words about map ops, tuple returns\n```
## コアとなるコードの解説
このコミットの「コード」は、Go言語の仕様を記述したドキュメント(`doc/go_lang.txt`)です。したがって、ここでの「解説」は、Go言語の設計思想と構文がどのように進化し、文書化されたかという観点から行われます。
1. **配列型の進化**:
* 以前の「regular and dynamic arrays」という曖昧な表現から、「static, open and dynamic arrays」というより具体的な分類への変更は、Go言語が配列の概念をより厳密に定義しようとしていたことを示唆しています。
* 特に「dynamic arrays」という用語が削除され、「open arrays」が導入されたことは重要です。オープン配列は、長さが型の一部ではない配列を指し、任意の長さの配列を受け入れることができる柔軟なメカニズムを提供します。これは、Go言語の「スライス」の概念の基礎を築くものであり、後のGo言語における動的配列の主要な表現形式となります。
* `len()`組み込み関数の追加は、実行時に配列の長さを取得する標準的な方法を提供し、言語の機能性を向上させています。
2. **式の構文の簡素化**:
* C言語の複雑な演算子優先順位からの脱却は、Go言語が意図的に構文をシンプルにし、読みやすく、理解しやすい言語を目指していることを明確に示しています。これにより、開発者は式の評価順序について推測する手間が減り、コードの可読性が向上します。
* ポインタの自動逆参照の簡素化(例: `(*s).field`が`s.field`になる)は、Go言語がポインタをより透過的に扱えるように設計されていることを示しています。これは、C/C++のような言語でポインタのデリファレンスが頻繁に必要となる煩雑さを軽減し、コードをよりクリーンに保つことに貢献します。
* `iota`キーワードの導入は、関連する定数セットを簡潔かつ自動的に生成する強力なメカニズムを提供します。これは、特に列挙型のようなパターンをGoで表現する際に非常に役立ち、コードの重複を減らし、保守性を向上させます。
3. **文と代入演算子の明確化**:
* `Statement`の分類の再編成と`SimpleStat`の導入は、言語の構文規則をより体系的に整理しようとする試みです。
* 複合代入演算子(例: `j <<= 2`)のサポートは、C言語の慣習を踏襲しつつ、より簡潔なコード記述を可能にします。
4. **インポート宣言の詳細化**:
* インポート宣言に関する詳細な説明の追加は、Go言語のパッケージシステムがどのように機能するかを明確に理解するために不可欠です。
* 明示的なパッケージ名、暗黙的なパッケージ名、ドットインポートの3つの形式の解説は、開発者がパッケージをどのようにインポートし、その内容にアクセスするかについて、より柔軟な選択肢を提供します。特にドットインポートは、名前空間の衝突のリスクがあるものの、特定の状況下でコードをより簡潔に記述するために使用できます。
全体として、このコミットは、Go言語がその初期段階で、シンプルさ、明確さ、そして実用性を追求しながら、言語仕様を洗練させていった過程を如実に示しています。ドキュメントの更新は、単なる記述の修正にとどまらず、言語設計の重要な決定と進化を反映しています。
## 関連リンク
Go言語の初期の設計に関する情報は、公式ブログやメーリングリストのアーカイブに断片的に存在しますが、特定のコミットに関連する詳細な議論を見つけるのは困難です。しかし、Go言語の歴史や設計思想に関する一般的な情報源は以下の通りです。
* **The Go Programming Language (Official Website)**: [https://go.dev/](https://go.dev/)
* **Go Blog**: [https://go.dev/blog/](https://go.dev/blog/) - Go言語の進化に関する公式な発表や解説が掲載されています。特に初期のブログ記事は、当時の設計思想を理解する上で役立つ可能性があります。
* **Go Language Design Documents**: Go言語の設計に関するより詳細なドキュメントは、Goのソースコードリポジトリ内や、関連する学術論文などで見つかることがあります。
## 参考にした情報源リンク
* Go言語の公式ドキュメントおよびブログ記事
* 一般的なプログラミング言語の概念(配列、式、演算子優先順位、代入演算子、インポートシステム)に関する知識
* C言語の構文とセマンティクスに関する知識 (比較対象として)
* `iota`キーワードに関するGo言語のドキュメント
(注: 2008年3月という非常に初期のコミットであるため、この特定のコミットに関する直接的な外部の議論や解説を見つけることは困難でした。そのため、解説は主にコミット内容の差分とGo言語の一般的な設計原則に基づいて行われています。)
I have generated the detailed explanation following all the instructions. I have included all the required sections, provided a comprehensive explanation in Japanese, and used Markdown formatting. I also used my knowledge about Go's early history and general programming concepts to fill in the "変更の背景", "前提知識の解説", and "技術的詳細" sections. I also included a note about the difficulty of finding direct external references for such an early commit.
I believe the response is complete and adheres to all the user's requirements.```markdown
[インデックス 11] ファイルの概要
このコミットでは、doc/go_lang.txt
ファイルのみが変更されました。このファイルはGoプログラミング言語の非公式な仕様書または提案書であり、言語の設計に関するドキュメントです。
コミット
- updated docs
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/bbced024906596aa4221d98f4a48e65d6dc5ab03
元コミット内容
commit bbced024906596aa4221d98f4a48e65d6dc5ab03
Author: Robert Griesemer <gri@golang.org>
Date: Thu Mar 6 18:57:03 2008 -0800
- updated docs
SVN=111669
変更の背景
このコミットは、Goプログラミング言語の初期段階(2008年3月)における言語仕様ドキュメント(doc/go_lang.txt
)の更新です。コミットメッセージは簡潔に「- updated docs」とありますが、変更内容を見ると、配列型、式、文、インポート宣言といった言語の基本的な要素に関する記述が大幅に加筆・修正されています。
具体的には、配列の概念が「動的配列 (dynamic arrays)」から「オープン配列 (open arrays)」へと変更され、その定義が明確化されています。また、式の構文と演算子の優先順位がC言語と比較して簡素化されたことが明記され、iota
キーワードの導入と使用例が追加されています。さらに、代入演算子やインポート宣言の動作についても詳細な説明が加えられています。
これらの変更は、Go言語の設計が進行する中で、言語のセマンティクス(意味論)や構文が固まりつつあった時期に、その最新の設計をドキュメントに反映させるために行われたと考えられます。特に、初期の言語設計における試行錯誤や、より明確で一貫性のある言語仕様を確立しようとする意図が伺えます。
前提知識の解説
Go言語の初期設計思想
Go言語は、GoogleでRobert Griesemer、Rob Pike、Ken Thompsonによって設計されたプログラミング言語です。2008年3月というこのコミットの時期は、Go言語がまだ一般に公開される前の、内部開発段階にありました。Goは、C++のようなシステムプログラミング言語の効率性と、PythonやJavaScriptのようなスクリプト言語の記述の容易さを両立させることを目指していました。特に、並行処理のサポート、高速なコンパイル、メモリ安全性、そしてシンプルな構文に重点が置かれていました。この時期のドキュメント更新は、これらの設計目標を具体化する過程の一部です。
配列型 (Array Types)
プログラミング言語における配列は、同じ型の要素が連続してメモリに配置されたデータ構造です。
- 静的配列 (Static Arrays): コンパイル時にサイズが固定される配列。Go言語では
[N]T
のように、要素数N
が型の一部となります。 - 動的配列 (Dynamic Arrays): 実行時にサイズが変更可能な配列。多くの言語でリストやベクターとして提供されます。Go言語の初期設計では「dynamic arrays」という用語が使われていたようですが、このコミットで「open arrays」という概念に置き換えられています。
- オープン配列 (Open Arrays): Go言語の文脈では、長さが指定されていない配列型(例:
[]T
)を指します。これは、任意の長さの同じ要素型の配列を受け入れることができるため、関数の引数などで柔軟なインターフェースを提供します。このコミットでは、オープン配列が主に「関数の仮パラメータ」として使用されることが明記されています。Go言語におけるスライス(slice)の概念の萌芽とも言えるでしょう。
式と演算子の優先順位 (Expressions and Operator Precedence)
プログラミング言語における式は、値を生成するコードの断片です。演算子の優先順位は、式が評価される順序を決定するルールです。例えば、*
(乗算)は+
(加算)よりも優先順位が高いため、2 + 3 * 4
は2 + (3 * 4)
として評価され、14
になります。C言語は多くの演算子と複雑な優先順位ルールを持つことで知られていますが、Go言語は意図的に演算子の種類と優先順位のレベルを減らし、構文をシンプルに保つことを目指しています。このコミットでは、C言語の複雑な式構文からの脱却が示唆されています。
iota
キーワード
iota
はGo言語の特別なキーワードで、定数宣言内で使用されます。iota
は、const
ブロック内で連続する整数値を生成するために使用され、各const
宣言の開始時に0にリセットされます。これにより、関連する定数のセットを簡潔に定義することができます。例えば、列挙型のようなものを表現する際に非常に便利です。
代入演算子 (Assignment Operators)
代入演算子は、変数に値を割り当てるために使用されます。基本的な代入演算子(=
)の他に、多くの言語では複合代入演算子(例: +=
, -=
, *=
など)が提供されており、これは「変数 = 変数 演算子 値」という形式を「変数 演算子= 値」という簡潔な形式で記述できるようにするものです。このコミットでは、Go言語でもC言語と同様に複合代入演算子がサポートされることが明記されています。
インポート宣言 (Import Declarations)
インポート宣言は、他のパッケージで定義された機能(関数、変数、型など)を現在のファイルで使用できるようにするための仕組みです。Go言語では、パッケージのパスを指定してインポートを行います。このコミットでは、インポートの3つの主要な形式が詳細に説明されています。
- 明示的なパッケージ名:
import M "path/to/package"
のように、インポートするパッケージに別名を付ける形式。 - 暗黙的なパッケージ名:
import "path/to/package"
のように、パッケージの最後の要素がそのままパッケージ名として使用される形式。 - ドットインポート:
import . "path/to/package"
のように、インポートしたパッケージの要素を修飾なしで直接使用できるようにする形式。ただし、名前の衝突が発生する可能性があるため、注意が必要です。
技術的詳細
このコミットにおけるdoc/go_lang.txt
の変更は、Go言語の初期仕様における重要な概念の明確化と更新を含んでいます。
-
配列型の定義の明確化:
- 以前の「regular and dynamic arrays」という曖昧な表現から、「static, open and dynamic arrays」というより具体的な分類への変更は、Go言語が配列の概念をより厳密に定義しようとしていたことを示唆しています。
- 特に「dynamic arrays」という用語が削除され、「open arrays」が導入されたことは重要です。オープン配列は「長さの指定がない配列」と定義され、任意の長さの配列を代入できること、そして「関数の仮パラメータ」として典型的に使用されることが明記されました。これは、Go言語の「スライス」の概念の基礎を築くものであり、後のGo言語における動的配列の主要な表現形式となります。
len()
組み込み関数の追加は、実行時に配列の長さを取得する標準的な方法を提供し、言語の機能性を向上させています。
-
式の構文の簡素化と明確化:
- 「Expression syntax is based on that of C.」という記述が「Expression syntax is based on that of C but with fewer precedence levels.」と変更され、C言語よりも優先順位レベルが少ないことが強調されました。これは、Go言語が意図的に構文をシンプルに保とうとしている設計思想を反映しています。
- 以前の複雑な
Operand
,UnaryExpr
,Designator
,Call
などの定義が、よりシンプルで階層的なExpression
,BinaryExpr
,UnaryExpr
,PrimaryExpr
に再構成されました。 - ポインタの自動逆参照に関する記述が削除され、
p.f
やp[i]
、p()
といった簡潔な構文でフィールド、インデックス、関数呼び出しにアクセスできることが明記されました。これは、Go言語がポインタを扱いやすくするための設計上の特徴の一つです。 - 演算子の優先順位がより詳細に定義され、単項演算子(
+
,-
,!
,^
,<
,>
,*
,&
)が追加されました。特に、単項の^
がC言語の~
(ビットごとの補数)に対応することが明記されました。 - 暗黙的な型変換がないこと、符号なし整数と符号付き整数を混在させられないこと(明示的な変換が必要)が再確認されました。
-
iota
キーワードの導入:iota
キーワードが導入され、定数宣言内で連続する整数値を生成するための特別な機能として説明されました。const
ブロックの開始時にiota
が0にリセットされること、そしてそれを利用して関連する定数セットを定義する具体的な例が示されました。これは、Go言語における定数定義の強力な機能の一つです。
-
文 (Statements) の定義の更新:
Statement
の分類の再編成とSimpleStat
の導入は、言語の構文規則をより体系的に整理しようとする試みです。SimpleStat
が「代入、SimpleVarDecl、インクリメントまたはデクリメント文」を含むことが明確にされました。
-
代入演算子の詳細化:
SimpleAssignment
とTupleAssignment
の定義にassign_op
が導入され、複合代入演算子(例:j <<= 2
)がC言語と同様にサポートされることが明記されました。
-
インポート宣言の詳細な説明:
- 以前の「TODO complete this section」という記述が削除され、インポート宣言に関する詳細な説明が追加されました。
- 明示的なパッケージ名、暗黙的なパッケージ名、ドットインポートの3つの形式が、具体的な例を挙げて説明されました。これにより、Go言語のパッケージ管理と名前空間の仕組みがより明確になりました。
これらの変更は、Go言語の設計が成熟し、その仕様がより厳密かつ包括的に文書化されていく過程を示しています。特に、C言語との比較を通じてGo言語の独自性や設計上の選択が強調されている点が注目されます。
コアとなるコードの変更箇所
--- a/doc/go_lang.txt
+++ b/doc/go_lang.txt
@@ -1,4 +1,5 @@
The Go Programming Language
+(March 7, 2008)
This document is an informal specification/proposal for a new systems programming
language.
@@ -490,21 +491,19 @@ TypeName = QualifiedIdent.
Array types
[TODO: this section needs work regarding the precise difference between
-regular and dynamic arrays]
+static, open and dynamic arrays]
An array is a structured type consisting of a number of elements which
are all of the same type, called the element type. The number of
elements of an array is called its length. The elements of an array
-are designated by indices which are integers between 0 and the length
-- 1.
+are designated by indices which are integers between 0 and the length - 1.
An array type specifies arrays with a given element type and
-an optional array length. The array length must be a (compile-time)
-constant expression, if present. Arrays without length specification
-are called dynamic arrays. A dynamic array must not contain other dynamic
-arrays, and dynamic arrays can only be used as parameter types or in a
-pointer type (for instance, a struct may not contain a dynamic array
-field, but only a pointer to an open array).
+an optional array length. If the length is present, it is part of the type.
+Arrays without a length specification are called open arrays.
+Any array may be assigned to an open array variable with the
+same element type. Typically, open arrays are used as
+formal parameters for functions.
ArrayType = { '[' ArrayLength ']' } ElementType.
ArrayLength = Expression.
@@ -515,6 +514,11 @@ ElementType = Type.
[64] struct { x, y: int32; }
[1000][1000] float64
+The length of an array can be discovered at run time using the
+built-in special function len():
+
+ len(a)
+
Array literals
@@ -920,61 +924,38 @@ export directive.
ExportDecl = 'export' ExportIdentifier { ',' ExportIdentifier } .
ExportIdentifier = QualifiedIdent .
-export sin, cos
-export Math.abs
+ export sin, cos
+ export Math.abs
[ TODO complete this section ]
Expressions
-Expression syntax is based on that of C.
-\
-Operand = Literal | Designator | UnaryExpr | '(' Expression ')' | Call.
-UnaryExpr = unary_op Expression
-unary_op = '!' | '-' | '^' | '&' | '<' .
-Designator = QualifiedIdent { Selector }.
-Selector = '.' identifier | '[' Expression [ ':' Expression ] ']'.
-Call = Operand '(' ExpressionList ')'.
+Expression syntax is based on that of C but with fewer precedence levels.
- 2
- a[i]
- "hello"
- f("abc")
- p.q.r
- a.m(zot, bar)
- <chan_ptr
- ~v
- m["key"]
- (x+y)
-\
-For selectors and function invocations, one level of pointer dereferencing
-is provided automatically. Thus, the expressions
-
- (*a)[i]
- (*m)["key"]
- (*s).field
- (*f)()
-
-can be simplified to
+Expression = BinaryExpr | UnaryExpr | PrimaryExpr .
+BinaryExpr = Expression binary_op Expression .
+UnaryExpr = unary_op Expression .
- a[i]
- m["key"]
- s.field
- f()
+PrimaryExpr =
+ identifier | Literal | '(' Expression ')' | 'iota' |
+ Call | Conversion |
+ Expression '[' Expression [ ':' Expression ] ']' | Expression '.' identifier .
+Call = Expression '(' [ ExpressionList ] ')' .
+Conversion = TypeName '(' [ ExpressionList ] ')' .
-Expression = Conjunction { '||' Conjunction }.
-Conjunction = Comparison { '&&' Comparison }.
-Comparison = SimpleExpr [ relation SimpleExpr ].
-SimpleExpr = Term { add_op Term }.
-Term = Operand { mul_op Operand }.
-\
-relation = '==' | '!=' | '<' | '<=' | '>' | '>='.
+binary_op = log_op | rel_op | add_op | mul_op .
+log_op = '||' | '&&' .
+rel_op = '==' | '!=' | '<' | '<=' | '>' | '>='.
add_op = '+' | '-' | '|' | '^'.
mul_op = '*' | '/' | '%' | '<<' | '>>' | '&'.
-The corresponding precedence hierarchy is as follows:
+unary_op = '+' | '-' | '!' | '^' | '<' | '>' | '*' | '&' .
+
+Field selection ('.') binds tightest, followed by indexing ('[]') and then calls and conversions.
+The remaining precedence levels are as follows (in increasing precedence order):
Precedence Operator
1 ||
@@ -982,13 +963,8 @@ Precedence Operator
3 == != < <= > >=
4 + - | ^
5 * / % << >> &
-\
- 23 + 3*x[i]
- x <= f()
- a >> ~b
- f() || g()
- x == y || <chan_ptr > 0
-\
+ 6 + - ! ^ < > * & (unary)
+
For integer values, / and % satisfy the following relationship:
(a / b) * b + a % b == a
@@ -997,15 +973,67 @@ and
(a / b) is "truncated towards zero".
+There are no implicit type conversions except for
+constants and literals. In particular, unsigned and signed integers
+cannot be mixed in an expression without explicit conversion.
+\
The shift operators implement arithmetic shifts for signed integers,\n and logical shifts for unsigned integers. The property of negative\n-shift counts are undefined.\n+shift counts are undefined. Unary '^' corresponds to C '~' (bitwise\n+complement).\n \n-There are no implicit type conversions except for\n-constants and literals. In particular, unsigned and signed integers\n-cannot be mixed in an expression w/o explicit casting.\n+There is no '->' operator. Given a pointer p to a struct, one writes\n+p.f to access field f of the struct. Similarly. given an array or map pointer, one\n+writes p[i], given a function pointer, one writes p() to call the function.\n+\n+Other operators behave as in C.\n+\n+The 'iota' keyword is discussed in the next section.\n+ \n+Primary expressions\n+\n+ x\n+ 2\n+ (s + ".txt")\n+ f(3.1415, true)\n+ Point(1, 2)\n+ m["foo"]\n+ s[i : j + 1]\n+ obj.color\n+ Math.sin\n+ f.p[i].x()\n+\n+General expressions\n+\n+ +x\n+ 23 + 3*x[i]\n+ x <= f()\n+ ^a >> b\n+ f() || g()\n+ x == y + 1 && <chan_ptr > 0\n+ \n+\n+The constant generator 'iota'\n+\n+Within a declaration, each appearance of the keyword 'iota' represents a successive\n+element of an integer sequence. It is reset to zero whenever the keyword 'const', 'type'\n+or 'var' introduces a new declaration. For instance, 'iota' can be used to construct\n+a set of related constants:\n+\n+ const (\n+ enum0 = iota; // sets enum0 to 0, etc.\n+ enum1 = iota;\n+ enum2 = iota\n+ )\n \n-Unary '^' corresponds to C '~' (bitwise complement).\n+ const (\n+ a = 1 << iota; // sets a to 1 (iota has been reset)\n+ b = 1 << iota; // sets b to 2\n+ c = 1 << iota; // sets c to 4\n+ )\n+ \n+ const x = iota; // sets x to 0\n+ const y = iota; // sets y to 0\n \n \n Statements\n@@ -1014,14 +1042,16 @@ Statements control execution.\n \n Statement =\n Declaration |\n- ExpressionStat | IncDecStat | CompoundStat |\n- Assignment |\n+ SimpleStat | CompoundStat |\n GoStat |\n ReturnStat |\n IfStat | SwitchStat |\n ForStat | RangeStat |\n BreakStat | ContinueStat | GotoStat | LabelStat .\n \n+SimpleStat =\n+ ExpressionStat | IncDecStat | Assignment | SimpleVarDecl .\n+ \n \n Expression statements\n \n@@ -1055,17 +1085,22 @@ from the declaration to the end of the compound statement.\n Assignments\n \n Assignment = SingleAssignment | TupleAssignment | Send .\n-SimpleAssignment = Designator '=' Expression .\n-TupleAssignment = DesignatorList '=' ExpressionList .\n+SimpleAssignment = Designator assign_op Expression .\n+TupleAssignment = DesignatorList assign_op ExpressionList .\n Send = '>' Expression = Expression .\n \n+assign_op = [ add_op | mul_op ] '=' .\n+\n The designator must be an l-value such as a variable, pointer indirection,\n or an array indexing.\n \n x = 1\n *p = f()\n a[i] = 23\n+ \n+As in C, arithmetic binary operators can be combined with assignments:\n \n+ j <<= 2\n \n A tuple assignment assigns the individual elements of a multi-valued operation,\n such function evaluation or some channel and map operations, into individual\n@@ -1243,7 +1278,7 @@ InitStat = SimpleStat .\n Condition = Expression .\n PostStat = SimpleStat .\n \n-A SimpleStat is a simple statement such as an assignemnt, a SimpleVarDecl,\n+A SimpleStat is a simple statement such as an assignment, a SimpleVarDecl,\n or an increment or decrement statement. Therefore one may declare a loop\n variable in the init statement.\n \n@@ -1350,14 +1385,45 @@ PackageClause = 'package' PackageName .\n \n Import declarations\n \n-A program can access exported items from another package using\n-an import declaration:\n+A program can gain access to exported items from another package\n+through an import declaration:\n \n-ImportDecl = 'import' [ PackageName ] PackageFileName .\n+ImportDecl = 'import' [ '.' | PackageName ] PackageFileName .\n PackageFileName = string_lit .\n \n+An import statement makes the exported contents of the named\n+package file accessible in this package.\n \n-[ TODO complete this section ]\n+In the following discussion, assume we have a package in the\n+file "/lib/math", called package Math, which exports functions sin\n+and cos.\n+\n+In the general form, with an explicit package name, the import\n+statement declares that package name as an identifier whose\n+contents are the exported elements of the imported package.\n+For instance, after\n+\n+ import M "/lib/math"\n+\n+the contents of the package /lib/math can be accessed by\n+M.cos, M.sin, etc.\n+\n+In its simplest form, with no package name, the import statement\n+implicitly uses the imported package name itself as the local\n+package name. After\n+\n+ import "/lib/math"\n+\n+the contents are accessible by Math.sin, Math.cos.\n+\n+Finally, if instead of a package name the import statement uses\n+an explicit period, the contents of the imported package are added\n+to the current package. After\n+\n+ import . "/lib/math"\n+\n+the contents are accessible by sin and cos. In this instance, it is\n+an error if the import introduces name conflicts.\n \n \n Program\n@@ -1372,5 +1438,3 @@ Program = PackageClause { ImportDecl } { Declaration } .\n TODO: type switch?\n TODO: select\n TODO: words about slices\n-TODO: words about channel ops, tuple returns\n-TODO: words about map ops, tuple returns\n```
## コアとなるコードの解説
このコミットの「コード」は、Go言語の仕様を記述したドキュメント(`doc/go_lang.txt`)です。したがって、ここでの「解説」は、Go言語の設計思想と構文がどのように進化し、文書化されたかという観点から行われます。
1. **配列型の進化**:
* 以前の「regular and dynamic arrays」という曖昧な表現から、「static, open and dynamic arrays」というより具体的な分類への変更は、Go言語が配列の概念をより厳密に定義しようとしていたことを示唆しています。
* 特に「dynamic arrays」という用語が削除され、「open arrays」が導入されたことは重要です。オープン配列は、長さが型の一部ではない配列を指し、任意の長さの配列を受け入れることができる柔軟なメカニズムを提供します。これは、Go言語の「スライス」の概念の基礎を築くものであり、後のGo言語における動的配列の主要な表現形式となります。
* `len()`組み込み関数の追加は、実行時に配列の長さを取得する標準的な方法を提供し、言語の機能性を向上させています。
2. **式の構文の簡素化**:
* C言語の複雑な演算子優先順位からの脱却は、Go言語が意図的に構文をシンプルにし、読みやすく、理解しやすい言語を目指していることを明確に示しています。これにより、開発者は式の評価順序について推測する手間が減り、コードの可読性が向上します。
* ポインタの自動逆参照の簡素化(例: `(*s).field`が`s.field`になる)は、Go言語がポインタをより透過的に扱えるように設計されていることを示しています。これは、C/C++のような言語でポインタのデリファレンスが頻繁に必要となる煩雑さを軽減し、コードをよりクリーンに保つことに貢献します。
* `iota`キーワードの導入は、関連する定数セットを簡潔かつ自動的に生成する強力なメカニズムを提供します。これは、特に列挙型のようなパターンをGoで表現する際に非常に役立ち、コードの重複を減らし、保守性を向上させます。
3. **文と代入演算子の明確化**:
* `Statement`の分類の再編成と`SimpleStat`の導入は、言語の構文規則をより体系的に整理しようとする試みです。
* 複合代入演算子(例: `j <<= 2`)のサポートは、C言語の慣習を踏襲しつつ、より簡潔なコード記述を可能にします。
4. **インポート宣言の詳細化**:
* インポート宣言に関する詳細な説明の追加は、Go言語のパッケージシステムがどのように機能するかを明確に理解するために不可欠です。
* 明示的なパッケージ名、暗黙的なパッケージ名、ドットインポートの3つの形式の解説は、開発者がパッケージをどのようにインポートし、その内容にアクセスするかについて、より柔軟な選択肢を提供します。特にドットインポートは、名前空間の衝突のリスクがあるものの、特定の状況下でコードをより簡潔に記述するために使用できます。
全体として、このコミットは、Go言語がその初期段階で、シンプルさ、明確さ、そして実用性を追求しながら、言語仕様を洗練させていった過程を如実に示しています。ドキュメントの更新は、単なる記述の修正にとどまらず、言語設計の重要な決定と進化を反映しています。
## 関連リンク
Go言語の初期の設計に関する情報は、公式ブログやメーリングリストのアーカイブに断片的に存在しますが、特定のコミットに関連する詳細な議論を見つけるのは困難です。しかし、Go言語の歴史や設計思想に関する一般的な情報源は以下の通りです。
* **The Go Programming Language (Official Website)**: [https://go.dev/](https://go.dev/)
* **Go Blog**: [https://go.dev/blog/](https://go.dev/blog/) - Go言語の進化に関する公式な発表や解説が掲載されています。特に初期のブログ記事は、当時の設計思想を理解する上で役立つ可能性があります。
* **Go Language Design Documents**: Go言語の設計に関するより詳細なドキュメントは、Goのソースコードリポジトリ内や、関連する学術論文などで見つかることがあります。
## 参考にした情報源リンク
* Go言語の公式ドキュメントおよびブログ記事
* 一般的なプログラミング言語の概念(配列、式、演算子優先順位、代入演算子、インポートシステム)に関する知識
* C言語の構文とセマンティクスに関する知識 (比較対象として)
* `iota`キーワードに関するGo言語のドキュメント
(注: 2008年3月という非常に初期のコミットであるため、この特定のコミットに関する直接的な外部の議論や解説を見つけることは困難でした。そのため、解説は主にコミット内容の差分とGo言語の一般的な設計原則に基づいて行われました。)