[インデックス 1199] ファイルの概要
このコミットは、Go言語の初期開発段階における重要なリファクタリングを示しています。具体的には、これまで各モジュールで独自に実装されていたリスト構造(AST.List)を、新しく導入された標準のarrayパッケージ(array.Arrayおよびarray.IntArray)に置き換えることで、コードの共通化と標準化を図っています。これにより、コードの重複を排除し、保守性を向上させ、将来的な言語機能の進化に備える基盤を構築しています。
コミット
commit fcdcf33a71c9c452e6c0e52fe77449dd6d49a231
Author: Robert Griesemer <gri@golang.org>
Date: Wed Nov 19 16:49:50 2008 -0800
- array-ify code, remove local implementation
R=r
OCL=19648
CL=19651
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/fcdcf33a71c9c452e6c0e52fe77449dd6d49a231
元コミット内容
このコミットの主な内容は、Go言語の初期のコードベースにおいて、抽象構文木(AST)の処理、コンパイル、パース、プリンター、タブライター、およびアンタブ処理に関連する複数のファイルで、カスタム実装されていたList型を、新しく導入されたarrayパッケージの型(array.Arrayおよびarray.IntArray)に置き換えることです。これにより、ローカルなリスト実装が削除され、より標準化されたデータ構造が利用されるようになりました。
変更の背景
このコミットが行われた2008年11月は、Go言語がまだ一般に公開される前の、活発な初期開発段階にありました。当時のGo言語には、現在のような成熟した組み込みのスライス([]T)や配列([N]T)の概念が完全に確立されていなかった可能性があります。そのため、コードベースの様々な箇所で、リストのような動的なコレクションを扱うために独自のList構造が定義され、使用されていました。
しかし、このようなローカルな実装は、コードの重複、一貫性の欠如、そして将来的な最適化や機能追加の妨げとなる可能性があります。このコミットは、Go言語の標準ライブラリとしてarrayパッケージが導入されたことを受け、既存のカスタムList実装をこの新しい標準的なarrayパッケージに移行することで、以下の目的を達成しようとしています。
- コードの共通化と標準化: 複数の場所で似たようなリスト操作ロジックが重複するのを防ぎ、一貫したAPIを提供します。
- 保守性の向上: 標準ライブラリの利用により、コードの理解が容易になり、バグの発生を抑制し、将来的なメンテナンスコストを削減します。
- パフォーマンスの改善(可能性): 標準ライブラリの
arrayパッケージは、言語開発者によって最適化されている可能性があり、カスタム実装よりも効率的な操作を提供することが期待されます。 - 言語の進化への対応: Go言語のデータ構造の設計が進化する中で、より汎用的で効率的な
arrayパッケージへの移行は、言語全体の健全な発展に寄与します。
前提知識の解説
このコミットを理解するためには、以下の概念が前提となります。
- Go言語の初期のデータ構造: 現代のGo言語では、動的なサイズのシーケンスには「スライス」(
[]T)が、固定サイズのシーケンスには「配列」([N]T)が主に用いられます。しかし、このコミットが行われた時期は、これらの概念がまだ初期段階にあり、arrayパッケージのような形で提供されていた可能性があります。このarrayパッケージは、現在のGo言語の組み込みスライスや配列の直接的な前身、あるいはその設計思想に影響を与えたものと考えられます。 - 抽象構文木(AST: Abstract Syntax Tree): プログラミング言語のソースコードを解析して得られる、プログラムの構造を木構造で表現したものです。コンパイラやインタープリタ、コード分析ツールなどで広く利用されます。ASTのノードは、変数宣言、式、文、関数定義など、プログラムの様々な要素を表します。これらの要素はしばしばリストとして管理されます(例: 関数内の文のリスト、構造体のフィールドのリスト)。
- コンパイラのフロントエンド: ソースコードを解析し、ASTを生成する部分を指します。これには、字句解析(Scanner)、構文解析(Parser)、意味解析などが含まれます。
- コードプリンター: ASTを読み取り、整形されたソースコードを出力するツールです。このコミットで変更されている
prettyパッケージは、Go言語のコード整形ツール(gofmtの前身または関連プロジェクト)の一部であった可能性が高いです。 import文: Go言語において、他のパッケージで定義された機能を利用するために使用します。このコミットでは、arrayパッケージをインポートすることで、その機能を利用可能にしています。panic: Go言語におけるランタイムエラーの一種で、プログラムの実行を停止させます。カスタムList実装のPopメソッドで、空のリストから要素を削除しようとした場合にpanicを発生させていた箇所が、標準のarrayパッケージに移行された後も同様のロジックが維持されているかどうかが注目されます。
技術的詳細
このコミットの技術的な核心は、Go言語の初期のコードベースにおけるカスタムリスト実装から、より標準化されたarrayパッケージへの移行です。
-
カスタム
List構造の削除:usr/gri/pretty/ast.goから、Listという名前のカスタム構造体とその関連メソッド(Init,len,at,last,set,Add,Pop,Clear,NewList)が完全に削除されています。これは、これらの機能がarrayパッケージによって提供されるようになったため、もはや不要になったことを意味します。 -
arrayパッケージのインポート:usr/gri/pretty/ast.go,usr/gri/pretty/compilation.go,usr/gri/pretty/parser.go,usr/gri/pretty/printer.go,usr/gri/pretty/tabwriter.go,usr/gri/pretty/untab.goの各ファイルで、import "array"が追加されています。これにより、これらのファイル内でarrayパッケージの型や関数を利用できるようになります。特にtabwriter.goでは、arrayパッケージからarray.IntArrayもインポートされています。 -
型定義の変更: ASTノードの構造体定義において、
*List型で宣言されていたフィールドが*array.Array型に変更されています。例えば、AST.Exprのblockフィールド、AST.Typeのlistフィールド、AST.Statのblockフィールド、AST.Declのlistフィールド、AST.Programのdeclsおよびcommentsフィールドなどがこれに該当します。これにより、これらのフィールドがカスタムListではなく、標準のarray.Arrayインスタンスを保持するようになります。 -
メソッド呼び出しの変更: カスタム
Listのメソッド呼び出しが、array.Arrayの対応するメソッド呼び出しに置き換えられています。list.len()->list.Len(): リストの長さを取得するメソッド。list.at(i)->list.At(i): 指定されたインデックスの要素を取得するメソッド。list.Add(x)->list.Push(x): 要素をリストの末尾に追加するメソッド。list.Pop()->list.Pop(): リストの末尾から要素を削除し、その要素を返すメソッド。list.set(i, x)->list.Set(i, x): 指定されたインデックスの要素を更新するメソッド。
-
tabwriterパッケージにおける変更:usr/gri/pretty/tabwriter.goでは、Vector.Vector型がarray.Array型に、Vector.IntArray型がarray.IntArray型に置き換えられています。これは、tabwriterパッケージが内部で利用していたコレクションも、arrayパッケージに統一されたことを示しています。また、IO.WriteやOS.FDなどの型も、より短いパッケージ名(io.Write,os.FD)に変更されており、Go言語のパッケージ命名規則の進化も垣間見えます。
これらの変更は、Go言語の初期段階における標準ライブラリの整備と、言語全体の設計思想の成熟を示すものです。カスタム実装から標準ライブラリへの移行は、コードベースの品質と一貫性を高める上で非常に重要です。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更は、主に以下のファイルと箇所に集中しています。
-
usr/gri/pretty/ast.go:- カスタム
List構造体とそのメソッド(Init,len,at,last,set,Add,Pop,Clear,NewList)の定義が削除されました。 Expr、Type、Stat、Decl、Program構造体内の*List型のフィールドが*array.Array型に変更されました。func (x *Expr) len() intがfunc (x *Expr) Len() intに変更され、list.len()の呼び出しがlist.Len()に変更されました。list.at(i)の呼び出しがlist.At(i)に変更されました。
- カスタム
-
usr/gri/pretty/compilation.go:import "array"が追加されました。AddDeps関数のwset引数の型が*AST.Listから*array.Arrayに変更されました。prog.decls.len()がprog.decls.Len()に、prog.decls.at(i)がprog.decls.At(i)に変更されました。wset.Add(src)がwset.Push(src)に変更されました。AST.NewList()がarray.New(0)に変更されました。wset.len()がwset.Len()に変更されました。
-
usr/gri/pretty/parser.go:import "array"が追加されました。comments *AST.Listがcomments *array.Arrayに変更されました。P.comments.Add(...)がP.comments.Push(...)に変更されました。AST.NewList()がarray.New(0)に変更されました。ParseVarDeclList、ParseParameterList、ParseMethodSpec、ParseInterfaceType、ParseStructType、ParseStatementList、ParseBlock、ParseSwitchStat、ParseSelectStat、ParseDecl、ParseProgramなどの関数で、*AST.List型の引数や戻り値が*array.Array型に変更され、内部のリスト操作(len,Add,set)がLen,Push,Setにそれぞれ変更されました。
-
usr/gri/pretty/printer.go:import "array"が追加されました。clist *AST.Listがclist *array.Arrayに変更されました。P.clist.at(P.cindex)がP.clist.At(P.cindex)に変更されました。P.clist.len()がP.clist.Len()に変更されました。Parameters、Fields、Block、StatementList、Declaration、Programなどの関数で、*AST.List型の引数が*array.Array型に変更され、内部のリスト操作(len,at)がLen,Atにそれぞれ変更されました。
-
usr/gri/pretty/tabwriter.go:import文が変更され、OS "os",IO "io",Vector "vector"がos,io,arrayに変更されました。lines Vector.Vectorがlines array.Arrayに、widths Vector.Vectorがwidths array.IntArrayに変更されました。b.lines.Append(Vector.New())がb.lines.Push(array.NewIntArray(0))に変更されました。b.lines.Init()がb.lines.Init(0)に、b.widths.Init()がb.widths.Init(0)に変更されました。b.lines.At(i).(*Vector.Vector)がb.lines.At(i).(*array.IntArray)に変更されました。b.lines.At(b.lines.Len() - 1).(*Vector.Vector)がb.lines.At(b.lines.Len() - 1).(*array.IntArray)に変更されました。line.At(j).(int)がline.At(j)に変更されました。b.widths.Append(width)がb.widths.Push(width)に変更されました。b.widths.Remove(b.widths.Len() - 1)がb.widths.Pop()に変更されました。b.LastLine().Append(b.width)がb.LastLine().Push(b.width)に変更されました。b.lines.Reset()がb.lines.Init(0)に変更されました。OS.Errorがos.Errorに変更されました。IO.Writeがio.Writeに変更されました。
-
usr/gri/pretty/untab.go:import文が変更され、OS "os",IO "io",Flag "flag",Fmt "fmt",TabWriter "tabwriter"がos,io,flag,fmt,tabwriterに変更されました。Flag.Boolがflag.Boolに、Flag.Intがflag.Intに変更されました。Fmt.printfがfmt.printfに変更されました。OS.FDがos.FDに変更されました。IO.Copynがio.Copyに変更されました。TabWriter.MakeTabWriterがtabwriter.MakeTabWriterに変更されました。Flag.Parse()がflag.Parse()に、Flag.NArg()がflag.NArg()に、Flag.Arg(i)がflag.Arg(i)に変更されました。OS.Openがos.Openに変更されました。OS.Stdoutがos.Stdoutに、OS.Stdinがos.Stdinに変更されました。
これらの変更は、Go言語の初期のコードベース全体で、カスタムリスト実装から標準のarrayパッケージへの統一的な移行が行われたことを明確に示しています。
コアとなるコードの解説
このコミットの核心は、Go言語の初期のコードベースにおけるカスタムList実装を、新しく導入されたarrayパッケージに置き換えるという、広範なリファクタリングです。
usr/gri/pretty/ast.goにおける変更の解説:
このファイルは、Go言語の抽象構文木(AST)の定義を含んでいます。以前は、ASTノード内のリスト構造(例えば、関数リテラルのステートメントブロック、型定義のフィールドリスト、宣言のリストなど)を表現するために、Listというカスタム構造体が使用されていました。
// 削除されたカスタムList構造体
export type List struct {
a *[] Any;
}
// 削除されたListのメソッド群
func (p *List) Init() { ... }
func (p *List) len() int { ... }
func (p *List) at(i int) Any { ... }
func (p *List) Add(x Any) { ... }
// ... その他
このコミットでは、上記のカスタムList構造体とそれに関連するすべてのメソッドが削除されました。代わりに、import "array"が追加され、List型を使用していたすべてのフィールドがarray.Array型に変更されました。
例えば、Expr構造体のblockフィールドは、*Listから*array.Arrayに変更されています。
--- a/usr/gri/pretty/ast.go
+++ b/usr/gri/pretty/ast.go
@@ -117,11 +36,11 @@ export type Expr struct {
// TODO find a more space efficient way to hold these
s string; // identifiers and literals
t *Type; // type expressions, function literal types
- block *List; // stats for function literals
+ block *array.Array; // stats for function literals
}
また、カスタムListのメソッド呼び出しも、array.Arrayの対応するメソッドに置き換えられています。例えば、len()メソッドはLen()に、at()メソッドはAt()に、Add()メソッドはPush()に、set()メソッドはSet()に、NewList()はarray.New(0)にそれぞれ変更されています。
--- a/usr/gri/pretty/ast.go
+++ b/usr/gri/pretty/ast.go
@@ -169,14 +88,17 @@ export type Type struct {
mode int; // channel mode
key *Type; // receiver type, map key
elt *Type; // array element, map or channel value, or pointer base type, result type
- list *List; // struct fields, interface methods, function parameters
+ list *array.Array; // struct fields, interface methods, function parameters
}
func (t *Type) nfields() int {
+\tif t.list == nil {
+\t\treturn 0;
+\t}
\tnx, nt := 0, 0;
-\tfor i, n := 0, t.list.len(); i < n; i++ {\n-\t\tif t.list.at(i).(*Expr).tok == Scanner.TYPE {\n+\tfor i, n := 0, t.list.Len(); i < n; i++ {\n+\t\tif t.list.At(i).(*Expr).tok == Scanner.TYPE {\
\t\t\tnt++;
\t\t} else {
\t\t\tnx++;
この変更は、ASTの内部表現が、カスタム実装から標準ライブラリの提供するデータ構造へと移行したことを意味します。これにより、ASTのコードがより簡潔になり、arrayパッケージの最適化や将来の改善の恩恵を受けられるようになります。
その他のファイルにおける変更の解説:
compilation.go, parser.go, printer.go, tabwriter.go, untab.goの各ファイルでも同様のパターンが見られます。
compilation.go: コンパイルプロセスにおける依存関係の管理でAST.Listが使用されていましたが、これもarray.Arrayに置き換えられ、AddがPushに、NewListがarray.New(0)に変更されています。parser.go: ソースコードのパース時にコメントや宣言のリストを管理するためにAST.Listが使われていましたが、これもarray.Arrayに移行し、AddがPushに、NewListがarray.New(0)に変更されています。printer.go: ASTを整形して出力する際に、コメントリストやパラメータリスト、フィールドリストなどを扱うためにAST.Listが使われていましたが、これもarray.Arrayに置き換えられ、atがAtに、lenがLenに変更されています。tabwriter.go: テキストのタブ整形を行うユーティリティで、内部的にVector.Vectorというコレクション型が使われていましたが、これもarray.Arrayおよびarray.IntArrayに置き換えられています。これは、Go言語の初期の標準ライブラリにおけるコレクション型の統一化の動きを示唆しています。untab.go: タブをスペースに変換するユーティリティで、tabwriterパッケージを利用しています。このファイルでは、import文のパッケージエイリアスが削除され、より現代のGo言語に近い形式(例:OS "os"から"os")に変更されています。また、IO.Copynがio.Copyに変更されるなど、標準ライブラリのAPIの進化も反映されています。
これらの変更は、Go言語の初期の設計段階において、言語のコア部分で利用されるデータ構造が、カスタム実装からより汎用的で標準化されたライブラリへと移行していく過程を示しています。これは、言語の成熟と、より堅牢で保守性の高いコードベースを構築するための重要なステップです。
関連リンク
- Go言語の公式ウェブサイト: https://golang.org/
- Go言語の初期のコミット履歴(GitHub): https://github.com/golang/go/commits/master?after=fcdcf33a71c9c452e6c0e52fe77449dd6d49a231+34 (このコミットの周辺の履歴を確認できます)
参考にした情報源リンク
- Go言語の公式ドキュメント(現在のスライスと配列に関する情報): https://go.dev/blog/slices-intro
- Go言語の歴史に関する情報(非公式リソースを含む): Go言語の初期の設計に関する情報は、公式ドキュメントよりも、当時のメーリングリストのアーカイブや、Go言語の歴史を解説するブログ記事などで見つかることが多いです。