haya14busa

haya14busa’s memo

2017年の振り返り

この記事は2017年 @haya14busa がやってきた活動,特にVimとかのざっくり振り返り記録です.

過去の振り返り記事

今年はざっくりプログラミング & Vimmer 歴 5年目でシンソツ=ニューシャイベントもあり環境も変わった年でした。

2017年 - Vim 活

VimConf 2017

今年は VimConf 2017 にて一応 キーノート=スピーカー として 60分の発表をしたのは一大イベントでした。 これほど長時間話す経験はまったくなかったので不安でいっぱいでしたが、 時間余りすぎてヤバイとかいうこともなく自分なりには頑張ったかなぁと思います。

正直準備不足は否めなくて特に通訳もあったので申し訳ない…という反省点はありました。 あと発表が自分語りによりすぎた感はあった気はします。 自分しかできない発表をしたいというのとバランスですね。

incsearch.vim is dead. Long live incsearch

incsearch.vim is dead. Long live incsearch で英語で話したのと、 VimConf でも触れましたが、incsearch.vim のインクリメンタルにハイライトする機能を 本体の Vim に入れれたのは本当によかったなぁと思います。 もっと本体のコードにも慣れていきたいですね。

2017年 - Go 活動

Go 本体への初コントリビューションとかを入れれたのはよかった。

入社とか

自分より格段に優秀なひとが周りにゴロゴロいる環境で自分とはなんなんだ…と思うのと同時にありがたい。 もっと頑張らないと。

最初はC++も全くわからないし(というかプログラミングがわかってない)やべぇ…ってなってましたが、 なんとかC++ともちょっとずつ仲良くなってきたのは2017年ではなかなかの収穫だったと思います。

はやく「C++完全に理解したい。」いいたい

スプラトゥーン2

いろいろ考えたり練習したりして、うまくなっていけると楽しいし、みんなで リーグマッチやプラベでわいわいしたりするのはとても楽しかった。 スプラトゥーンはゲーム自体も楽しいですが、スプラトゥーンによって繋がりが増えて みんなでわいわいできるのが本当に神ゲーだなぁと思います。 俺達の信じた任天堂を信じろ。

今年の総評

スプラトゥーンしすぎと、仕事したら割とプログラミング欲も満たされり疲れたりしてるせいで 今年はアウトプット少なかったなぁと反省しています…

来年はもっと趣味グラミングの時間をとりたい。あと英語を引き続き頑張らないと…

以上, @haya14busa の 2017年の振り返り記録でした.

来年もイカよろしく〜

コードのエッジへ移動しろ! Vim-edgemotion 作った

この記事は Vim2 Advent Calendar 2017 14日目の記事です。

vim-edgemotion つくった

vim-edgemotion-demo

VimConf 2017t9md さんの発表 で紹介されていた、 Atom vim-mode-plus の機能の一つ, Edge motion を Vim に移植しました。

https://github.com/haya14busa/vim-edgemotion

Edge Motion とは?

Edge Motion は上下方向へのカーソル移動を拡張するモーションで、”コードブロック”のエッジ(端)へカーソルを移動させることができます。 ブロック内にいればそのブロックの端へ、すでにブロック端にいたりブロック外で実行すると次にぶつかるブロックの端までカーソルを移動します。

VimConf2017 でのデモ(本記事の冒頭のgif) でも直感的・視覚的に移動できてよさそう感は 伝わると思うのですが、個人的に便利だなぁと思うのはキチンとインデントされているコードであれば、 関数やifブロックを効率的に、しかも言語を問わずに移動できるところです。

edgemotion indent demo

GIF では Vim script の if-elseif-else 間の移動や、function-endfunction 間の移動を行っていますが、 CでもGoでもPythonでもHaskell でも、様々な言語でこのインデントベースで次のエッジに飛ぶという カーソル移動は効果を発揮するかと思います。

vim-edgemotionにおけるコードブロックってなに?

edgemotion visualize

GIF にあるように、以下の正規表現でコードのブロックをヴィジュアライズすることができます。

1
2
" Code block regex: [^[:space:]][[:space:]]\ze[^[:space:]]\|[^[:space:]]
" :let @/ = '[^[:space:]][[:space:]]\ze[^[:space:]]\|[^[:space:]]' | set hls

要するに空白文字でないか、または非空白文字に挟まれている空白文字をコードブロックと見なしています。 これは例えば :let @/ = の空白がブロックとみなされないと、カーソルが思っても見ないところで止まったり、 予想以上のところまで移動してしまうことを防ぐためで、Atom vim-mode-plus の仕様にあわせています。

最初はそのヒューリスティックでいいのかな…という気持ちはありましたが、他にいい判定方法も思いつかなかったし仕方ない。 Edge Motion は説明なしに直感的・視覚的に使えると見せかけて仕様を理解しないと 驚いてしまうかもしれないというのがデザインの難しいところですね…

使い方サンプル

お好みのキーにマッピングしてください。僕は <C-j>/<C-k>にマッピングしてみた。

1
2
map <C-j> <Plug>(edgemotion-j)
map <C-k> <Plug>(edgemotion-k)

<Plug>(edgemotion-j) で下方向、<Plug>(edgemotion-k) で上方向にカーソルを移動します。

宣伝

vim-edgemotion は 今年僕も発表した VimConf 2017t9md さんの発表 から インスパイア… というかまるまるパクってVim plugin にポートしてできたプラグインです。

VimConf はエディタの垣根を超えるカンファレンスで奇しくも昨日の Vim2 Advent Calendar 2017 13日目の記事( vim-shiny という plugin を作った) も VimConf2017 で発表された vim-mode-plus にインスパイアされてプラグインを作っています。

宣伝2

また vim-edgemotion はもともと VimConf2017 開催中に30minくらいで作ったのですが、 あとで t9md さんに見てもらうとどうやら実装が違ったらしく、「その仕様はいいかもしれないけど それならedgemotion と名乗るな」とt9mdさんに怒られました(笑)。

結局自分でもオリジナルの仕様が良さそうということで今の仕様にその後変更しました。 この変更はつい[要出典] 先日 土善旅館 というにて開催された進捗合宿で ダメになるソファーでダメになりながら、ネコとペアプロすることで実装されました。

その様子です。

Vim進捗週末旅行@土善旅館についてあわせて読みたい

おわりに

  • Edge Motion は直感的・視覚的にカーソルの上下移動ができてなかなか可能性を感じるので使ってみてね
  • VimConf は来年の VimConf2018 もオススメ
  • 土善旅館 はネコにゃんとペアプロして積んでたタスクを崩せるので便利

Go に暗黙の型変換機能を明示的に導入する

Go に暗黙の型変換はない

Go には Tour of Go でも習うように,暗黙の型変換といったものは存在せず,明示的に型変換をする必要があります.

Unlike in C, in Go assignment between items of different type requires an explicit conversion. – Type conversions https://tour.golang.org/basics/13

このデザインについては FAQ にも書いてあります. FAQ: Why does Go not provide implicit numeric conversions? https://golang.org/doc/faq#conversions

(厳密には interface への変換だけは勝手にやってくれるのでその意味では暗黙の型変換はあるといえる気もします)

このデザインは僕も好きです.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-typeconv を作りました.

go-typeconv 作った

haya14busa/go-typeconv: Bring implicit type conversion into Go in a explicit way

gotypeconv は Go のソースコードを受け取って,型まわりのエラーを見つけ,AST を自動で書き換えて修正します. gofmt と同様に,stdout に修正後のコードをプリントしたり,ファイルを直接書き換えたり,diffを表示することが出来ます.

最終的には明示的な型変換を使ったソースコードになるけど,その変換をある意味暗黙的にやってくれる.というのがコンセプトです.

インストール

1
$ go get -u github.com/haya14busa/go-typeconv/cmd/gotypeconv

使い方

./testdata/tour.input.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 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)
}

上記のコードは以下で示すように型変換周りでエラーがでます.

1
2
3
$ 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 を実行するとこの通り!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ 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 では使えない…というときもこの通り.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
}
1
2
3
4
5
6
7
8
9
10
11
12
$ 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)
 }

vim-gofmt

haya14busa/vim-gofmt: Formats Go source code asynchronously with multiple Go formatters.

Vim で実行したい場合は (Experimentalですが) vim-gofmt というGoの formatter を複数,非同期で実行するプラグインを作成したのでこれを使ってみてください.

設定例:

1
2
3
4
5
let g:gofmt_formatters = [
\   { 'cmd': 'gofmt', 'args': ['-s', '-w'] },
\   { 'cmd': 'goimports', 'args': ['-w'] },
\   { 'cmd': 'gotypeconv', 'args': ['-w'] },
\ ]

実行: (filetype が Go のカレントバッファで) :Fmt

正直ワンバイナリでやってほしいけど,gofmt -sgoimports が同時に実行できないので, いずれ複数フォーマッタ対応のVim プラグイン 欲しいなと思ってたので作りました. どちらも,特に gofmt はかなり高速なので複数実行してもあまり気になりません.

なお非同期にgofmt実行するといっても,バッファが書き換えられていたら上書きしたりするような挙動ではないので安心してください. (不具合あったら直します)

なお

1
:command! GoTypeConv call gofmt#fmt([{ 'cmd': 'gotypeconv', 'args': ['-w'] }])

とか書いておくと:GoTypeConvでgotypeconv だけ実行出来たりしますが,API は変わるかも知れないのでご注意ください.

また以下のようにすると保存時に自動で実行できます.が,APIは(ry

1
2
3
4
augroup vim-gofmt
  autocmd!
  autocmd BufWritePre *.go :Fmt
augroup END

機械で出来ることは機械にやらせたい

途中で IDE ちゃんになっているようですが,Go の場合 IDE ちゃんじゃなくても大丈夫!

The grammar is compact and regular, allowing for easy analysis by automatic tools such as integrated development environments. – The Go Programming Language Specification - The Go Programming Language

Go は spec にまで書かれているように,Goのためのツールを書くのはとても簡単です.

修正系ツールだけでも

  • gofmt -s はフォーマットだけでなくシンプルにかけるところを自動で修正してくれ
  • goimports は自動で import 文を追加したり削除したりしてくれ
  • gotypeconv は型変換エラーを自動で修正してくれます

また proposal: gofmt: Add option to ignore parse error if no bad node in AST · Issue #18939 · golang/go のプロポーザルにあるように,実は末尾コンマがないといったエラーも自動で修正できます. (gofmtに入るかはわからないけど個人的には入って欲しいなー)

なお https://github.com/rhysd/gofmtrlx を入れると末尾カンマは修正してくれますし,実は go-typeconv も勝手に修正してくれます.

gosimple という simple にかけるところを教えてくれる linter もあります. (自動修正機能は今の所ないけどオプションあっても良い気がするなー)

linter がたくさんあることは Go の CI で lint と カバレッジ回して非人間的なレビューは自動化しよう in 2016年 - haya14busa で紹介しました.

go/* を使えばパッケージをソースコードをパースして AST を取得したり, それを Format された形式で print したり,型情報を取得したり… などと言ったことが簡単にできます.

golang.org/x/tools/go/loader を使えば,簡単に型情報を使える形でプログラムをロードできますし, golang.org/x/tools/go/ssa なんてものもあります.

go/* パッケージ だけでなく,golang.org/x/tools/go 下のパッケージをみても面白いです.

AST ベースのツールは他言語でもたくさんあると思うので比較的わかりやすいですが, 型情報まで使えるパッケージを提供している言語はそんなになくて難しいですが,godoc の他にも公式チュートリアルが参考になります.https://golang.org/s/types-tutorial

日本語情報でも motemen さんの GoのためのGotenntenn さんの各種記事 goパッケージで簡単に静的解析して世界を広げよう #golang - Qiita が詳しいです.

ぜひ,みなさんもGoのためのGo ツール作ってみると面白いかと思います. いろんなことができるので,なかなか楽しいです.

はぁ…Go かわいいよ Go…(ため息)

Go に恋した Vimmer の2016年の振り返り

この記事では2016年 @haya14busa がやってきた活動,特にVimとかGo 活動をざっくり振り返ります. 個人的な備忘録です.あとポエム.

過去の振り返り記事

今年はざっくりプログラミング & Vimmer 歴 4年目でした. そろそろプログラミングの基礎的なところわかってなくても,まだはじめてそんな経ってなくてまだ勉強できてないんです〜,また今度やっておきますっ! みたいな言い訳が完全に通用しない年になってきた気がします (そもそも実際に言ったことは無い). まだまだ学んでおきたい分野はたくさんあって,特にもうちょっとレイヤーが下の要素もやっていって理解しておきたさがありますね…

“Go” に恋した 2016年

AlphaGo, Pokemon GO, Amazon Go… “Go” は2016年の1つのキーワードだったように思いますが,個人的には Go 言語の “Go” に恋した1年でした.

credit: Gopher by tenntenn CC BY 3.0

Go との出会い

Go 言語との出会いは去年インターンでGoをはじめて使ったときでした.

その時一目惚れ… をしたわけではありません.

シンプルでとにかくプロジェクトで動くものは作れる感じだったので,なんとなく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 と触れ合う時間は増えました.

2016年秋,気づけば Go に恋に落ちてた

それからも,定期的に Go で何か作ったりしてました.例えば,

この頃には Go がかなり手に馴染むようになってきました.上記に上げたツールも Go の開発を助けるツールだということからも,Go をより書くようになってきてることがうかがえます.

特にきっかけはありませんでした.でもこの頃にはもう Go に恋してたんだと思います. その結果,特に意味もなく Vim script のパーサを Go 言語で動かしたいなぁ… 既存の Python 実装とかあるけど,Go でいじりたいなぁ… と思って Go 実装を作ったりしてました.

go-vimlparser - Vim Script Parser written in 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 Advent Calendar で書きました.

vim-go-client の通信のハンドリングのデザインは Go の net/http のコードを読んで参考にしたりしました. が,もうちょっといい感じにできそうな気がする… 実際に Go で vim-stacktrace という便利プラグインを作れることを確認できて,go-vimlparser も有効活用できたりなど, Vim活にも Go が絡むようになってきてますます Go が好きになっていきました.

Go と Vim との間に生まれた子供 - reviewdog

(Go と Vim が結婚して子供を産みました.僕のGoへの恋は片想い)

reviewdog logo

reviewdog は僕の 2016年に作ったプロダクトの一番のヒット作と言えます.reviewdog の GitHub のスターは執筆時現在 268 です. reviewdog が好きなのは,実際に超便利プロダクトだということはもちろん,Vim の ‘errorformat’ という便利な機能を Go言語で port して Vim の外でも使えるようにしたという,Vim と Go の”よさ” が存分に発揮されたプロダクトだというところがとても気に入っています.

reviewdog のようなものを作るには,いろんな linter などのコマンドの結果を扱わなくてはいけません. 他のツールでは, gometalinter のように linter ごとに正規表現をアドホックに作ったり, それぞれのコマンドの runner を作ったり, checkstyle xml やJSONのある機械的な形式を吐くlinterにのみ対応するといった割り切りをしています.

Vim はそういういろんなコマンドやいろんなアウトプット形式に対応するというのは得意で, ‘errorformat’ という scanf-like な機能がこれにあたり,この形式を使うことに決めました.

ただしこの’errorformat’は Vim でしか使えません.そこを同じくマルチプラットフォームで簡単に動く Go 言語で同等の機能をポート(haya14busa/errorformat)し, Vim のよさを Go がサポートしてより広く使ってもらえるようにできました.

reviewdog は現在もちょくちょく改善していて,特にローカルでもより動かしやすいようにしようと思ってます. 年内にある程度開発してバージョン1.0にしようかと思っていましたが,もう少しかかりそうです.

reviewdog の状況

OSS で導入するのは CI サービスがうまく Secure Token を扱ってくれないせいで, 若干最初の導入が手間なのが使ってもらうには少し壁になってるのが悲しい.Travis〜CircleCI〜頼む〜改善してくれ〜. 一度導入してしまえばあとは楽だし,もしくはローカルでだけ使っても便利なのでもうちょっと使用事案増えて欲しい.

Go 活動その他

Go が好き

書いてるうちに手に馴染んできて,いつの間にか好きになってましたが,結局僕はなぜ Go に恋に落ちたのでしょうか. 1つは間違いなく Go の Simplicity に惹かれたんだと思います.

Simplicity is Complicated

Rob Pike の “Simplicity is Complicated” という発表がオススメです. 僕はこの発表をみて,胸がきゅーーんっとなりました.Go かわいいよ Go.

Go はなぜ成功したんでしょうか? コンパイル速度,実行速度,デプロイの容易さ,充実したツール,質の高いライブラリ,interfaceやconcurrencyをサポートする言語機能… そのどれもが大事で,僕も実際どれも好きですが,Rob Pike は “Simplicity” が答えだといいます. そしてこの “Simplicity” は “Complicated” だとも.

シンプルという言葉はともすれば薄っぺらくなります.特にVimmerという職業(?)柄, Vim plugin とかをいくつかみると,”simple” だとか,他にも “easy”, “minimalictic” と言った単語が並んでたりします.これは自戒を込めてですが,場合によってはちょっと薄っぺらいなぁと感じるようなものもあったりします.

Simplicity is the art of hiding complexity.

Go が simple だとか,less is more だとか,引き算の言語だ…といった表現をされるとき, 僕は全く薄っぺらいだとかは思いません. これは”Simplicity” というのは実は”Complicated” であり, “Simplicity” の背後には”complexity”を洗練された,緻密なデザインや設計,実装が隠れているからなんだと思います.

GCやgoroutine,interfaceといった実際の例がスライドで説明されているので是非見てみてください.

僕らが享受しているGo の “Simplicity” は簡単に実現されたものではありません. 僕らが Go に対して,「あぁ,こういうのでいいんだよ.こういうので.」と感じるとき, その背後には Go がたくさんの機能を削ぎ落として,直交する必要十分な機能のデザインと 複雑な実装が存在しています.

Simplicity is hard—to design.
Simplicity is complicated—to build.
But if you get it right…
Simplicity is easy—to use.
The success of Go proves it.
https://talks.golang.org/2015/simplicity-is-complicated.slide#30

“Simplicity” をデザインするのは難しく,”Simplicity”の実現はとても複雑です. でもこれらをうまくやると,”Simplicity” は使うのが簡単になります.

これが Go は, “simple” であり,”less is more” であり,”引き算の言語” であり, 「あぁ,こういうのでいいんだよ.こういうので.」ということなのでしょう.

Go is more Pythonic than Python

Go and the Zen of Python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
>>> 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 本体になんかコントリビュッションしたいと思いつつできなかったので,機会があればやっていきたい.

あとは,Go のよさを再確認するため(?)に別のパラダイムの言語(rust とか)にちょい浮気しつつやっていきたいですね.

Vim との 2016年 - 本妻は Vim

2016年のVim活もザックリ振り返ります.

EasyMotion 3.0

Vimのカーソル移動はもっともっと爆速になる! Vim-EasyMotion v3.0 をリリースしました - haya14busa

結構前にやった気もしますが,今年でした. バージョン3.0の機能追加でウィンドウをまたいだEasyMotionによるカーソル移動が実現しました. 完全にライフチェンジング機能だし,待望の機能の実現で自分の Vim script 力の高まりも確認できてよかった.

vital のロード高速化

まず,revital.vim という別プラグインで vital.vim のロードを高速化できることを示した後,結局 vital.vim 本体に入れてもらいました. ついでにリファクタリングしたりインターフェースの改善も行いました(後方互換性は維持している). 特定の環境でvital.vimが遅いという問題を完全に解決し,現在では autoload 関数を呼ぶのと変わらなくなってます.

はじめてのパッチ - Vim の Contributer に!

vital のロード高速化の際に Vim のバグを踏んで,小さいですがはじめてパッチを書いて取り込まれました! このバグは本体のバグだったということもあって異常に原因究明が難しく,原因を見つけたのは lambdalisue さんでした.

今年は他にも EasyMotion で踏んだバグの修正パッチや,Vim8.0 リリース前の channelやjob 機能を叩いたパッチを書いたり, Vim 8.0 に lambda いれようぜ! ともう一回 vim-jp でちょっと騒いで,あとは vim-jp の mattn さんや k-takata さんに丸投げするなどをしてました.

そのあたりの話で1つ記事を書いてます -> Vim 8.0 released and Now, I’m a contributor of Vim !!! – Medium

今年になって小さいとは言え Vimのパッチ書いたりできたのはとても嬉しかったです. Vim のソースコードも読むことは出来るし,デバッグの仕方もわかってきたし, 場合によってはちょっと修正するくらいのことも出来るということがわかったのは収穫でした. ただ,もっと C 力は高めたほうがよさそう.

Vim 8.0 & Go関連

  • go-vimlparser
  • vim-go-client
  • vim-stacktrace
  • パッチいくつか.

詳細は Go のところで紹介したので省略.

VimConf

発表は go-vimlparser についての話ですでに書きましたが, t9md さんの t9md/atom-vim-mode-plus の発表が面白くて, Atom の vim-mode-plus のアイデアを Vim にバックポートする業などをしていました.

haya14busa/vim-metarepeat は vim-mode-plus の occurence で出来るようになる便利な機能を別の角度から実装したもので, ドットリピートをオペレータとして,テキストオブジェクト内の対象に対して一括でドットリピートを実行するものです. 記事には書いてないけどissueにちょっと考えとかを書いています -> https://github.com/vim-jp/issues/issues/977#issuecomment-259703728 vim-metarepeat はかなり便利で常用していて,記事書くかぁと思いながら今年が終了しました.

haya14busa/vim-textobj-function-syntax は関数text-objectをVimのsyntaxを使って言語ごとに用意せずに使えるようにしたものです. syntax によって使える/使えない言語がありますが,ちょっと便利. これも記事にはしてないけどissueにちょっとコメントしてます.-> https://github.com/vim-jp/issues/issues/987#issuecomment-262870187

Google Translate の衝撃と Vim

Google Japan Blog: Google 翻訳が進化しました。

今年はGoogle Translate が日英間の翻訳に Neural Machine Translation を導入して, 機械翻訳がとても流暢になりました.Google Translate さんすごい. この改善をみて,Vimから使いやすくするためにいくつかプラグインを作りました.

英語でブログ記事を書く

Google Translate の流れでいうと今年後半からは実験的にブログ記事も英語で書くようにしました. Google Translate 改善前からやっていて,改善後も勿論(?)Google Translateはあくまで補助としての使用ですが.

書いた英語 Posts

英語で書く記事かどうか決めるというよりは,基本的に英語で書いて,どうしても日本語で書いたほうがよさそうだったら日本語で書くことにしています. 直近の2つのアドベントカレンダーは普通に日本語で書いてしまって守れてないけど…正直時間が取れなかったので日本語に逃げました… まぁ日本語で発信するのもそれはそれで大事だと思うのでいいかなぁーとは思ってます.

英語で書く理由としては,今後使うし勉強のためやらなきゃ…という理由と, 発信を日本に閉じる必要性はないかなーと最近は考えてるからでした. 僕はVimとかGoについて書くことが最近は多いですが,それらの記事のターゲットは大抵 Vimmer か Gopher で, 別に想定読者を日本人に絞る必要性はないんですよね.

日本語で書いた後,英訳しようとはなかなか行動できないので,英語・日本語両方ポスト作ると言うよりは, 実質デファクトになってる英語で書けば英語読める日本人は読めるので,基本的には 英語で1つ記事を書くということにしました.

…とはいえ時間やクオリティーの兼ね合い,日本のコミュニティーの活性化, フィードバックのもらいやすさ,そして何より現状の英語力が足りてない問題など, 色々と問題はありますが.そもそもたくさんの人に読んでもらうというのが目的ではないという観点もありそうだけど.

英語で書くと特に日本では伸びづらいですが, 日本で伸びなくても今までリーチしなかった層にリーチするし, 今年書いた記事はだいたいある程度は日本人もそれ以外も読んでもらえたっぽいので, 今の所やってみてよかったです. reddit とかにあげるとそこそこ読みにきてくれて便利.(redditのCEOがコメント編集したという事件があって,若干使いたさが減りましたが…). 来年も続けていこうかなぁと思います.

英語に関してはWritingもそうだけど,どちらかといえば勉強する必要があったのはSpeaking/Listeningだった気が… という説もあり,今後がとても心配すぎる…

2016年活動情報

GitHub Contributions

GitHub Contributions (including private contributions)

GitHub Contributions (including private contributions)

GitHub Contributions (public)

GitHub Contributions (public)

Posts

Posts (English)

歩くのが下手って気付いた2016年

今年は1つ大きく躓いた出来事がありました.躓いただけなら立て直せばよかったはずだったけど, そのまま歩き続ける意味を見失い,歩みを進めようとする心はもう折れていて,しばらくその場でうずくまってしまいました. 躓いたことにも,歩き直せなかったことにも,道を変えて歩くことさえもできずに,ただただうずくまってました. 今まで通りとはいかないにしても,もうちょっとうまく歩けたはずでした. あぁ,僕は歩くのがなんて下手なんだ…

今の所,詳細はオープンインターネットには書かなくてもいいかな…と思ってます. 書く気がないのにここでふんわり書いたのは完全に自己満足です.忘年です.忘れないけど.

あとは完全にここでやるべきことではないけど,一種の懺悔みたいなものです. 僕が単に1人でうずくまったまま世界から消えても,別に世界はどうってことないけど, 無駄に不要なところにまで迷惑をおかけしてしまったり,いくつか見えた差し伸べられた手も 完全にシャットアウトしてしまったことは,単純に反省しています.ごめんなさい.

最近は人間的な生活をおくるリハビリをしていて,心に平穏は戻りつつある気はします.

最後に

2016年の振り返り記事だって言ってるのに無駄なポエムがところどころにあるせいで無駄に長くなった気がします. あとオープンにやってきたこと何でもかんでも放り込み過ぎて長くなったので,もうちょっとハイライトだけ振り返ればよかった気もする. まぁ,まだまだとはいえ,2016年結構いろいろやったのでは? と振り返って思いました. 来年の目標,なにか定量的なものを設定して振り返るのが本当は良い気がしますが, 来年はとりあえず強く生きることを目標にします.

来年がとても素晴らしい1年でありますように.

Go の CI で Lint と カバレッジ回して非人間的なレビューは自動化しよう in 2016年

この記事は Go (その3) Advent Calendar 2016 の24日目の記事です(代打). メリークリスマス!

本記事では Go 言語プロジェクトの CI で回すと便利な各種lintの紹介やカバレッジ計測の方法などなどについて紹介します.

Go 言語おすすめ linter

Go の lint 一覧といえば gometalinter じゃん? みたいな話もあると思うのですが,CIで回すには個別に linter を明示的に回すほうが良いかと思います. ということで 僕が普段使ってるオススメ linter の紹介です.

go vet

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

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.

gofmtがコードのリフォーマットを,govetがコードの正確性をチェックするのに対して,golint は Go のソースコードの “コーディングスタイル” の問題を報告します.これはエラーというよりも “suggestion” に近く, 基本的に従っていたほうがGoの慣習に沿った確実によいコードになるのですが, まれに,いやここの関数はコメント書かなくても絶対いいよね…とかいうケースもあり 若干消耗することもあるので星4です.

なお,結局はコーディングスタイルの問題をリポートするという思想からデフォルトでは問題があっても exit code は 0 になるので,落としたい場合は -set_exit_status flag を付けましょう.

errcheck

errcheck は関数のエラーの返り値をハンドリングチェックしているかどうかを静的に解析してくれるツールです. エラーを捨てた状態の場合思わぬ動作(nilになってたりだとか)するので,これをチェックしてくれるのは大変嬉しいです.

ただし,少し実際直さなくてもいいという意味でのfalse positive が多くCIで動かすには工夫が必要で星3です.エラーをチェックしてくれるという便利度は満点. なお,最近は標準ライブラリで必ずエラーがnilとして返ってくるような関数についてはリポートしないという false positive を減らす工夫もされているようです.

staticcheck

staticcheck は一言で言うとサードパーティーの go vetです.go vet でチェックされていないようなことを静的に解析してくれます. (例えば正規表現がvalidかチェックしてくれるかなどなど…たくさん項目があるのでREADMEを見てみてください.)

作者の dominikh さん は Go 言語の開発にも関わっていて,dominikh さん製Go lint ツールは個人的には 信頼できる印象です.github/go の issue でも go vet のissueなどに, staticcheck は実際こういうのチェックしてるけどと言った感じで参照されてたりするのを見かけます.

そして次の2つのツールも dominikh さん製です.

gosimple

gosimple は一言で言うとdominikh さん製のサードパーティー gofmt -s です.もっとコードをシンプルに出来るところを報告してくれます. (ただし執筆時現在,自動で修正してくれるオプションとかはない)

例えば if err != nil { return err }; return nil といった構造のコードがあれば return err で十分だよ? と報告してくれます.

報告に false positive もない印象で,あーそんなメソッドあったのか〜ということに気付いたりできてオススメです.

go-unused

使ってない identifier をチェックしてくれます. Go のコンパイラ自体が使ってない変数があるとコンパイル通らないという話もありますが, unused は グローバル変数の var や const, struct の field,export されていない関数などで 使われていないものを報告してくれます.

報告してくれないのは使われてない関数の引数くらいでしょうか? これはインターフェースを満たすための関数など, 引数もらうけど使わないんやというケースなどを考えて報告されていないのではないかという気がします.[要出典]

また個人的には使えてないので強くオススメできませんが,パッケージのリストを渡して exported なもので,渡したパッケージ内で使われていないものをチェック出来る機能もあります. https://github.com/dominikh/go-unused#whole-program-analysis “internal” packages などを使っていたりする場合は便利かもしれないですね.

unused は個人的には大変便利に使っていて,ごくまれにデバック用のexportしてない関数を報告されて, あー…ってなる以外に false positive な結果もなく便利に使っています.

この前 interface を満たすためのダミーの関数をいろんな struct に定義してたんですが, その際,追加すべきでない struct にも追加してしまい,それを unused が報告 してくれたことがありました.インターフェースを満たすかどうかといった観点での”used”もちゃんと見てくれていて大変良い子だな〜と思い感動しました. オススメです.

gofmt -s

linter ではない若干番外編その1.

gofmt -s の結果 diff があるかどうか,つまりもっとシンプルにかけた部分がないかということをチェックできます.

コマンド: (! gofmt -s -d . | grep '^')

括弧は travis などのyamlでvalidなものにするために使っています. gofmt も exit code が 1 になったりすることはないので個人的には grep '^' の結果を否定することによって,diffを表示しつつ,diffがあればfailにするという手法を使っています. (xargs -r とかはMacでは-rがないとかがある…)

基本的にGo言語開発時はみんな gofmt かけていると思いますが,-s は付けていなかったり, goimportgofmt を代用していた場合に -s オプションがなくてかけてなかったりするので CI で実行するとちょっと便利です.

ちょっと便利なんですが,-s つけるとめっちゃシンプルになって絶対いいよね…! というよりは, あー手元で実行してなくて,CIサーバでdiffでちゃったよ…直すか…とちょ〜っとだけ消耗することが あるという意味で個人的にはオススメ度星4です.(goimport -sフラグ足してくれ〜)

go test -race

  • おすすめ度: ★★★★★

linter ではない若干番外編その2. go test-race を付けるとrace condition があるかチェックしてくれます. 特に goroutine とか使ってるコードであれば,CIでのテスト実行時にはぜひ付けておくべきでしょう. 僕も何度も-raceに怒られてコード直したり,ああここlockいるなと気付かされております.

その他

あんまり僕がまだ使えてなくて,ちゃんとオススメできないけど便利な静的チェックツールはいくつかあります.

interfacer

  • https://github.com/mvdan/interfacer
  • 引数の型とかインターフェースでいいじゃん?というところを指摘してくれる. e.g. *os.File -> io.Reader
  • 個人的にはインターフェースにするといいところは最初からインターフェース使ってたりするしそこまでの恩恵は感じない

gosum

  • https://github.com/haya14busa/gosum
  • 急に拙作ツールの宣伝(?)
  • Go のインターフェースで直和型っぽいことを表現したときの,type switch に抜け漏れがないかを静的にチェックするツールです.
  • Scala でいう sealed trait のパターンマッチでコンパイラが抜け漏れがあると warning だしてくれるというやつのGoバージョン
  • 詳しくはこの記事に書きました -> Sum/Union/Variant Type in Go and Static Check Tool of switch-case handling – Medium
  • 書いてみて,使ってみて実際便利だと思ってるんですが,まだちゃんとCIで回したりはしてないので,その他枠で雑に紹介です.

ところで linter 書くときって,AST ベースで解析するツールが僕の観測範囲では多いと思うですが, Goは必要であれば go/types パッケージを使って型情報まで使って 解析できるので大変便利ですね…! 標準ライブラリでカバーされてるところも +1

“go/ast ではしゃいでるのはもう古い! 時代は go/types !” みたいな煽りタイトルの解説記事を最近は待ち望んでます. go/types 関連は標準ライブラリの中でもかなり大きいものなのでなかなか僕もまだ全貌を理解できてないです.

その他のその他

gometalinter とか Go Report Card で紹介されているツール.

https://github.com/fzipp/gocyclo とかイマイチ恩恵を受けたことがないんですが,gometalinter とか Go Report Card で使われているツールは参考になるかもしれません.

reviewdog: linter の false positive と闘う

特に golint や errcheck など,上記でオススメしたlinterの中には,false positive なリポートが結構あって,めっちゃ便利でチェックしたいんだけど CI で fail にしづらい… というものがいくつかあります.

そこで拙作ツール reviewdog の紹介です.(宣伝)

もちろん Go 製です!

(画像は実際のPull Requestのコメントへのリンクになってます)

reviewdog は Go 言語の linter に限らず,任意のコマンドの結果を’errorformat’ という形式を使うことでパースして,diff で新たに追加された部分にたいする問題だけを 表示したり,GitHub にコメントすることが出来るツールです. 詳しくは -> reviewdog を飼ってコードレビューや開発を改善しませんか - haya14busa

結果をdiffでフィルターすれば,それらの問題についてのみPull Requestのレビュー時やコミット時にチェックすることが出来るので, もし false positive な結果な場合は単に無視すれば次回以降に同じ問題は報告されません.

もちろん,例えば先に紹介した unused などは全然diffと関係ない部分で新たに問題がおきたりすることもあり, 結果を diff でフィルターするというのはfalse positiveと闘う銀の弾丸ではないのですが, 多くの場合これで十分機能を果たすでしょう. (一応 diff 外の問題もうまくまとめて報告する仕組みも足したいな〜という気持ちはあります)

Go 製ツールである reviewdog は自分自身のコードでドックフーディングしていて,この記事で紹介した いろんなGo 用 linter を実行していて,上記の画像のように実際に便利に使っています. 参考: reviewdog/reviewdog.yml (最近はyamlでも管理できるようにしていて,より簡単にローカルで実行したり,git hook で実行しやすくしたりしたいなどと改善しています)

Goのテストカバレッジをレビューでも活用する

Go は標準で go test -coverprofile=coverage.out . などと実行するとテストカバレッジを取得することができます. ただ実は CI などでカバレッジを取得する際は注意点があり,複数のパッケージをまとめてカバレッジを計測することはできません. つまり例えば go test -coverprofile=coverage.out ./... とはできません.これは go test 内部ではパッケージごとにテスト実行用バイナリを作成してそれぞれ実行してるという設計になっていることに起因します. issue は上がってますが標準では対応するのはなかなか骨が折れそうです. (ちょっと修正してコントリビューションしてみようかと格闘しましたが構造的に地味に大変そうでした…)

そこで現状で,複数パッケージのテストカバレッジに対応するために色んな所で Makefile やGoのツールを使うといった解決方法が紹介されています. しかし!紹介されていて,確かにある程度はどれも動くのですが,実は多くのスクリプトはちょっと片手落ちなものになっています. 例えば go test の -coverpkg 引数を使わないと依存先のコードカバレッジが取得できてなかったり, 結果の coverage.out に重複行が生まれるケースがあったりします.

解決策

mattn さんの mattn/goveralls では上記の問題に対応したマルチパッケージ対応テストカバレッジ機能が備わっています.というか僕がPull Request しました. Coveralls に投稿する場合は goveralls を使うと良いと思います. (goveralls -service=travis-ci でよしなにやってくれる)

ローカルでは?

goveralls にコントリビューションしたあと,あれ…これローカルでもやりたいじゃん…ということに気付き, 既存のMakefileソリューションや似たツールはいくつかあるにはあったんですが,上記の問題などの対応とか いろいろ面倒だったのでマルチパッケージカバレッジ作成用go test のラッパーツールを作りました.

haya14busa/goverage: go test -coverprofile for multiple packages

1
$ goverage -coverprofile=coverage.out ./...

とかするといい感じに coverage.out が生成されます.便利.

codecov に投稿してレビューでも活用する - “おい、coverall もいいけど codecov 使えよ”

サブタイは b4b4r07 さんリスペクトです. ref: おい、peco もいいけど fzf 使えよ - Qiita

Codecov という Coveralls と似たサービスがあるのはご存知でしょうか? 僕は以前からたまに見かけたことはあったのですが,最近はじめて使ってみて,断然 coverall よりいいじゃん…!!! と感じました.

全体的に洗練されてる…というよさもあるんですが,一番いいところは Pull Request の diff に対するカバレッジを表示できて, ブラウザの拡張をインストールすれば GitHub の Pull Request 画面上でカバーされた行をオーバーレイで確認できるところが大変気に入りました.

もちろん Codecov 上のページでも見れます: Compare ⋅ haya14busa/reviewdog

Go 言語リポジトリのカバレッジを travis で計測して codecov へ投稿する例:

1
2
3
4
5
6
7
8
9
10
# .travis.yml
install:
  - go get github.com/haya14busa/goverage

script:
  - goverage -coverprofile=coverage.txt ./...

after_success:
  - goveralls -service=travis-ci -coverprofile=coverage.txt
  - bash <(curl -s https://codecov.io/bash)

codecov 公式のGo言語用リポジトリの例 では Makefile を使ってますが, 完全に上記のマルチパッケージサポートの問題を踏んでるので goverage を使うと良いと思います.

テストカバレッジは別に100%を目指さなくてもよいと思っていて,全体のカバレッジが何%以下とか何%下がったらステータスをfail にするといった機能がcoverallやcodecov にはありますが,この辺を有効にすると結構消耗するかと思います.

ただ,カバレッジを参考にすることは有用だし,ユニットテストは基本的に書くべきです. Pull Request などではカバーすべきところをしっかりカバーしたテストを期待したいし,, レビュー時にもそのあたりが可視化されると大変便利です.

実際僕は Go のレビューするときに手元でテストまわしてcoverageみて,ここテストそもそも無いから足して欲しいだとか, この行はカバーされてないけど,カバーすべき部分なのでテストケース足して欲しいとか言ったりするんですが, codecov を使うとこのフローがやりやすいし,レビュイーもPull Request を出した時点で自分で気付いて テスト足したりできると思います.

実は codecov 使い始めたのは最近で,僕自身がチーム開発として使った経験はまだないのですが, coverall よりも codecov 使うとこの辺いい感じに可視化されて人間が指摘しなくてもよくなったり, レビュワーとして指摘しやすくなったりすると思います.

せっかく Go という言語は標準でテストが書きやすく,カバレッジ計測もしやすいので,ぜひ皆さんもカバレッジを計測して,codecov 使ってみてはいかがでしょうか?

まとめ

Go のCIでまわすと便利なlintツールを紹介したり,テストカバレッジの取得方や codecov のオススメなどをしました. Go は go/ast, go/types など go/ 下の標準パッケージを使ってコードをパースしてASTを取得したり,型情報を取得したりなど するライブラリが用意されているので,必要に応じて自前でチェックツールを作ったりもしやすく面白いです.

この記事で紹介したものは,一般的に多く使われているみなさんが知っているようなものから,あまり知られてないものまであるかと思いますが, すべて少なくとも僕が使っていて便利だなぁ〜,と思ったものを紹介してみました. 他に便利なオススメツールなどがあれば教えてください!

個人OSSプロジェクトではレビュワー最初はいない問題などがあり,最近はPull Requestを開いてlint チェックさせたり,カバレッジみたり, 改めてブラウザ画面上でセルフレビューするなどしていて,まぁちょっと面倒もあるけど良い感じです. あとは設計レビューとかもしてくれるGoのツールがあればカンペキですね〜〜〜!!!1

もちろんチーム開発でのレビューでも,消耗しがちな非人間的な指摘は機械にやってもらって, もっと大事な観点をレビューするために,この記事で各種紹介した手法は役に立つかなと思います.

来年も Go 書いていくぞ!