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

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

このコミットは、Go言語のコンパイラ(gc)において、関数型(TFUNC)に対してもメソッドを宣言できるようにする変更を導入しています。具体的には、コンパイラの型チェックフェーズでメソッド宣言を処理するdclmethod関数に、TFUNCケースを追加することで、この機能を実現しています。

コミット

commit 5e2c05877de1d69919b48a605799370d32f00853
Author: Russ Cox <rsc@golang.org>
Date:   Thu Feb 5 13:33:07 2009 -0800

    allow methods on funcs.

    R=ken
    OCL=24442
    CL=24442
---
 src/cmd/gc/subr.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c
index e37dce60ae..593d07cd38 100644
--- a/src/cmd/gc/subr.c
+++ b/src/cmd/gc/subr.c
@@ -1547,6 +1547,7 @@ dclmethod(Type *t)
 	case TMAP:
 	case TCHAN:
 	case TSTRING:
+	case TFUNC:
 		break;
 	}

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

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

元コミット内容

allow methods on funcs.

このコミットメッセージは簡潔に「関数にメソッドを許可する」と述べており、Go言語の型システムにおける重要な拡張を示唆しています。

変更の背景

Go言語の設計哲学の一つに、柔軟で直交性の高い型システムがあります。初期のGo言語の設計段階から、メソッドを任意の型にアタッチできる能力は重要な要素でした。これにより、従来のオブジェクト指向言語のようなクラス階層に縛られることなく、インターフェースを通じて型情報を伝達し、構造的型付けとポリモーフィズムを実現することが可能になります。

このコミットが行われた2009年2月は、Go言語がまだ活発に開発されていた初期段階にあたります。この時期には、言語の基本的な機能やセマンティクスが固められていました。関数型にメソッドを定義できることは、Goの強力な型システムをさらに強化し、より表現力豊かで整理されたコードを書くための基盤となります。例えば、特定の関数シグネチャに振る舞いを直接関連付けることで、コールバック関数やイベントハンドラなどのパターンをよりGoらしく記述できるようになります。

前提知識の解説

Go言語の型システムとメソッド

Go言語では、メソッドは特定の型に関連付けられた関数です。他の多くのオブジェクト指向言語とは異なり、Goにはクラスという概念がありません。その代わりに、任意の「名前付き型(named type)」に対してメソッドを定義できます。これは構造体(struct)だけでなく、プリミティブ型(int, stringなど)や、このコミットで追加された関数型(function type)にも適用されます。

メソッドの定義は以下の構文で行われます。

func (receiver Type) MethodName(parameters) (results) {
    // メソッドの実装
}

ここでreceiverは、メソッドが呼び出されるインスタンス(値またはポインタ)を表します。

Goコンパイラ(gc)の構造

Go言語の公式コンパイラはgc(Go Compiler)と呼ばれ、Go言語自体で書かれています。コンパイルプロセスは複数のフェーズに分かれています。

  1. 字句解析と構文解析(Parsing): ソースコードをトークンに分解し、抽象構文木(AST: Abstract Syntax Tree)を構築します。
  2. 型チェック(Type-checking): ASTを分析し、すべての型が正しく使用され、一貫していることを確認します。このフェーズで、変数宣言、関数定義、メソッド宣言などが処理され、それらの型が解決・検証されます。
  3. 中間コード生成と最適化(SSA Generation and Optimization): ASTをSSA(Static Single Assignment)形式に変換し、様々な最適化を適用します。
  4. コード生成(Code Generation): 最適化されたSSA形式を機械語に変換します。

このコミットで変更されているsrc/cmd/gc/subr.cファイルは、Goコンパイラの初期のバージョンにおける型チェックやシンボル解決に関連するサブルーチンが含まれていたと考えられます。

dclmethod関数

dclmethodは「declare method」(メソッドを宣言する)の略であると推測されます。これはGoコンパイラの内部関数であり、コンパイルプロセスの型チェックフェーズにおいて、メソッドの宣言を処理し、登録する役割を担っています。この関数は、メソッドが定義されているレシーバの型を引数として受け取り、その型がメソッドを持つことをコンパイラの内部表現に記録します。

TFUNC

TFUNCは、Goコンパイラの内部表現における「関数型(function type)」を識別するための定数または列挙値です。コンパイラは、intstring、構造体などの他の型と区別するために、TFUNCのような内部的な型識別子を使用します。

技術的詳細

このコミットの技術的な核心は、Goコンパイラのdclmethod関数が、メソッドを定義できる型のリストにTFUNC(関数型)を追加した点にあります。

dclmethod関数は、メソッドが宣言されたレシーバの型をチェックし、その型がメソッドを持つことが許可されているかどうかを判断します。変更前のコードでは、TMAP(マップ型)、TCHAN(チャネル型)、TSTRING(文字列型)など、特定の組み込み型に対してのみメソッドの宣言が許可されていました。これらの型は、Go言語の設計上、特別な振る舞いを持つため、メソッドをアタッチできる必要がありました。

このコミットでは、dclmethod関数内のswitch文にcase TFUNC:が追加されました。これにより、コンパイラは関数型に対してもメソッドが宣言されていることを正当なものとして認識し、エラーを発生させずにコンパイルを進めることができるようになります。

この変更により、Go言語では以下のようなコードが合法となります。

package main

import "fmt"

// MyFunc はカスタムの関数型を定義します
type MyFunc func(int, int) int

// Add は MyFunc 型のメソッドです
func (f MyFunc) Add(a, b int) int {
	return f(a, b)
}

// Multiply は MyFunc 型の別のメソッドです
func (f MyFunc) Multiply(a, b int) int {
	return a * b // このメソッドはレシーバfを直接呼び出さない例
}

func main() {
	// MyFunc 型の変数を宣言し、関数リテラルを代入します
	myOperation := MyFunc(func(x, y int) int {
		return x + y
	})

	// MyFunc 型のメソッドを呼び出します
	resultAdd := myOperation.Add(5, 3)
	fmt.Println("Add result:", resultAdd) // 出力: Add result: 8

	resultMultiply := myOperation.Multiply(5, 3)
	fmt.Println("Multiply result:", resultMultiply) // 出力: Multiply result: 15
}

上記の例では、MyFuncという名前付き関数型を定義し、その型に対してAddMultiplyというメソッドを定義しています。これにより、関数自体に振る舞いをカプセル化し、より表現力豊かなAPIを設計することが可能になります。

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

変更はsrc/cmd/gc/subr.cファイルのdclmethod関数内の一箇所のみです。

--- a/src/cmd/gc/subr.c
+++ b/src/cmd/gc/subr.c
@@ -1547,6 +1547,7 @@ dclmethod(Type *t)
 	case TMAP:
 	case TCHAN:
 	case TSTRING:
+	case TFUNC:
 		break;
 	}

具体的には、dclmethod関数内のswitch文にcase TFUNC:が追加されています。このswitch文は、メソッドが宣言されているレシーバの型(Type *t)をチェックし、その型がメソッドを持つことが許可されている場合にbreakで処理を続行します。TFUNCが追加されたことで、関数型もこのチェックを通過できるようになりました。

コアとなるコードの解説

dclmethod関数は、Goコンパイラの型チェックフェーズにおいて、メソッド宣言の妥当性を検証する役割を担っています。この関数は、メソッドが定義されているレシーバの型tを受け取ります。

変更前のコードでは、switch文がt->etype(型の基本要素型)を評価し、TMAPTCHANTSTRINGといった特定の型に対してのみメソッドの定義を許可していました。これらの型は、Go言語のランタイムや組み込み機能と密接に関連しており、特別な振る舞いをメソッドとして提供する必要がありました。

case TFUNC:の追加は、コンパイラが関数型を他の許可された型(マップ、チャネル、文字列)と同等に扱い、それらに対してメソッドを定義することを許可するという明確な指示です。このbreak文は、TFUNC型の場合には特別なエラー処理や追加の検証は不要であり、メソッドの宣言が有効であることを示しています。

このシンプルな変更により、Go言語の型システムはさらに柔軟になり、開発者は関数型をより強力な抽象化の手段として利用できるようになりました。これは、Goが「オブジェクト指向」の概念をクラスではなく、インターフェースとメソッドの組み合わせで実現していることの一例でもあります。

関連リンク

  • Go言語の公式ドキュメント: https://go.dev/
  • Go言語のコンパイラソースコード(現在のバージョン): https://github.com/golang/go/tree/master/src/cmd/compile
    • dclmethod関数は、現在のGoコンパイラではsrc/cmd/compile/internal/typecheck/dcl.goなどのファイルに分散・再編成されていますが、その概念は引き継がれています。

参考にした情報源リンク

  • Go compiler dclmethod TFUNC の検索結果
  • Go language methods on function types early design の検索結果
  • Go言語のコンパイルプロセスに関する一般的な情報源
  • Go言語のメソッドに関する公式ドキュメントやチュートリアル