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

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

このコミットは、Go言語の公式仕様書である doc/go_spec.html ファイルに対する変更です。具体的には、Go言語の型システムに関するセクションが大幅に更新され、特に「代入互換性 (assignment compatibility)」と「比較互換性 (comparison compatibility)」に関する記述が整理・統合されました。

コミット

commit 5af7de3fe37378cb61060e554b885b1f26a8f8f6
Author: Rob Pike <r@golang.org>
Date:   Tue Feb 24 15:17:59 2009 -0800

    Updated the section on Types.
    Moved assignment compatibility to its own small section. Although most rules are type-specific,
    some are not and it reduces redundancy to combine them.
    Also, more experimentally, wrote a section on comparison compatibility.
    
    R=gri
    DELTA=382  (125 added, 122 deleted, 135 changed)
    OCL=25355
    CL=25382

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

https://github.com/golang/go/commit/5af7de3fe37378cb61060e554b885b1f26a8f8f6

元コミット内容

このコミットは、Go言語仕様書の「型 (Types)」セクションを更新しました。代入互換性に関する記述を独立した小さなセクションに移動させました。これにより、ほとんどのルールは型固有であるものの、そうでないものもあり、それらをまとめることで冗長性を減らすことができます。また、より実験的な試みとして、比較互換性に関するセクションも記述しました。

変更の背景

Go言語は、その設計初期段階から厳格な型システムを持つことを目指していました。しかし、言語仕様の策定が進む中で、型の「互換性」に関するルールが、各型の説明セクション(例えば、配列型、構造体型、ポインタ型など)に分散して記述されており、全体として冗長で、一貫性に欠ける部分がありました。

このコミットの主な背景は以下の通りです。

  1. 冗長性の排除: 各型セクションで個別に代入や比較のルールを記述することは、多くの重複を生み出していました。型固有のルールと、複数の型に共通するルールを分離し、共通ルールを独立したセクションにまとめることで、仕様書全体の記述を簡潔にし、保守性を高める必要がありました。
  2. 一貫性の向上: 分散していたルールを統合することで、Go言語の型システムにおける代入と比較の振る舞いをより一貫性のある形で提示できるようになります。これにより、開発者が言語の挙動を理解しやすくなります。
  3. 明確化: 特に比較互換性については、Go言語の設計思想として、C言語のような暗黙的な型変換やポインタ演算を許容しない厳格なルールが採用されていました。この厳格なルールを明確に定義し、開発者に誤解を与えないようにすることが重要でした。コミットメッセージにある「より実験的に書かれた」という記述は、この比較互換性のルールが、この時点ではまだ完全に固まりきっておらず、試行錯誤の段階であったことを示唆しています。
  4. 可読性の向上: 関連する情報を一箇所に集約することで、読者が特定のルールを探しやすくなり、仕様書全体の可読性が向上します。

これらの背景から、Go言語の型システムの中核をなす「代入」と「比較」の概念を、より体系的かつ網羅的に記述するための改訂が行われました。

前提知識の解説

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

  1. Go言語の型システム:

    • 静的型付け: Goは静的型付け言語であり、変数の型はコンパイル時に決定されます。これにより、実行時エラーの多くを未然に防ぐことができます。
    • 型安全性: Goは型安全性を重視しており、異なる型の間の暗黙的な変換はほとんど許容されません。明示的な型変換 (type conversion) が必要な場合が多いです。
    • 基本型 (Basic Types): int, float64, bool, string など、言語に組み込まれている基本的な型です。
    • 複合型 (Composite Types): 基本型を組み合わせて作られる型で、配列 (arrays)、構造体 (structs)、ポインタ (pointers)、関数 (functions)、インターフェース (interfaces)、スライス (slices)、マップ (maps)、チャネル (channels) などがあります。
  2. 代入互換性 (Assignment Compatibility):

    • ある型の値が、別の型の変数に代入できるかどうかのルールを指します。Goでは、通常、厳密に同じ型でなければ代入できませんが、特定の条件下では異なる型間での代入が許可されます(例: インターフェースへの代入、nil の代入)。
  3. 比較互換性 (Comparison Compatibility):

    • ある型の値が、別の型の値と比較できるかどうかのルールを指します。Goでは、比較演算子 (==, !=, <, >, <=, >=) を使用して値を比較しますが、すべての型がすべての比較演算子をサポートしているわけではありません。特に、複合型の中には比較ができないものや、nil との比較のみが許されるものがあります。
  4. 静的型 (Static Type) と動的型 (Dynamic Type):

    • 静的型: 変数が宣言されたときにコンパイル時に決定される型です。
    • 動的型: インターフェース型の変数に格納されている実際の値の型です。実行時に変化する可能性があります。インターフェース型以外の変数では、静的型と動的型は常に同じです。
  5. 完全な型 (Complete Type) と不完全な型 (Incomplete Type):

    • Go言語の型宣言において、型が完全に定義されているかどうかの概念です。例えば、構造体やインターフェースは、前方宣言 (forward declaration) された時点では不完全な型であり、そのフィールドやメソッドが完全に定義されて初めて完全な型となります。変数の宣言時には、その変数の型は完全である必要があります。
  6. Go言語仕様書 (Go Language Specification):

    • Go言語の公式な振る舞いを定義する文書です。言語の構文、セマンティクス、組み込み関数、パッケージなどが詳細に記述されており、Goコンパイラやツールの実装の基盤となります。このコミットは、この仕様書の内容を直接変更しています。

これらの概念を理解することで、コミットがGo言語の型システムにどのような影響を与え、なぜこのような変更が必要とされたのかを深く把握することができます。

技術的詳細

このコミットは、Go言語仕様書 doc/go_spec.html の「Types」セクションに焦点を当てた大規模な改訂です。主な技術的変更点は、型の「代入互換性」と「比較互換性」に関する記述の構造化と集約です。

  1. 型セクションの再構成:

    • 以前は、各複合型(配列、構造体、ポインタ、関数、インターフェース、スライス、マップ、チャネル)のセクション内に、それぞれの型に特化した代入互換性や比較に関する記述が散在していました。
    • このコミットでは、これらの記述の多くが削除され、代わりに仕様書の後半に新しく独立したセクションとして「Assignment compatibility」と「Comparison compatibility」が追加されました。
    • これにより、型固有のルールと、複数の型に共通する一般的なルールが明確に分離され、仕様書全体の論理的な構造が改善されました。
  2. 「Assignment compatibility」セクションの新設:

    • この新しいセクションでは、Go言語における値の代入に関する包括的なルールがまとめられています。
    • 特に、nil 定数がポインタ、関数、スライス、マップ、チャネル、インターフェースの各変数に代入可能であること。
    • 配列が、要素型が等しいスライス変数に代入できること(この際、配列はコピーされず、配列全体を含むスライスが作成される)。
    • 値がインターフェースを実装している場合、その値をインターフェース変数に代入できること。
    • 双方向チャネル型の値が、要素型が等しい任意のチャネル変数に代入できること。
    • これらのルールが、一箇所に集約されたことで、Goの代入規則の全体像が把握しやすくなりました。
  3. 「Comparison compatibility」セクションの新設:

    • このセクションは、Go言語における値の比較に関する詳細なルールを定義しています。コミットメッセージにある「より実験的に書かれた」という記述は、この比較ルールの定義が特に慎重に進められていたことを示唆しています。
    • 数値型と文字列型は、すべての比較演算子 (==, !=, <, >, <=, >=) で比較可能であること。
    • 真偽値型は、等価性 (==, !=) のみで比較可能であること。
    • 重要な点として、配列と構造体は比較できないことが明記されました。これは、C言語などとは異なるGoの厳格な型システムの特徴の一つです。
    • スライスとインターフェースは、明示的に nil とのみ比較可能であること。
    • ポインタ値は、同じメモリ位置を指す場合に等しいこと。
    • 関数値は、同じ関数を指す場合に等しいこと。
    • チャネルとマップ値は、make 関数による同じ呼び出しによって作成された場合に等しいこと。
    • インターフェース値は、静的型が同じであれば比較互換性があり、動的型が同じであれば等しいこと。
    • これらの詳細な比較ルールが体系的に記述されたことで、Goプログラムにおける比較演算の振る舞いが明確になりました。
  4. その他の変更:

    • 文字列リテラルの連結に関する記述が、より一般的な文字列セクションに移動されました。
    • byte 型が uint8 のエイリアスであることが、算術型の一覧の最後に明示的に記述され、より自然な流れになりました。
    • 「Platform-specific convenience types」という表現が「Architecture-specific convenience types」に変更され、より正確な用語が使用されました。
    • TODO コメントの整理や、細かな誤字修正(例: parseable から parsableExpressionLIst から ExpressionList)も行われています。

これらの変更は、Go言語の仕様書が初期段階から成熟していく過程で、記述の正確性、一貫性、そして読者の理解を深めるための努力が払われていたことを示しています。特に、型システムの中核をなす代入と比較のルールを明確に定義し、集約したことは、Go言語の堅牢性と予測可能性を保証する上で極めて重要です。

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

このコミットは doc/go_spec.html ファイルのみを変更しており、その差分は非常に広範囲にわたります。ここでは、特に重要な変更点、すなわち「代入互換性」と「比較互換性」の新しいセクションの追加と、それに伴う既存セクションからの関連記述の削除に焦点を当てて抜粋します。

新しい「Assignment compatibility」セクションの追加

--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -1334,6 +1242,95 @@ struct { a, b *T5 } and struct { a, b *T5 }\n As an example, "T0" and "T1" are equal but not identical because they have\n different declarations.\n \n+<h3>Assignment compatibility</h3>\n+\n+<!--\n+TODO in another round of editing:\n+It may make sense to have a special section in this doc containing these rule\n+sets for:\n+\n+complete/incomplete types\n+equality of types\n+identity of types\n+comparisons\n+assignment compatibility\n+-->\n+\n+<p>\n+Values of any type may always be assigned to variables\n+of equal static type. Some types and values have conditions under which they may\n+be assigned to different types:\n+</p>\n+<ul>\n+<li>\n+The predeclared constant <code>nil</code> can be assigned to any\n+pointer, function, slice, map, channel, or interface variable.\n+<li>\n+Arrays can be assigned to slice variables with equal element type.\n+When assigning to a slice variable, the array is not copied but a\n+slice comprising the entire array is created.\n+</li>\n+<li>\n+A value can be assigned to an interface variable if the dynamic\n+type of the value implements the interface.\n+</li>\n+<li>\n+A value of bidirectional channel type can be assigned to any channel\n+variable of equal channel value type.\n+</li>\n+</ul>

新しい「Comparison compatibility」セクションの追加

--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -1334,6 +1242,95 @@ struct { a, b *T5 } and struct { a, b *T5 }\n As an example, "T0" and "T1" are equal but not identical because they have\n different declarations.\n \n+<h3>Comparison compatibility</h3>\n+\n+<p>\n+Values of any type may be compared to other values of equal static\n+type.  Values of numeric and string type may be compared using the\n+full range of comparison operators as described in §Comparison operators;\n+booleans may be compared only for equality or inequality.\n+</p>\n+\n+<p>\n+Values of composite type may be\n+compared for equality or inequality using the <code>==</code> and\n+<code>!=</code> operators, with the following provisos:\n+</p>\n+<ul>\n+<li>\n+Arrays and structs may not be compared to anything.\n+</li>\n+<li>\n+A slice value may only be compared explicitly against <code>nil</code>\n+and is equal to <code>nil</code> if it has been assigned the explicit\n+value <code>nil</code> or if it is a variable (or array element,\n+field, etc.) that has not been modified since it was created\n+uninitialized.\n+</li>\n+<li>\n+Similarly, an interface value is equal to <code>nil</code> if it has\n+been assigned the explicit value <code>nil</code> or if it is a\n+variable (or array element, field, etc.) that has not been modified\n+since it was created uninitialized.\n+</li>\n+<li>\n+For types that can be compared to <code>nil</code>,\n+two values of the same type are equal if they both equal <code>nil</code>,\n+unequal if one equals <code>nil</code> and one does not.\n+</li>\n+<li>\n+Pointer values are equal if they point to the same location.\n+</li>\n+<li>\n+Function values are equal if they point to the same function.\n+</li>\n+<li>\n+Channel and map values are equal if they were created by the same call of <code>make</code>\n+(§Making slices, maps, and channels).\n+</li>\n+<li>\n+Interface values are comparison compatible if they have the same static type and\n+equal if they have the same dynamic type.\n+</li>\n+</ul>

既存の型セクションからの互換性記述の削除例 (例: Array types)

--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -698,19 +691,14 @@ ArrayLength = Expression .\n ElementType = CompleteType .\n </pre>\n \n-The array length and its value are part of the array type. The array length\n-must be a constant expression (§Constant expressions) that evaluates to an\n-integer value >= 0.\n <p>\n-The number of elements of an array "a" can be discovered using the built-in\n-function\n-\n-<pre>\n-len(a)\n-</pre>\n-\n-The length of arrays is known at compile-time, and the result of a call to\n-"len(a)" is a compile-time constant.\n+The length is part of the array's type and must must be a constant\n+expression (§Constant expressions) that evaluates to a non-negative\n+integer value.  The length of array <code>a</code> can be discovered\n+using the built-in function <code>len(a)</code>, which is a\n+compile-time constant.  The elements can be indexed by integer\n+indices 0 through the <code>len(a)-1</code> (§Indexes).\n+</p>\n \n <pre>\n [32]byte\n@@ -718,19 +706,15 @@ The length of arrays is known at compile-time, and the result of a call to\n [1000]*float64\n </pre>\n \n-Assignment compatibility: Arrays can be assigned to variables of equal type\n-and to slice variables with equal element type. When assigning to a slice\n-variable, the array is not copied but a slice comprising the entire array\n-is created.\n-\n \n <h3>Struct types</h3>

同様に、Pointer types, Function types, Interface types, Slice types, Map types, Channel types の各セクションからも、代入互換性や比較に関する記述が削除されています。

「Comparison operators」セクションの更新

--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -2484,11 +2481,12 @@ not occur. For instance, it may not assume that "x &lt; x + 1" is always true.\n \n <h3>Comparison operators</h3>\n \n+<p>\n Comparison operators yield a boolean result. All comparison operators apply\n-to strings and numeric types. The operators "==" and "!=" also apply to\n-boolean values, pointer, interface, and channel types. Slice and\n-map types only support testing for equality against the predeclared value\n-"nil".\n+to basic types except bools.\n+The operators <code>==</code> and <code>!=</code> apply, at least in some cases,\n+to all types except arrays and structs.\n+</p>\n \n <pre class="grammar">\n ==    equal\n@@ -2499,15 +2497,19 @@ map types only support testing for equality against the predeclared value\n >=    greater or equal\n </pre>\n \n+<p>\n+Numeric basic types are compared in the usual way.\n+</p>\n+<p>\n Strings are compared byte-wise (lexically).\n+</p>\n <p>\n Booleans are equal if they are either both "true" or both "false".\n+</p>\n <p>\n-Pointers are equal if they point to the same value.\n-<p>\n-Interface, slice, map, and channel types can be compared for equality according\n-to the rules specified in the section on §Interface types, §Slice types, §Map types,\n-and §Channel types, respectively.\n+The rules for comparison of composite types are described in the\n+section on §Comparison compatibility.\n+</p>\n \n \n <h3>Logical operators</h3>

この変更により、比較演算子の一般的な適用範囲が簡潔に述べられ、複合型の比較ルールについては新設された「Comparison compatibility」セクションを参照するように誘導されています。

コアとなるコードの解説

このコミットの核心は、Go言語の型システムにおける「代入」と「比較」のルールを、より体系的かつ網羅的に定義し直した点にあります。

新設された Assignment compatibility セクションは、Go言語における値の代入がどのような条件下で許可されるかを明確にしています。特に注目すべきは、nil の代入規則がポインタ、関数、スライス、マップ、チャネル、インターフェースといった複数の型に共通して適用されることが一箇所にまとめられたことです。また、配列からスライスへの代入が、データのコピーを伴わず、既存の配列を参照するスライスを作成するというGo特有の挙動も明記されています。インターフェースへの代入ルールも、値がインターフェースを実装しているかどうかに基づくという、Goのポリモーフィズムの根幹をなす部分が簡潔に説明されています。

もう一つの重要な新設セクションである Comparison compatibility は、Go言語における値の比較の厳格なルールを詳細に記述しています。このセクションは、GoがC言語のような緩やかな比較を許容しないことを明確に示しています。例えば、配列と構造体は比較できないというルールは、Goが値のセマンティクスを重視し、これらの複合型が同一性を比較するのに適さないという設計思想を反映しています。スライスやインターフェースが nil とのみ比較可能であるという点も、これらの型が参照型であり、その内部構造の比較が複雑であるため、明示的な nil チェックに限定していることを示唆しています。ポインタ、関数、チャネル、マップの比較ルールは、それらが参照する実体が同一であるかどうかに基づいており、Goの参照セマンティクスを反映しています。インターフェースの比較ルールは、静的型と動的型の両方を考慮する必要があるという、インターフェースの複雑な性質を扱っています。

これらの変更は、Go言語の型システムが、開発者にとって予測可能で堅牢なものであることを保証するために不可欠です。各型セクションに散らばっていた互換性ルールを統合することで、冗長性が排除され、仕様書全体の可読性と保守性が向上しました。これにより、Go言語の学習者や実装者は、代入や比較の振る舞いをより容易に理解し、Goの設計思想に沿ったコードを書くことができるようになりました。このコミットは、Go言語が初期段階から、その核となる概念をいかに厳密に定義しようとしていたかを示す好例と言えます。

関連リンク

参考にした情報源リンク

  • Go言語のコミット履歴: https://github.com/golang/go/commits/master/
  • Go言語仕様書 (コミット当時のバージョンに近いもの): このコミットはGo言語の非常に初期の段階のものであるため、現在の仕様書とは内容が異なります。当時の正確な仕様書をウェブ上で見つけるのは困難ですが、Go言語の進化の過程を理解する上で、現在の仕様書と比較検討することが有益です。