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

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

このコミットは、Go言語の初期の仕様書である doc/go_spec.txt に対する重要な更新を含んでいます。主な変更点は、インターフェース宣言におけるメソッドの記述方法の柔軟性向上と、エクスポートに関する初期の言語仕様の追加です。

コミット

commit 337af317813d14fd6ed0e5a62072923a6f181570
Author: Robert Griesemer <gri@golang.org>
Date:   Mon Nov 17 18:11:36 2008 -0800

    - allow for multiple method names per function type in an interface decl.
    - added some initial language with respect to exports
    
    R=r
    DELTA=95  (47 added, 31 deleted, 17 changed)
    OCL=19407
    CL=19426

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

https://github.com/golang/go/commit/337af317813d14fd6ed0e5a62072923a6f181570

元コミット内容

このコミットは、Go言語の仕様書 doc/go_spec.txt に対して以下の2つの主要な変更を加えています。

  1. インターフェース宣言における複数メソッド名の許可: 同じ関数型を持つ複数のメソッド名を1つのインターフェース宣言内で記述できるようになりました。これにより、インターフェースの定義がより簡潔になります。
  2. エクスポートに関する初期言語仕様の追加: パッケージのエクスポート(外部からのアクセス)に関する初期のルールが仕様書に明記されました。これには、export キーワードの導入と、識別子の可視性に関する概念が含まれます。

変更の背景

このコミットが行われた2008年11月は、Go言語がまだ活発に開発され、その仕様が固まりつつあった非常に初期の段階です。Go言語の設計目標の一つに「シンプルさ」と「実用性」がありました。

  1. インターフェースの柔軟性向上: 以前のインターフェース宣言では、同じシグネチャを持つメソッドであっても個別に記述する必要がありました。これは冗長であり、特に多くの関連するメソッドを持つインターフェースを定義する際に不便でした。この変更は、インターフェースの定義をより簡潔にし、コードの可読性と記述効率を向上させることを目的としています。これは、Go言語が目指す「簡潔さ」と「表現力」の一環です。

  2. エクスポートルールの明確化: どの識別子(変数、関数、型など)がパッケージの外部からアクセス可能であるかを明確にすることは、大規模なプロジェクトにおけるモジュール性、カプセル化、およびコードの再利用性を確保するために不可欠です。このコミット以前は、エクスポートに関するルールが不明確であったか、あるいは存在しなかった可能性があります。この変更は、Goのパッケージシステムがどのように機能するかについての基本的な枠組みを確立し、開発者が予測可能で安全な方法でコードを構成できるようにするための初期ステップでした。特に、exportキーワードの導入は、Goの可視性ルール(大文字で始まる識別子がエクスポートされるという現在のルール)が確立される前の過渡期の設計を示唆しています。

これらの変更は、Go言語がその設計原則に基づき、より堅牢で使いやすい言語となるための基盤を築くものでした。

前提知識の解説

このコミットの理解には、Go言語の基本的な概念、特に初期の設計思想に関する知識が役立ちます。

  1. Go言語の初期設計: Go言語は、GoogleでRobert Griesemer、Rob Pike、Ken Thompsonによって設計されました。彼らは、既存の言語(C++、Javaなど)の複雑さやコンパイル時間の長さに不満を抱き、よりシンプルで効率的なシステムプログラミング言語を目指しました。初期のGoは、現在のGoとは異なる構文やキーワードを持つ部分があり、このコミットはその進化の過程を示しています。

  2. インターフェース (Interfaces): Goのインターフェースは、メソッドのシグネチャの集合を定義する型です。Goのインターフェースは「暗黙的」に実装されます。つまり、ある型がインターフェースで定義されたすべてのメソッドを実装していれば、その型はそのインターフェースを満たします。このコミット以前は、インターフェースのメソッド宣言の構文が現在とは異なり、より冗長であった可能性があります。

  3. パッケージとエクスポート (Packages and Exports): Goのコードはパッケージに組織されます。パッケージは、関連する機能の集合をカプセル化し、名前空間を提供します。他のパッケージから特定の識別子(関数、変数、型など)にアクセスできるようにするためには、それらを「エクスポート」する必要があります。このコミットの時点では、エクスポートのメカニズムとして export キーワードが検討されていたことが示唆されています。現在のGoでは、識別子の最初の文字が大文字であるかどうかに基づいてエクスポートが決定されます(大文字で始まる識別子はエクスポートされ、小文字で始まる識別子はパッケージプライベートです)。このコミットは、その現在のルールが確立される前の、異なるアプローチが試みられていたことを示しています。

  4. Go言語仕様書 (Go Language Specification): doc/go_spec.txt は、Go言語の文法とセマンティクスを定義する公式文書の草稿です。この文書は、言語の進化とともに更新され、Go言語の設計思想と機能の変遷を追う上で非常に重要です。

  5. OCL/CL: OCL (Original Change List) と CL (Change List) は、Google内部のコードレビューシステムで使用される識別子です。これらは、このコミットがGoogleの内部開発プロセスの一部として行われたことを示しています。

技術的詳細

このコミットは、Go言語の仕様書 doc/go_spec.txt の複数のセクションにわたる変更を含んでいます。

1. インターフェース宣言の変更

  • 変更前: MethodSpec = identifier FunctionType .
    • これは、インターフェース内の各メソッドが個別の識別子と関数型を持つことを意味していました。例えば、Read(b Buffer) bool;Write(b Buffer) bool; は別々に記述する必要がありました。
  • 変更後: MethodSpec = IdentifierList FunctionType .
    • IdentifierList は、カンマで区切られた識別子のリストを意味します。これにより、同じ関数型を持つ複数のメソッドを1行で宣言できるようになりました。
    • :
      • 変更前:
        interface {
            Read(b Buffer) bool;
            Write(b Buffer) bool;
            Close();
        }
        
      • 変更後:
        interface {
            Read, Write    (b Buffer) bool;
            Close          ();
        }
        
    • この変更は、インターフェースの定義をより簡潔にし、特に多くの関連するメソッドを持つインターフェースの可読性を向上させます。

2. エクスポートに関する仕様の追加と明確化

  • 「Predeclared identifiers」セクションの追加:

    • bool, byte, uint8, ..., string などの基本型。
    • uint, int, float, uintptr などのプラットフォーム固有の便宜型。
    • true, false, iota, nil などの事前宣言された定数。
    • cap(), convert(), len(), new(), panic(), panicln(), print(), println(), typeof(), ... などの事前宣言された関数。
    • これらの識別子がGo言語の組み込み要素として最初から利用可能であることを明確にしています。
  • 「Exported declarations」セクションの追加と詳細化:

    • グローバル宣言は「エクスポート」とマークされることで、現在のソースファイル外からアクセス可能になることが明記されました。
    • 他のソースファイルはパッケージをインポートし、修飾識別子(PackageName.Identifier)を介してエクスポートされた識別子にアクセスできると説明されています。
    • ローカル宣言はエクスポートできないことが明確にされています。
    • 2種類のエクスポートの概念の導入:
      • 無制限エクスポート (unrestricted export): export キーワードでマークされた宣言は、そのパッケージをインポートする任意のファイルからアクセス可能です。
      • パッケージ制限エクスポート (package-restricted export): package キーワードでマークされた宣言は、同じパッケージに属するファイルからのみアクセス可能です。
        • : package type Node *struct { val int; next *Node }
        • この package キーワードによるエクスポートの概念は、現在のGo言語には存在しません。これは、Goの可視性ルールが進化する過程で試みられた設計の一つであったことを示唆しています。現在のGoでは、識別子の先頭文字の大文字/小文字によって可視性が制御されます。
    • 識別子が型を表す場合、その型構造全体もエクスポートされることが説明されています。特に、structinterface 型が宣言された場合、すべてのフィールドとメソッドもエクスポートされます。
    • TODOコメント: フィールドやメソッドの可視性を制限する必要があること、デフォルトでは構造体フィールドやメソッドが自動的にエクスポートされないようにすべきであること、エクスポートは識別子ベースであるべきであることなど、今後の検討事項が残されています。これは、Goの可視性ルールがまだ流動的であったことを示しています。

3. 「Export declarations」セクションの整理

  • 既存の「Export declarations」セクションが、新しく追加された「Exported declarations」セクションの内容と重複するため、再構成の必要性が示唆されています。
  • export sin, cosexport math.abs のような、現在のGoには存在しないエクスポート構文の例が残されています。これもまた、初期の設計段階における様々な試行錯誤を示しています。

4. 「Qualified identifiers」セクションの追加

  • 修飾識別子(PackageName.identifier)に関するセクションが追加されました。これは、パッケージをインポートした後に、そのパッケージのエクスポートされた識別子にアクセスするための基本的な構文を定義するものです。

5. その他の変更

  • const decls: "const a, b = 1, 2" is not allowed - why not? Should be symmetric to vars. というTODOコメントが追加され、定数宣言の対称性に関する疑問が提起されています。

これらの変更は、Go言語の構文とセマンティクスがどのように進化し、現在の形に近づいていったかを示す貴重な記録です。特に、エクスポートに関する exportpackage キーワードの試みは、現在のGoの可視性ルール(大文字/小文字)が採用される前の設計思想を垣間見ることができます。

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

このコミットは、Go言語の仕様書 doc/go_spec.txt のみを変更しています。具体的な変更箇所は以下の通りです。

  1. インターフェース型定義の MethodSpec:

    --- a/doc/go_spec.txt
    +++ b/doc/go_spec.txt
    @@ -1319,13 +1332,12 @@ An interface type denotes the set of all types that implement at least
     the set of methods specified by the interface type, and the value "nil".
     
     	InterfaceType = "interface" [ "{" [ List<MethodSpec> ] "}" ] .
    -	MethodSpec = identifier FunctionType .
    +	MethodSpec = IdentifierList FunctionType .
     
     	// A basic file interface.
     	interface {
    -		Read(b Buffer) bool;
    -		Write(b Buffer) bool;
    -		Close();
    +		Read, Write	(b Buffer) bool;
    +		Close		();
     	}
     
     Any type (including interface types) whose interface has, possibly as a
    @@ -1348,8 +1360,7 @@ In general, a type implements an arbitrary number of interfaces.
     For instance, consider the interface
     
     	type Lock interface {
    -		lock();
    -		unlock();
    +		lock, unlock	();
     	}
    
  2. 「Predeclared identifiers」および「Exported declarations」セクションの追加と修正:

    --- a/doc/go_spec.txt
    +++ b/doc/go_spec.txt
    @@ -595,52 +598,62 @@ same identifier declared in an outer block.
     An entity is said to be ``local\'\' to its scope. Declarations in the package
     scope are ``global\'\' declarations.
     
    -- Global declarations optionally may be marked for export with the reserved word
    -- "export". Local declarations can never be exported.
    -- Identifiers declared in exported declarations (and no other identifiers)
    -- are made visible to clients of this package, that is, other packages that import
    -- this package.
    --
    -- If the declaration defines a type, the type structure is exported as well. In
    -- particular, if the declaration defines a new "struct" or "interface" type,
    -- all structure fields and all structure and interface methods are exported also.
    --
    --	export const pi float = 3.14159265
    --	export func Parse(source string);
    --
    -- Note that at the moment the old-style export via ExportDecl is still supported.
    --
    -- TODO: Eventually we need to be able to restrict visibility of fields and methods.
    -- (gri) The default should be no struct fields and methods are automatically exported.
    -- Export should be identifier-based: an identifier is either exported or not, and thus
    -- visible or not in importing package.
    --
    -- TODO: Need some text with respect to QualifiedIdents.
    --
    --	QualifiedIdent = [ PackageName "." ] identifier .
    --	PackageName = identifier .
     
    +Predeclared identifiers
    +----
      
     The following identifiers are predeclared:
      
    -- all basic types:
    +All basic types:
      
      	bool, byte, uint8, uint16, uint32, uint64, int8, int16, int32, int64,
      	float32, float64, float80, string
      	
    -- a set of platform-specific convenience types:
    +A set of platform-specific convenience types:
      
      	uint, int, float, uintptr
      	
    -- the predeclared constants:
    +The predeclared constants:
      
      	true, false, iota, nil
      	
    -- the predeclared functions (note: this list is likely to change):
    +The predeclared functions (note: this list is likely to change):
      
      	cap(), convert(), len(), new(), panic(), panicln(), print(), println(), typeof(), ...
      
      
    +Exported declarations
    +----
    +
    +Global declarations optionally may be marked for ``export\'\', thus making the
    +declared identifier accessible outside the current source file. Another source
    +file may then import the package (§Packages) and access exported identifiers
    +via qualified identifiers (§Qualified identifiers). Local declarations can
    +never be marked for export.
    +
    +There are two kinds of exports: If a declaration in a package P is marked with
    +the keyword "export", the declared identifier is accessible in any file
    +importing P; this is called ``unrestricted export\'\'. If a declaration is
    +marked with the keyword "package", the declared identifier is only accessible
    +in files belonging to the same package P; this is called ``package-restricted\'\'
    +export.
    +
    +If the identifier represents a type, it must be a complete type (§Types) and
    +the type structure is exported as well. In particular, if the declaration
    +defines a "struct" or "interface" type, all structure fields and all structure
    +and interface methods are exported also.
    +
    +	export const pi float = 3.14159265
    +	export func Parse(source string);
    +
    +	package type Node *struct { val int; next *Node }
    +
    +TODO: Eventually we need to be able to restrict visibility of fields and methods.
    +(gri) The default should be no struct fields and methods are automatically exported.
    +Export should be identifier-based: an identifier is either exported or not, and thus
    +visible or not in importing package.
    +
    +
     Const declarations
     ----
      
    @@ -807,6 +820,10 @@ this construct can be used to declare local temporary variables.\n  Export declarations\n  ----\n  \n    +TODO:\n    +1) rephrase this section (much of it covered by Exported declarations)\n    +2) rethink need for this kind of export\n    +\n  Global identifiers may be exported, thus making the\n  exported identifier visible outside the package.  Another package may\n  then import the identifier to use it.\n    @@ -830,10 +847,6 @@ export directive.\n  	export sin, cos\n  	export math.abs\n  \n    -TODO: complete this section\n    -\n    -TODO: export as a mechanism for public and private struct fields?\n    -\n  \n  Types\n  ----
    
  3. 「Qualified identifiers」セクションの追加:

    --- a/doc/go_spec.txt
    +++ b/doc/go_spec.txt
    @@ -1538,7 +1549,12 @@ are known at compile-time.\n  Qualified identifiers\n  ----\n  \n    -TODO(gri) write this section
    +A qualified identifier is an identifier qualified by a package name.\n    +\n    +TODO(gri) expand this section.\n    +\n    +	QualifiedIdent = { PackageName "." } identifier .\n    +	PackageName = identifier .\n  \n  \n  Iota
    

コアとなるコードの解説

このコミットの「コード」は、Go言語の仕様書そのものです。したがって、変更されたテキストがGo言語の設計と機能にどのように影響するかを解説します。

  1. インターフェース宣言の簡素化:

    • MethodSpec = IdentifierList FunctionType . への変更は、インターフェースの定義において、同じ関数シグネチャを持つ複数のメソッドをカンマ区切りで一度に宣言できるようになったことを意味します。
    • これは、Go言語の設計哲学である「簡潔さ」と「読みやすさ」を追求した結果です。例えば、io.Readerio.Writer のように、ReadWrite が同じ (b Buffer) (n int, err error) シグネチャを持つ場合、以前は2行に分けて記述する必要がありましたが、この変更により Read, Write (b Buffer) (n int, err error) のように1行で記述できるようになります。これにより、インターフェースの定義がよりコンパクトになり、関連するメソッドが視覚的にグループ化されるため、コードの理解が容易になります。
  2. エクスポートメカニズムの初期定義:

    • 「Predeclared identifiers」セクションの追加は、Go言語が提供する組み込みの型、定数、関数を明確にリストアップし、言語の基本的な構成要素を定義しています。これは、Goプログラムの記述において、これらの識別子が特別な宣言なしに利用できることを保証します。
    • 「Exported declarations」セクションは、Goのパッケージシステムにおける可視性ルールに関する初期の試みを示しています。
      • export キーワードによる「無制限エクスポート」は、現在のGoの可視性ルール(大文字で始まる識別子がエクスポートされる)の前身と考えられます。このキーワードは、明示的にエクスポートしたい識別子を指定する意図があったと推測されます。
      • package キーワードによる「パッケージ制限エクスポート」は、現在のGoには存在しない概念です。これは、パッケージ内部でのみアクセス可能な識別子を明示的に指定するメカニズムとして検討された可能性があります。しかし、最終的にはGoの設計者は、識別子の命名規則(大文字/小文字)によって可視性を制御する、よりシンプルで慣用的な方法を選択しました。
    • これらの初期の定義は、Go言語がモジュール性とカプセル化をどのように実現しようとしていたかを示しており、現在のGoのパッケージシステムの基礎を築く上で重要なステップでした。
  3. 修飾識別子の導入:

    • 「Qualified identifiers」セクションの追加は、Goのパッケージシステムにおいて、他のパッケージからエクスポートされた識別子にアクセスするための標準的な方法を確立します。PackageName.identifier という構文は、Goのモジュール性を維持しつつ、異なるパッケージ間の連携を可能にするための基本的なメカニズムです。

全体として、このコミットはGo言語の初期の仕様策定における重要なマイルストーンであり、言語の簡潔さ、表現力、およびモジュール性を追求する設計思想が反映されています。特に、エクスポートに関する様々なアプローチの試行錯誤は、言語設計の複雑さと、最終的にシンプルで効果的なソリューションに到達するまでの過程を示しています。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Go言語の初期のコミット履歴とメーリングリストの議論 (公開されている場合)
  • Go言語の設計に関するブログ記事や論文 (Robert Griesemer, Rob Pike, Ken Thompsonによるもの)
  • Go言語の仕様書 doc/go_spec.txt の内容
  • GitHubのコミットページ: https://github.com/golang/go/commit/337af317813d14fd6ed0e5a62072923a6f181570