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

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

このコミットは、Go言語の標準ライブラリである go/doc パッケージにおけるAPIの変更を元に戻し、クリーンアップを行うものです。特に、メソッドの表現方法に関する以前の設計判断を修正し、Func 型にメソッド固有の情報を統合することで、APIの使いやすさとコードの可読性を向上させています。

コミット

commit d571c5ca78a58489a1fd223dd6749a650668ccdc
Author: Robert Griesemer <gri@golang.org>
Date:   Wed Jan 25 16:48:06 2012 -0800

    go/doc: revert API change (per former discussion) and cleanup
    
    Separating Method from Func made the code only more complicated
    without adding much to the useability/readability of the API.
    Reverted to where it was, but leaving the new method-specific
    fields Orig and Level.
    
    Former clients (godoc) of doc.Method only used the Func fields;
    and because Func was embedded, no changes are needed with respect
    to the removal of Method.
    
    Changed type of Func.Recv from ast.Expr to string. This was a
    long-standing TODO. Also implemented Func.Orig field (another TODO).
    
    No further go/doc API changes are expected for Go 1.
    
    R=rsc, r, r
    CC=golang-dev
    https://golang.org/cl/5577043

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

https://github.com/golang/go/commit/d571c5ca78a58489a1fd223dd6749a650668ccdc

元コミット内容

このコミットは、go/doc パッケージにおいて、関数とメソッドを区別するために導入された Method 型を廃止し、以前の状態に戻すものです。以前の変更では、メソッドをより詳細に記述するために Method 型が導入されましたが、結果としてコードが複雑になり、APIの使いやすさや可読性に大きなメリットをもたらさなかったと判断されました。

具体的には、以下の変更が行われています。

  • Method 型の削除。
  • Func 型に、メソッド固有のフィールドである Orig (元のレシーバ型) と Level (埋め込みレベル) を残しつつ、メソッドの情報を統合。
  • Func.Recv フィールドの型を ast.Expr から string に変更。これは以前からのTODO(課題)でした。
  • Func.Orig フィールドの実装。これも以前からのTODOでした。

この変更により、go/doc パッケージのAPIは簡素化され、Go 1リリースに向けてこれ以上のAPI変更は予定されていないことが示されています。

変更の背景

このコミットの背景には、go/doc パッケージのAPI設計に関する議論と、Go 1リリースに向けたAPIの安定化という目標があります。

当初、go/doc パッケージでは、関数とメソッドを区別するために FuncMethod という異なる型を導入する試みがありました。これは、メソッドがレシーバを持つという特性をAPIレベルで明確に表現するためと考えられます。しかし、コミットメッセージにあるように、「MethodFunc から分離したことで、コードはより複雑になっただけで、APIの使いやすさや可読性に大きなメリットをもたらさなかった」という結論に至りました。

godoc のような既存のクライアントは、doc.MethodFunc フィールドのみを使用しており、Func が埋め込まれていたため、Method の削除によっても大きな変更は不要でした。このことは、Method 型の分離が実用上あまり意味がなかったことを示唆しています。

また、Func.Recv の型が ast.Expr であったことや、Func.Orig フィールドが未実装であったことは、以前からの「TODO」(課題)として認識されていました。ast.Expr は抽象構文木における一般的な式を表す型であり、レシーバの型を文字列として直接扱う方が、ドキュメント生成の文脈ではよりシンプルで扱いやすいと判断されたのでしょう。

Go 1のリリースが迫る中で、APIの安定性と簡潔性は非常に重要でした。このコミットは、複雑さを減らし、より直感的で使いやすいAPIに回帰することで、go/doc パッケージをGo 1の標準として確立するための最終調整の一環として行われました。

前提知識の解説

このコミットを理解するためには、以下のGo言語および関連ツールの基本的な知識が必要です。

  1. Go言語の関数とメソッド:

    • 関数 (Function): 特定のタスクを実行するコードブロック。レシーバを持ちません。
    • メソッド (Method): 特定の型に関連付けられた関数。レシーバ(func (r Type) MethodName(...)r Type の部分)を持ち、その型の値に対して呼び出されます。Goでは、構造体だけでなく、任意の型にメソッドを定義できます。
  2. Goの抽象構文木 (AST):

    • Goコンパイラは、ソースコードを解析して抽象構文木(AST)を生成します。ASTは、プログラムの構造を木構造で表現したものです。
    • go/ast パッケージは、GoのソースコードのASTを操作するための機能を提供します。
    • ast.Expr: ASTにおける式を表すインターフェース型。変数、リテラル、関数呼び出し、型など、様々な種類の式が含まれます。
    • ast.FuncDecl: 関数またはメソッドの宣言を表すASTノード。
    • ast.FieldList: 関数のパラメータや結果、メソッドのレシーバなどを表すフィールドのリスト。
    • ast.StarExpr: ポインタ型(例: *T)を表すASTノード。
    • ast.Ident: 識別子(変数名、型名など)を表すASTノード。
  3. go/doc パッケージ:

    • Goの標準ライブラリの一部で、Goのソースコードからパッケージ、型、関数、メソッドなどのドキュメントを抽出・生成するためのパッケージです。
    • godoc コマンドやGoの公式ドキュメントサイト(pkg.go.devなど)は、この go/doc パッケージを利用してドキュメントを生成しています。
    • このパッケージは、go/ast パッケージで解析されたASTを受け取り、それをより高レベルなドキュメント構造に変換します。
  4. 埋め込み (Embedding) とメソッドの継承:

    • Goでは、構造体の中に別の構造体やインターフェースを「埋め込む」ことができます。これにより、埋め込まれた型のフィールドやメソッドが、埋め込み元の型に「昇格」され、直接アクセスできるようになります。
    • メソッドの埋め込みは、コードの再利用性を高める強力なメカニズムです。go/doc パッケージは、この埋め込みによって継承されたメソッドも適切にドキュメント化する必要があります。
  5. Go 1の安定化:

    • Go 1は、Go言語の最初の安定版リリースであり、その後のGo言語の互換性保証の基盤となりました。Go 1リリース前には、APIの最終的な調整と安定化が活発に行われていました。このコミットもその一環です。

これらの知識があることで、コミットがなぜ行われたのか、そしてそれがGo言語のドキュメント生成システムにどのような影響を与えるのかを深く理解することができます。

技術的詳細

このコミットの技術的詳細は、主に go/doc パッケージ内部のデータ構造と処理ロジックの変更に集約されます。

  1. Method 型の廃止と Func 型への統合:

    • 以前は、src/pkg/go/doc/doc.goMethod という構造体が存在し、これは *Func を埋め込んでいました。つまり、メソッドは Func のすべてのフィールドを持ち、さらにメソッド固有のフィールド(OriginLevel)を追加で持つ設計でした。
    • このコミットでは、Method 型が完全に削除されました。
    • 代わりに、Type 構造体の Methods フィールドの型が []*Method から []*Func に変更されました。これは、メソッドがもはや独立した Method 型として表現されず、Func 型のインスタンスとして扱われることを意味します。
    • Func 構造体には、メソッド固有の情報を格納するための新しいフィールドが追加されました。
      • Recv string: メソッドのレシーバの型を文字列で表現します(例: "T""*T")。
      • Orig string: 埋め込みメソッドの場合、そのメソッドが元々定義されていたレシーバの型を文字列で表現します。
      • Level int: 埋め込みの深さを示します。0は埋め込みではないことを意味します。
  2. Func.Recv の型変更 (ast.Expr から string へ):

    • 以前の Func 構造体では、Recv フィールドは ast.Expr 型でした。これは、レシーバの型がASTノードとして直接保持されていたことを意味します。
    • このコミットでは、Recv の型が string に変更されました。これにより、レシーバの型情報を文字列として直接扱うことができ、ドキュメント生成の際にASTを再解析する必要がなくなります。
    • この変更に伴い、src/pkg/go/doc/reader.gorecvString(recv ast.Expr) string というヘルパー関数が追加されました。この関数は、ast.Expr 型のレシーバを受け取り、それを "T""*T" のような文字列形式に変換する役割を担います。これにより、ASTの複雑さを抽象化し、Func 型の Recv フィールドに簡潔な文字列を格納できるようになりました。
  3. メソッドセットの処理ロジックの変更:

    • src/pkg/go/doc/reader.go 内の methodSet 型は、以前は map[string]*Method でしたが、このコミットで map[string]*Func に変更されました。これは、メソッドが Func 型として直接管理されるようになったことを反映しています。
    • methodSet.set および methodSet.add メソッドのロジックも、Method 型の代わりに Func 型を直接操作するように修正されました。特に、メソッドの衝突解決(同じ名前のメソッドが異なる埋め込みレベルで存在する場合など)のロジックも Func 型に基づいて調整されています。
    • customizeRecv 関数も、*Method を受け取って *Method を返す代わりに、*Func を受け取って *Func を返すように変更されました。この関数は、埋め込みメソッドのレシーバ情報を調整するために使用されます。
  4. フィルタリングロジックの簡素化:

    • src/pkg/go/doc/filter.go に存在した filterMethods 関数が削除されました。
    • Type 構造体の Methods フィールドが []*Func になったため、メソッドのフィルタリングも filterFuncs 関数で統一的に処理できるようになりました。これにより、コードの重複が排除され、簡潔性が向上しました。

これらの変更は、go/doc パッケージの内部構造を簡素化し、メソッドの表現をより効率的かつ統一的に行うことを目的としています。特に、ast.Expr から string への変更は、ドキュメント生成のパフォーマンスとコードの保守性にも寄与すると考えられます。

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

このコミットにおけるコアとなるコードの変更箇所は以下のファイルに集中しています。

  1. src/pkg/go/doc/doc.go:

    • Method 構造体の定義が削除されました。
    • Type 構造体の Methods フィールドの型が []*Method から []*Func に変更されました。
    • Func 構造体に以下のフィールドが追加されました。
      type Func struct {
          Doc  string
          Name string
          Decl *ast.FuncDecl
      
          // methods
          // (for functions, these fields have the respective zero value)
          Recv  string // actual   receiver "T" or "*T"
          Orig  string // original receiver "T" or "*T"
          Level int    // embedding level; 0 means not embedded
      }
      
  2. src/pkg/go/doc/filter.go:

    • filterMethods 関数が削除されました。
    • filterTypes 関数内で、td.Methods = filterMethods(td.Methods, f) の呼び出しが td.Methods = filterFuncs(td.Methods, f) に変更されました。
  3. src/pkg/go/doc/reader.go:

    • methodSet 型の定義が type methodSet map[string]*Method から type methodSet map[string]*Func に変更されました。
    • recvString(recv ast.Expr) string ヘルパー関数が追加されました。
    • methodSet.set メソッド内で、Method 型のインスタンスを生成していた部分が Func 型のインスタンスを生成するように変更され、Recv, Orig フィールドが設定されるようになりました。
    • methodSet.add メソッドの引数と内部ロジックが *Method から *Func に変更されました。
    • sortedFuncssortedMethods のロジックが統合され、sortedFuncs がメソッドも処理するように変更されました。
    • customizeRecv 関数の引数と戻り値の型が *Method から *Func に変更され、内部で Func 型のフィールドを直接操作するように修正されました。

これらの変更は、go/doc パッケージのデータモデルと、そのデータモデルを操作するロジックの根本的な変更を示しています。

コアとなるコードの解説

src/pkg/go/doc/doc.go の変更

このファイルは、go/doc パッケージが公開する主要なデータ構造を定義しています。

  • Method 型の削除は、メソッドを独立したエンティティとして扱うのではなく、関数の一種として Func 型で表現するという設計思想への回帰を示しています。
  • Type 構造体の Methods フィールドが []*Func になったことで、型が持つメソッドのリストは、通常の関数と同じ Func 型のポインタの配列として扱われるようになりました。
  • Func 構造体への Recv, Orig, Level フィールドの追加は、メソッド固有の情報を Func 型自体が保持できるようにするためのものです。
    • Recv: メソッドのレシーバの型(例: MyType*MyType)を文字列で保持します。これは、ドキュメント表示の際に直接利用できる形式です。
    • Orig: 埋め込みによって「継承」されたメソッドの場合、そのメソッドが元々定義されていたレシーバの型を保持します。これにより、メソッドの出自を追跡できます。
    • Level: 埋め込みの深さを示します。0は直接定義されたメソッド、1は1段階埋め込まれた型から継承されたメソッド、といった具合です。これにより、メソッドの「近さ」を判断できます。

src/pkg/go/doc/filter.go の変更

このファイルは、ドキュメント要素をフィルタリングするための関数を提供します。

  • filterMethods の削除と filterFuncs への統合は、Method 型が廃止され、メソッドが Func 型として扱われるようになった結果です。これにより、関数とメソッドのフィルタリングロジックが統一され、コードの重複が排除されました。これは、APIの簡素化と内部実装の整合性向上に貢献します。

src/pkg/go/doc/reader.go の変更

このファイルは、GoのASTを読み込み、go/doc パッケージのデータ構造に変換する主要なロジックを含んでいます。

  • methodSet の型が map[string]*Func に変更されたことは、内部でメソッドを管理する際にも Func 型を直接使用するようになったことを意味します。これにより、Method 型を介した間接的な処理が不要になり、コードが簡素化されます。
  • recvString ヘルパー関数の導入は、Func.Recvstring 型にするための重要な変更です。この関数は、ast.Expr 型のレシーバ(ASTノード)を受け取り、それを人間が読める文字列形式(例: "T""*T")に変換します。これにより、ASTの複雑な構造を直接扱う必要がなくなり、Func 型のデータがよりシンプルになります。
  • methodSet.set および methodSet.add メソッドの変更は、ASTから読み取った関数やメソッドの情報を Func 型のインスタンスとして methodSet に追加するロジックを反映しています。特に、Func 型の Recv, Orig, Level フィールドがここで適切に設定されるようになります。
  • customizeRecv 関数の変更は、埋め込みメソッドのレシーバ情報を調整する際に、Func 型のインスタンスを直接操作するようにしたものです。これにより、Method 型を介した変換が不要になり、処理が直接的になります。

これらの変更は、go/doc パッケージがGoのソースコードからドキュメント情報を抽出し、内部で表現する方法を根本的に見直した結果であり、APIの簡素化と内部実装の効率化を両立させています。

関連リンク

参考にした情報源リンク

  • コミットメッセージと差分 (git diff)
  • Go言語の公式ドキュメント (go/doc, go/ast パッケージの解説)
  • Go言語のソースコード (src/pkg/go/doc/)
  • Go言語の歴史に関する一般的な知識 (Go 1の安定化プロセスなど)