The Go Programming Language Specificationの翻訳、12回目です。
前回までの訳はGo言語仕様[日本語訳]にまとめてあります。


パッケージ

Go言語のプログラムは、複数のパッケージをひとつにリンクすることによって作られます。さらに各パッケージは、そのパッケージに所属する定数、型、変数、関数を宣言している、ひとつ以上のソースファイルから構成されます。これらパッケージ内の要素は、同一パッケージ内であれば、別ファイルからアクセスすることができます。また要素がエクスポートされていれば、他パッケージからアクセスすることができます。

ソースファイルの構成

各ソースファイルは、そのファイルがどのパッケージに属しているかを定義するパッケージ文で始まります。以降は必須ではありませんが、ソースファイル内で使用したいパッケージを宣言する一連のインポート宣言。さらに、関数、型、変数、定数の一連の定義が続きます。

SourceFile       = PackageClause { ImportDecl [ ";" ] } { TopLevelDecl [ ";" ] } .

パッケージ文

各ソースファイルはパッケージ文で始まり、そのファイルが所属するパッケージを定めます。

PackageClause  = "package" PackageName .
PackageName    = identifier .

このパッケージ名(PackageName)はブランク識別子であってはなりません。

package math

パッケージの実装は、同じパッケージ名を共有するファイル群によって構成されます。実装によっては、同一パッケージ内のすべてのソースファイルが、同一ディレクトリ内に置かれている必要があります。

インポート宣言

インポート宣言によって、インポートされたパッケージ内でエクスポートされている識別子を使うことで、インポート宣言を記述しているソースファイルから、それらにアクセス可能になります。インポートでは、アクセスするために使用する識別子(PackageName)、およびインポートされるパッケージを指定するImportPathを指定します。

ImportDecl       = "import" ( ImportSpec | "(" [ ImportSpecList ] ")" ) .
ImportSpecList   = ImportSpec { ";" ImportSpec } [ ";" ] .
ImportSpec       = [ "." | PackageName ] ImportPath .
ImportPath       = StringLit .

PackageNameは、限定付き識別子を使い、そのパッケージでエクスポートされている識別子にアクセスするために使用されます。PackageNameは、ファイルブロック内で宣言されます。PackageNameを省略したときは、インポートされた側パッケージのパッケージ文で指定されている識別子がデフォルトとして使用されます。名前の代わりにピリオド(.)が指定されたときは、そのパッケージでエクスポートされている全識別子が、カレントのファイルのファイルブロックで宣言され、プレフィックスなしでアクセスできるようになります。

ImportPathの解釈は実装に依存しますが、一般的には、コンパイル済みパッケージのパス名の一部であり、パッケージのリポジトリへの相対パスです。

仮に、Sin関数をエクスポートしているmathパッケージがあり、"lib/math"というパスにそのコンパイル済みパッケージがインストールされているとします。下の表では、各インポート方法でこのパッケージをインポートしたとき、そのファイル内でSin関数がどのようにしてアクセスされるかを説明します。

インポート宣言                Sinのローカル名

import   "lib/math"         math.Sin
import M "lib/math"         M.Sin
import . "lib/math"         Sin

インポート宣言は、インポート「する側」と「される側」の依存関係を宣言します。自分自身のパッケージをインポートすること、またはインポートしたパッケージ内でエクスポートされている識別子を一切参照しないことは誤った使い方です。インポートによる副作用(初期化)のためだけにパッケージをインポートするときは、パッケージ名としてブランク識別子を使ってください。

import _ "lib/math"

パッケージサンプル

これは、並列処理による「素数のふるい」を実装した、Goのパッケージ一式です。

package main

import "fmt"

// 2,3,4,...というシーケンスをチャネル'ch'に送信
func generate(ch chan<- int) {
	for i := 2; ; i++ {
		ch <- i;	// 'i'をチャネル'ch'に送信
	}
}

// チャネル'in'の値をチャネル'out'にコピー
// ただし'prime'で割り切れる値を除く
func filter(src <-chan int, dst chan<- int, prime int) {
	for i := range src {	// 'src'から受信した値でループ
		if i%prime != 0 {
			dst <- i;	// 'i'をチャネル'dst'に送信
		}
	}
}

// 素数のふるい:フィルターを数珠つなぎに組み合わせて処理する
func sieve() {
	ch := make(chan int);	// 新しいチャネルを作成
	go generate(ch);	// generate()をサブプロセスとして開始
	for {
		prime := <-ch;
		fmt.Print(prime, "\n");
		ch1 := make(chan int);
		go filter(ch, ch1, prime);
		ch = ch1;
	}
}

func main() {
	sieve();
}