最近は色んなツールがどんどんGoで開発されてますよね。
NetflixのChaosMonkeyなど、既存ツールをGoで書き直す事例もあるようです。
いまどきの言語には珍しく、かなりシンプルな機能しかないGoですが、なぜこんなに流行っているのか確かめるために触ってみました。
その際気になったことなどのまとめです。
Go環境
go version go1.7.3 darwin/amd64
パッケージ管理
GoにはRubyGemsやMaven的なライブラリの中央管理リポジトリがありません。
かわりに、GithubやBitbucketなどのGitホスティングサービスがその役割を果たします。
go get
ライブラリを使う際は、go get github.com/user/repo
を実行することで、$GOPATH/src/github.com/user/repo
にリポジトリがcloneされ、コード上ではimport github.com/user/repo/package
で参照できるようになります。
vendoring
go get
だと、goプロジェクトごとに別のバージョンのライブラリを使い分けることができません。
Go 1.6以降では、goプロジェクト内のvendor
ディレクトリがimportの探索パスに入るため、
git submodule
などでvendor/github.com/user/repo
にリポジトリを置くことで、バージョンを固定できます。例: https://github.com/ocadaruma/go-javactl
Maybe
Goには、HaskellでいうMaybe / ScalaでいうOption的なものがありません。
例えば「必須でない設定項目」など、存在しないかもしれない値を表現する際は、ポインタ型で持っておくか、zero valueとの比較を行うケースが多いようです。
// ポインタの例
type MySQLConfig struct {
Host string
User string
Password string
Port *int
}
func connect(config MySQLConfig) {
port := 3306
if config.Port != nil { port = *config.Port }
// ...
}
// zero valueの例
type MySQLConfig struct {
Host string
User string
Password string
Port int
}
func Connect(config MySQLConfig) {
port := 3306
if config.Port != 0 { port = config.Port }
// ...
}
zero valueとnilを分ける必要がない(zero valueが有効な値でない)場合は、素直にzero valueとの比較にするのがよさそうです。
Named return valuesと早期リターンの組み合わせ
Goには例外機構がありません。
失敗する可能性のある関数は(返り値の型, error)
というタプルを返すのが慣習です。
失敗する可能性のある関数を複数呼び出しす場合、Named return valuesで返り値を宣言しておき、失敗した時点でreturn
するパターンをよく見かけます。
func Prepare() (err error) {
err = os.MkdirAll("logs", 0744)
if err != nil { return }
err = os.MkdirAll("working", 0744)
if err != nil { return }
err = os.Remove(".tmp_file")
if err != nil { return }
// ...
}
まとめ
- 言語機能が少なくて、いろいろと困る
- 反面、魔術の余地が少なく、人の書いたコードが読める(良い)