このデザインは僕も好きです.The Zen of Python も言うように何事も “Explicit is better than Implicit” だと感じます.
Go ではほぼ全ての機能が Explicit に表現され,Go の Readablity に繋がっています.
はぁ…Go かわいいよ Go…
しかし,そうは言ってもint->int64などの安全な変換ふくめてひたすら手で型変換しなきゃいけないのはつらいこともあります.
競技プログラミングといった書捨てスクリプトで最初は int でやってたけど,int64 に変換しなきゃ…というときや,
func max(x int64, ys ...int64) int64 っていうテンプレ関数を用意していても,型が int のままでは使えず毎回 int64() で囲ったり int() で戻したりする必要があります.
$ go get -u github.com/haya14busa/go-typeconv/cmd/gotypeconv
使い方
./testdata/tour.input.go
1234567891011121314
// https://tour.golang.org/basics/13
package main
import (
"fmt"
"math"
)
func main() {
var x, y int = 3, 4
var f float64 = math.Sqrt(x*x + y*y)
var z uint = f
fmt.Println(x, y, z)
}
上記のコードは以下で示すように型変換周りでエラーがでます.
123
$ go build testdata/tour.input.go
testdata/tour.input.go:11: cannot use x * x + y * y (type int) as type float64 in argument to math.Sqrt
testdata/tour.input.go:12: cannot use f (type float64) as type uint in assignment
gotypeconv を実行するとこの通り!
123456789101112131415
$ gotypeconv ./testdata/tour.input.go
// https://tour.golang.org/basics/13
package main
import (
"fmt"
"math"
)
func main() {
var x, y int = 3, 4
var f float64 = math.Sqrt(float64(x*x + y*y))
var z uint = uint(f)
fmt.Println(x, y, z)
}
func max(x int64, ys ...int64) int64 といったテンプレートあるけど int では使えない…というときもこの通り.
1234567891011121314151617181920212223
package main
import "fmt"
func main() {
var (
x int = 1
y int64 = 14
z float64 = -1.4
)
var ans int = max(x, x+y, z)
fmt.Println(ans)
}
func max(x int64, ys ...int64) int64 {
for _, y := range ys {
if y > x {
x = y
}
}
return x
}
123456789101112
$ gotypeconv -d testdata/max.input.go
diff testdata/max.input.go gotypeconv/testdata/max.input.go
--- /tmp/gotypeconv308367427 2017-02-06 08:53:30.460299789 +0900
+++ /tmp/gotypeconv267288262 2017-02-06 08:53:30.460299789 +0900
@@ -9,7 +9,7 @@
z float64 = -1.4
)
- var ans int = max(x, x+y, z)
+ var ans int = int(max(int64(x), int64(x)+y, int64(z)))
fmt.Println(ans)
}
シンプルでとにかくプロジェクトで動くものは作れる感じだったので,なんとなくGoよさそうかなぁとは感じつつも,
ちょっと気になっている子(Scala) との大きな違いに戸惑ってたりしてました.
“Functional Programming”? なにそれおいしいの? とでも聞こえてきそうな Go の雰囲気に,
僕は一歩足を引いて評価を保留してました.
ただ,さも vim で書くために生まれてきた言語では…? というほどの gofmt
といった周辺のツールの充実っぷりはこの頃から大好きで,言語というよりは周囲の
エコシステムがよさ…という印象でした.
また,Go と出会ったころと時を同じくして,僕はスプラトゥーンと出会ってました.
お家に帰って Go やってる場合ではなく,一日中イカする毎日.
イカの存在によって Go とはどこかビジネスライクな付き合いにとどまってました.イカはっょぃ.
Go との別れ
それから程なくして,インターンが終了しました.
Go をいちから学ぶところからはじめたにも関わらず,
終了時にある程度のものが出来たことを Go にも感謝しつつ,
一旦 Go との付き合いもお別れになりました.
結局,まぁなんかよさそうな言語だ程度の(ふんわり)印象でここで一旦お別れしたわけですが,
後々,このときのいい環境で Go を書いてレビューしてもらったり,
教えてもらったことはとても役に立ちました.
ありがたい.
Go との再会
2016年の春,忙しかった時期も一段落し,スプラトゥーンとの距離をうまく保てるようになった僕は
ちょっとした小さいツールを Go で書くことにしました.
理由としては,コマンドラインツールとか Go で書くのは便利そうだったことや,
来年以降もGo書くことになりそうかなぁという打算的な思考,
また同じ頃にはじめたアルバイト先のいくつかのサブシステムは Go で書かれていることもあり,
Go 書けるとそのへんも触れておもしろそうかなという気持ちがありました.
数カ月の Go とのお別れ期間でいろいろ忘れていることに戸惑いつつも,書いていくうちに勘も戻ってきました.
ちょっとした自分用ツールをいくつか Go で完成させて,簡単にサーバにもっていって動かすことができてよいなーと感じたり,
Google App Engine で雑に動かしたりして遊んでました.
アルバイト先でもちょっとずつ書いたりするようになって,Go と触れ合う時間は増えました.
Just for fun ではじめましたが,結果として最速 Vim script parser 実装となって,Vimmer にも嬉しい便利なものになったと思います.
Go のパフォーマンスチューニングのやり方も改めて経験できて身についたし,
AST の表現や AST walker の実装をするために go/ast
のコードを読んでインターフェースをパク…参考にしたりとおもしろかった.
Go の標準ライブラリのデザインはとても参考になるし,それを Go 言語でザクザク読めるのは本当にいいなぁと思います.
Go と Vim との共同作業
この頃,Vim はJSONやchannel, job 機能が実装され,外部インターフェースとの通信が容易になり,もうすぐ Vim 8.0 出すよ〜という時期でした.
Go に恋した Vimmer としては,もちろんここで Go 言語を使って Vim との共同作業をさせたいというのは必然です!
(これでは Go と Vim がカップルになってるのではということは気にしない.でも実際2人の相性はとてもよい)
vim-go-client の通信のハンドリングのデザインは Go の net/http のコードを読んで参考にしたりしました.
が,もうちょっといい感じにできそうな気がする…
実際に Go で vim-stacktrace という便利プラグインを作れることを確認できて,go-vimlparser も有効活用できたりなど,
Vim活にも Go が絡むようになってきてますます Go が好きになっていきました.
シンプルという言葉はともすれば薄っぺらくなります.特にVimmerという職業(?)柄,
Vim plugin とかをいくつかみると,”simple” だとか,他にも “easy”, “minimalictic”
と言った単語が並んでたりします.これは自戒を込めてですが,場合によってはちょっと薄っぺらいなぁと感じるようなものもあったりします.
Simplicity is the art of hiding complexity.
Go が simple だとか,less is more だとか,引き算の言語だ…といった表現をされるとき,
僕は全く薄っぺらいだとかは思いません.
これは”Simplicity” というのは実は”Complicated” であり,
“Simplicity” の背後には”complexity”を洗練された,緻密なデザインや設計,実装が隠れているからなんだと思います.
>>> import this
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
僕が最初に学んだ言語は Python で,The Zen of Python の思想はとても好きでした.
Explicit is better than implicit.
Simple is better than complex.
Readability counts.
There should be one– and preferably only one –obvious way to do it.
Go は The Zen of Python に,ともすれば Python 以上にマッチしています.
Go が Pythonic なところも間違いなく僕が Go を好きになった一因です.
なんなら,Go が Python より Pythonic なので,もう Python で書くようなところは,
Go で書きたい.
Go の前に学んだ言語は Scala でした.
Scala が魅せてくれるプログラミング言語の可能性にはスゴイなぁ,おもしろいなぁ,
と思いつつも,Scala が気になる子止まりだったのは,The Zen of Python の思想の真逆を
行くような,ないし真逆のコードが生まれやすい言語だからだと思います.
Scala は今も書いているし,堅牢かつ規模の大きいコードも Scala だと比較的安心して書いていけたり,
僕に “Functional Programming” などを教えてくれた Scala は好きですが,恋に落ちるほどではなかった.
僕を含め,もう少し人類が進化するとまた変わってくる気もする.
Go とのこれから
2017年もGoと仲良くやってきたいです.個人的には特に Vim との連携とか含め.
Go 本体になんかコントリビュッションしたいと思いつつできなかったので,機会があればやっていきたい.
Vet examines Go source code and reports suspicious constructs, such as Printf
calls whose arguments do not align with the format string. Vet uses
heuristics that do not guarantee all reports are genuine problems, but it can
find errors not caught by the compilers.
Go 言語標準でついているlintツールで,コンパイラが検出しないエラーを検出できます.
false positive な結果も無いようにデザインされているのでエラーがリポートされた場合は安心してCIをfailにできます.
go vet に検出されたエラーはほぼ100%直したほうがよいでしょう.
Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes.
Golint differs from govet. Govet is concerned with correctness, whereas golint is concerned with coding style.
Go は標準で go test -coverprofile=coverage.out . などと実行するとテストカバレッジを取得することができます.
ただ実は CI などでカバレッジを取得する際は注意点があり,複数のパッケージをまとめてカバレッジを計測することはできません.
つまり例えば go test -coverprofile=coverage.out ./... とはできません.これは
go test 内部ではパッケージごとにテスト実行用バイナリを作成してそれぞれ実行してるという設計になっていることに起因します.
issue は上がってますが標準では対応するのはなかなか骨が折れそうです.
(ちょっと修正してコントリビューションしてみようかと格闘しましたが構造的に地味に大変そうでした…)
そこで現状で,複数パッケージのテストカバレッジに対応するために色んな所で Makefile やGoのツールを使うといった解決方法が紹介されています.
しかし!紹介されていて,確かにある程度はどれも動くのですが,実は多くのスクリプトはちょっと片手落ちなものになっています.
例えば go test の -coverpkg 引数を使わないと依存先のコードカバレッジが取得できてなかったり,
結果の coverage.out に重複行が生まれるケースがあったりします.