[インデックス 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言語の歴史を解説するブログ記事などで見つかることが多いです。