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

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

このコミットは、Go言語のコードベース全体における関数型の定義と関数の参照方法に関する重要な構文変更を適用するものです。具体的には、関数型を明示的にfuncキーワードを用いて定義するように変更し、関数を値として扱う際のポインタ記法(*&)を削除しています。これにより、Go言語における関数がより自然な形でファーストクラスオブジェクトとして扱われるようになります。

コミット

commit 4cf7711568da9510bae09338e03a66049767c851
Author: Russ Cox <rsc@golang.org>
Date:   Fri Jan 30 14:39:31 2009 -0800

    update go code tree to new func rules.
    
    R=r
    DELTA=367  (111 added, 59 deleted, 197 changed)
    OCL=23957
    CL=23960

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

https://github.com/golang/go/commit/4cf7711568da9510bae09338e03a66049767c851

元コミット内容

このコミットの元々の内容は、Go言語のコードツリーを新しい関数ルールに更新することです。これは、Go言語の初期開発段階における言語仕様の進化の一環であり、関数型の定義方法と関数値の扱いに関する構文が変更されたことを示しています。

変更の背景

Go言語は、その設計段階において、シンプルさ、効率性、並行処理の容易さを追求していました。初期のGo言語では、関数型を定義する際にC言語のようなポインタ記法(例: type binOp (a, b int) int;)を使用したり、関数を変数に代入したり引数として渡したりする際に明示的にアドレス演算子&を使用する必要がありました。

しかし、関数がファーストクラスオブジェクトであるというGoの設計思想をより明確に反映し、コードの可読性と記述性を向上させるために、この構文が変更されることになりました。新しいルールでは、関数型を定義する際にfuncキーワードを導入し、関数を変数に代入したり引数として渡したりする際に&を不要とすることで、より直感的でGoらしい記述が可能になります。この変更は、言語の成熟と、開発者がより自然に関数を扱えるようにするための重要なステップでした。

前提知識の解説

このコミットを理解するためには、以下のGo言語の基本的な概念と、当時の言語設計に関する知識が役立ちます。

  1. 関数型 (Function Types): Go言語では、関数もデータ型の一種として扱われます。つまり、関数のシグネチャ(引数の型と戻り値の型)を型として定義し、その型を持つ関数を変数に代入したり、関数の引数として渡したり、関数の戻り値として返したりすることができます。

    • 変更前: type MyFunc (arg1 Type1, arg2 Type2) ReturnType; のようなC言語の関数ポインタ宣言に似た構文が使われていた可能性があります。
    • 変更後: type MyFunc func(arg1 Type1, arg2 Type2) ReturnType; のように、funcキーワードが明示的に導入されました。
  2. ファーストクラス関数 (First-Class Functions): Go言語において関数は「ファーストクラス」です。これは以下のことを意味します。

    • 関数を変数に代入できる。
    • 関数を他の関数の引数として渡せる(高階関数)。
    • 関数を他の関数の戻り値として返せる。
    • 関数リテラル(匿名関数)をその場で定義できる。 このコミットは、このファーストクラス関数の概念をより自然な構文で表現するための変更です。
  3. ポインタ (Pointers): Go言語にはポインタがありますが、C/C++のようなポインタ演算はできません。ポインタは主に、値のコピーではなく参照を渡すために使用されます。

    • 変更前: 関数を変数に代入したり、引数として渡したりする際に、関数の「アドレス」を示すために&演算子が必要だった可能性があります(例: &myFunction)。これは、関数が直接値として扱われるのではなく、そのアドレスを介して参照されるという、よりC言語的な考え方に基づいていたことを示唆しています。
    • 変更後: 関数は直接値として扱われるため、&演算子は不要になりました。これにより、関数が他のデータ型(整数、文字列など)と同様に、よりシームレスに扱えるようになります。
  4. once.Do関数: syncパッケージ(当時はlib/once.go)のonce.Do関数は、指定された関数が一度だけ実行されることを保証するためのものです。この関数も、引数として関数を受け取ります。このコミットでは、once.Do(&_LoadConfig)のような呼び出しがonce.Do(_LoadConfig)に変更されており、関数を引数として渡す際の&が不要になったことを示しています。

技術的詳細

このコミットの技術的詳細は、Go言語のコンパイラとランタイムが関数型と関数値をどのように解釈し、処理するかという根幹に関わる変更です。

変更前は、関数型を定義する際に、例えばtype binOp (a, b int) int;のように、引数リストと戻り値の型を括弧で囲む形式が採用されていました。これは、C言語の関数ポインタの宣言(例: typedef int (*binOp)(int a, int b);)に影響を受けたものと考えられます。この構文では、binOpが「intを2つ引数にとりintを返す関数へのポインタ」を意味する型として解釈されていました。

また、関数を変数に代入したり、他の関数に引数として渡したりする際には、&演算子を使って関数のアドレスを取得する必要がありました(例: var f *() int = &myFunction;)。これは、関数がメモリ上の特定のアドレスに存在するコードブロックとして扱われ、そのアドレスを介して間接的に呼び出されるという、低レベルな視点に基づいています。

このコミットによって導入された新しいルールは、以下の点で大きく異なります。

  1. 関数型の明示的なfuncキーワード: type binOp func(a, b int) int; のように、funcキーワードを導入することで、その型が「関数」であることをより明確に示します。これにより、言語のセマンティクスがより直感的になり、コードの意図が明確になります。
  2. 関数値の直接的な扱い: 関数を変数に代入したり、引数として渡したりする際に、&演算子が不要になりました。これは、Go言語が関数を「値」として直接扱えるようになったことを意味します。コンパイラは、関数名が使われた際に、その関数のコードブロックへの参照(またはクロージャの場合はその環境を含む構造体への参照)を自動的に生成するようになります。これにより、関数は整数や文字列と同様に、他のデータ型と等しく扱われるファーストクラスのエンティティとしての地位を確立します。
  3. リフレクションAPIへの影響: src/lib/reflect/value.goの変更は、Goのリフレクション機能が関数型をどのように扱うかにも影響を与えます。creatorFn型が*(typ Type, addr Addr) Valueからfunc(typ Type, addr Addr) Valueに変更され、creatorマップの初期化でも&が削除されています。これは、リフレクションシステムが関数をより直接的に操作できるようになることを示唆しています。
  4. テストコードの更新: 多数のテストファイルが変更されており、新しい関数ルールに準拠するように更新されています。特に、test/func4.gotest/func5.goという新しいテストファイルが追加されており、これらは新しい関数型の定義と関数値の扱いに関する挙動を検証するためのものです。test/fixedbugs/bug029.gotest/fixedbugs/bug134.goといった古いバグテストが削除されているのは、これらのバグが新しい関数ルールによって解決されたか、あるいは関連する古い構文が廃止されたためと考えられます。

この変更は、Go言語の初期段階における言語設計の洗練化の一例であり、より一貫性があり、使いやすい言語を目指すGo開発チームの姿勢を反映しています。

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

このコミットにおけるコアとなるコードの変更は、主に以下のパターンに集約されます。

  1. 関数型の定義:

    • 変更前: type TypeName (param1 Type1, ...) ReturnType;
    • 変更後: type TypeName func(param1 Type1, ...) ReturnType; 例: doc/progs/server.go
    -type binOp (a, b int) int;
    +type binOp func(a, b int) int;
    
  2. 関数を引数として渡す際のポインタ記法の削除:

    • 変更前: funcName(&functionReference)
    • 変更後: funcName(functionReference) 例: src/lib/net/dnsclient.go
    -	once.Do(&_LoadConfig);
    +	once.Do(_LoadConfig);
    
  3. 関数を変数に代入する際のポインタ記法の削除:

    • 変更前: varName = &functionReference
    • 変更後: varName = functionReference 例: src/lib/reflect/value.go
    -	MissingKind : &missingCreator,
    +	MissingKind : missingCreator,
    

    および test/ken/ptrfun.go

    -	c.x = &g;
    +	c.x = g;
    
  4. 関数型をフィールドとして持つ構造体の定義:

    • 変更前: FieldName *(param1 Type1, ...) ReturnType;
    • 変更後: FieldName func(param1 Type1, ...) ReturnType; 例: src/lib/bufio_test.go
    -	fn *([]byte) io.Read;
    +	fn func([]byte) io.Read;
    

これらの変更は、Go言語の様々な標準ライブラリファイル(bufio_test.go, http/server.go, net/, once.go, reflect/value.go, testing.go, time/zoneinfo.go)およびテストファイルにわたって広範囲に適用されています。

コアとなるコードの解説

このコミットの核心は、Go言語における関数が「ファーストクラスの値」として扱われるという設計原則を、より直接的かつ簡潔な構文で表現することにあります。

以前のGo言語の構文では、関数型を定義する際にtype binOp (a, b int) int;のように、関数シグネチャを括弧で囲む形式が用いられていました。これは、C言語の関数ポインタの型定義(例: typedef int (*binOp)(int, int);)に似ており、関数が「ポインタ」として扱われることを示唆していました。また、関数を変数に代入したり、他の関数に引数として渡したりする際には、&演算子を使って明示的に関数のアドレスを取得する必要がありました(例: once.Do(&_LoadConfig);)。これは、関数がメモリ上のアドレスを持つエンティティであり、そのアドレスを介して操作されるという、より低レベルな視点に基づいています。

このコミットによって導入された新しい構文では、以下の点が改善されました。

  1. funcキーワードによる関数型の明示: type binOp func(a, b int) int; のように、funcキーワードを導入することで、その型が「関数」であることを明確に宣言します。これにより、コードの可読性が向上し、型が何を表しているのかが一目でわかるようになります。これは、Go言語の型システムにおける関数型の位置付けをより明確にするものです。

  2. &演算子の不要化: 関数を変数に代入したり、引数として渡したりする際に、&演算子が不要になりました(例: once.Do(_LoadConfig);)。これは、Go言語が関数を他のプリミティブ型(整数、文字列など)と同様に、直接「値」として扱えるようになったことを意味します。コンパイラは、関数名が使われた際に、その関数のコードブロックへの適切な参照を自動的に生成するようになります。これにより、関数はより自然に、そしてシームレスに、Go言語のプログラム内で操作できるようになります。

この変更は、Go言語の設計哲学である「シンプルさ」と「明瞭さ」を追求した結果です。関数をより直感的に扱えるようにすることで、高階関数やクロージャといった強力な機能が、よりGoらしい自然な形で利用できるようになり、コードの表現力と柔軟性が向上しました。これは、Go言語が現代的なプログラミングパラダイムをサポートするための重要な進化の一歩と言えます。

関連リンク

  • Go言語の公式ドキュメント(当時のものを見つけるのは難しいですが、現在の関数に関するドキュメントは参考になります)
  • Go言語の初期の設計に関する議論やメーリングリストのアーカイブ(Go言語の進化の過程を追うことができます)

参考にした情報源リンク