読者です 読者をやめる 読者になる 読者になる

その手の平は尻もつかめるさ

ギジュツ的な事をメーンで書く予定です

YAPC Asia Hachioji 2016 に行ってきた & 喋ってきた

YAPC Asia Hachioji 2016 に行ってきて,ついでに喋ってきました.
諸般の事情で2日目しか行けなかったんですが,濃いトークをたくさん聞けて良かった.

特に,@ さんの M-V-Whatever の話 は非常に参考になって良かった.ああした知識の裏付けがあって,なおかつ体系づけて物事を説明できるというのは最早アートの一種だと思う.僕はそういった知識や能力が欠如していると思っていて,なにかと直感でやりがちなのでものすごく羨ましい.
@ さんの Unlisp の話 もかなり面白かった.言語処理系を作る時のメンタリティとか,実装する上での哲学や程よい妥協感,また実装上の葛藤なんかを生々しく聞けてとても良かった.言語処理系はかねがね書きたいと思っていて,多分それを実行に移す時にこの発表のことを思い出すのだろうな,という予感がある.端的に言ってとにかく格好良い発表だった.
@ さんの開発フローケーススタディもめちゃめちゃに良かった,良かったというか羨ましさを感じた.チームで1つのプロジェクトを遂行するにあたっての最初から最後までを第三者でも分かるように1本のトークになっていて,お話的な面白さを含みつつ,チームビルディングの妙技を感じられて非常に良かった.印象に残っているのは進捗度とか見積もりをしっかり可視化していたということで,ああいった裏付けが取れている状態で「こうしましょう」「ああしましょう」みたいな提案をしつつプロジェクトを進行していけるのは格好いいなあと思った.メンバーの納得感とか,プロジェクトにそぐわなくなってきたら手法を変えるとか,そういう話がされていたのも良かった.
あと,@ さんのCPANの依存モジュールをもう少し正しく検出したいというトークは,perl の静的解析ツールを書いていると出てくるあるあるみたいな感じで「あー,見覚えあるな〜〜」となり,過去の体験が程よくフラッシュバックしてきて面白かった.後で perl の構文は難しいですねえ,という話をできて楽しかったです.


ところで僕のトークは以下の様な感じ.

speakerdeck.com

普段のトークとは打って変わってというか,あまり技術の詳細の話はせずに,普段新しい言語を趣味とかでやるときに自分がやっている事を紹介したというストーリーです.なんか当日のトークでは結構色々言ったのでスライドだけでは伝わらない部分も多いかもしれない.後々動画が公開されるらしいので,なんかわからない所あったら見てみてください.
とにかく言いたかったことの主は「恐怖 = 無知」という部分で,これは僕がプログラミングを始めた時に抱いていた感情そのもので,今でもたまに味わうことがある.これを克服しないことにはどうにもならんのでとにかくやるしかない!!! みたいな話が主題です.
ちなみに,自己紹介の画像は gif アニメだったんだけど,pdf にしたら静止画になってしまった.以下に gif のオリジナルを示します.

f:id:moznion:20160704120517g:plain

おかげさまでこのトーク,ベストスピーカー賞2位を頂きましてとても嬉しかったです.皆様ありがとうございます.


あと LT もしてきたので,その資料も貼ります.

speakerdeck.com

総務省が配ってる,市外局番の doc (word のファイル) を解析して,S 式として扱って tokenize しましょうという内容.結局時間が足りなくてその実装は書けなかったのだけれど,いずれ実装したいと思っている.


あと,久々に会った人と廊下やら懇親会やらで色々喋れて楽しかったです.


YAPC Asia Hachioji,良い意味での手作り感で趣があり,完全に楽しいイベントだった.
主催の @ さん,運営の皆様ありがとうございました.また参加します.

YAPC::Asia Hachioji 2016 の2日目で喋ります

各位お疲れ様です.
さて表題の通り,YAPC::Asia Hachioji 2016 の2日目16時から,
「突如見知らぬ言語に出会ってしまった俺達は」というタイトルで発表します.

どんな話をするかというと,まあプロポーザルに書いてあるとおりなんですが,新しい言語とかを学ぶ為にどうすれば良いのか,そして実際にどうやっているのか,みたいな話をする予定です.プログラミング初学者の人をターゲットとしています.
今回本当に魂を売ってエモい話にしようと頑張っていたんですが,蓋を開けてみればいつもの様に漫談になってしまった……なので漫談に興味のある人もターゲットとなっています.エモ漫談みたいな新境地を開拓していきたい.

皆さん2日目の会場でお会いしましょう.1日目は友人の結婚式で喋るのでいません.

sprint という Java の string formatter を書いた

java

sprint という Java 向けの String formatter を書いた.

Maven Central にも置いてある.

http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22net.moznion%22%20AND%20a%3A%22sprint%22

sprint は Sprint#ff() というメソッドを提供している.使い方としては以下の様な感じ.

final Sprint sprint = new Sprint();
System.out.Println(sprint.ff("Hello: {}!", "John")); // => Hello: John!

処理としては,template を parse して placeholder とそれ以外とを検出して,その検出した placeholder に変数を埋めつつ文字列を生成している.
そしてコードを読むと分かる通り,sprint は実際には StringBuilder の wrapper のようになっていて,適宜 StringBuilder#append() を呼びながら文字列を組み立てている.

工夫などとしては以下の通り.

簡易な template

sprint では簡単な template を利用する.例えば "{}: {}" という感じ.この template 中の {} は placeholder であり,sprint はこの placeholder に対して与えられたパラメータを埋めていく.メソッドの受け取る第一引数はこの template であり,以降の引数は placeholder に埋めるためのパラメータとして扱われる.この時,このパラメータは StringBuilder#append() に渡されるので,結果的に String.valueOf() を適用した結果が文字列中に埋められる.
これは Logback の文字列 template もこんな感じだったと思う.楽で良い.

template の parse 結果を使い回す.

文字列を format する度に template を parse していては効率的ではない.template に対する parse の結果は一意のはずで,記憶しておいて以降の処理で使いまわしたとしても矛盾は起きないはず.なので sprint では template の parse 結果を template 文字列と紐付けてインスタンス内に保持し,同じ template を使った二回目以降の format の際にはその保持しておいた結果を用いる.つまり,sprint では template の parse は一度だけ実行されるということになる.
なお,sprint ではこの parse した結果の構造を走査することで文字列を生成している.

こうした処理の削減によりパフォーマンスが向上している.ベンチマークの結果としては以下の様な感じ(jdk1.8.0_92 で実行.コードはここ).

f:id:moznion:20160605223844p:plain

小さい

コードの規模は小さく,外部に対する依存がないので組み込みやすい.
あと Java 7 でも動くので Android とかでも使えそう.


という感じ.
String formatter はかねがね作りたいと思っていたのでこの土日で作ってみた次第.
どうぞご利用ください.

git-reviewer 書いた

git

code review の reviewer 選出をする時,pull request の内容をざっと眺めてから「この部分だから XX さんかな」とか「あそこのコードは YY さんが詳しいだろう」とか,そういう感じで選ぶことが多くて,つまりは勘と経験で選びがちになってしまう.これについては常々いくばくかの危うさを感じていた.
そもそも,「reviewer として誰が最適か」という知識はプロジェクトに長く関わっている人でなければ知りにくいものであり,いわば属人的な知識のひとつだと思っている.プロジェクトからそういった長老的な人がいなくなってしまったら,最適な code review を実施できなくなってしまう可能性がある.


従って,やはり技術で解決ということになる.
Facebook が作っている mention-bot という GitHubbot として動作するやつがあって,これは pull request が送られてくるとその pull request について blame を実行して code reviewer の候補を出してくれるという気の利いた処理を自動で行ってくれる.

mention-bot は便利で,我々も使っているのだけれど若干の不満もある (おおよそ良いのだけれど……)

  • WIP の pull request だと,作業途中時点での reviewer が選出されてしまう.mention-bot は pull request が送られた時点での reviewer を選出してしまうので,WIP pull request との相性が悪い.
  • GitHub じゃないと動かない

後者は身も蓋もない話だけれど,前者については若干問題があるなーと思っていた.
任意の時点での reviewer 選出をもっと気軽にできれば良いのに,と.


というわけで,git-reviewer というスクリプトを書いた.手元で git のサブコマンドを実行することで reviewer 候補の選出が出来る.

使い方は至って簡単で,

$ git reviewer <into branch> <from branch>

としてやると,その brnach 間の diff について最適と思われる reviewer を選出してくれる (into branch を省略すると,current branch が into branch として扱われる).


仕組みとしては極めてシンプルで,

1. branch 間の diff を取る
2. diff が出た各ファイルについて,削除された行をかき集める (すなわち,diff の先頭に - が付いている行)
3. 削除された行 (つまり変更を入れられた行) のもともとの author を git blame により特定する
4. その author をかき集める

という処理を行い,頻出する author が reviewer 候補として選出される.
もしも diff に削除行が1つもない場合は,変更があったファイルの全行についてその author を集計し,その数が多い人を reviewer 候補として扱うようにしている.


このコマンドを手元で実行することで,手軽に reviewer 候補を知ることが出来て便利になった.めでたしめでたし.
何か「こうした方が良いのではないか」「おかしいのではないか」などがあったら教えて下さい.


[追記]
実行してみればお分かり頂けると思うのだけれど,git-reviewer の出力はとてもシンプルなものになっている.以下のような感じ.

moznion: 123
nozniom: 42
foobar: 2

これらは影響行数の降順として出力されるので,上に表示されればされるほど reviewer 力が高いという事になる.

もしも除外したい committer がいるならば,パイプで grep -v とかで除外すれば良いのかな〜とか思っていたのだけれど,確かに reviewer 側のオプションで食わせられるようにしても良いかもしれない.これが UNIX 哲学だ!! と頭ごなしに殴りつけても良いことはないのです.参考になりました.


以下はコミュニケーションの様子

Docker 使って golang で書いたツールの cross platform build をする

golang docker

まず以下の様なシェルスクリプトを用意する.

#!/bin/bash

# ここで依存しているパッケージを go get する
# 例えば以下の様な感じ
# go get -v gopkg.in/yaml.v2
# go get -v gopkg.in/redis.v3

for GOOS in darwin linux; do
  for GOARCH in 386 amd64; do
    export GOOS
    export GOARCH
    go build -v -o bin/tool-$GOOS-$GOARCH main.go
  done
done

darwinlinux について,それぞれ i386amd64 アーキテクチャ向けのバイナリを作るようなスクリプト.main.go はビルド対象のファイル.成果物はカレントディレクトリの bin 以下に生成されるので,あらかじめ mkdir しておく必要がある (後述).

なお,GOOSGOARCH を export しておかないとうまく動かない.ドキュメントではこれが省略されていて少しハマった.

そんでもってこのスクリプトを呼び出す.以下は Makefile の例.

build:
	docker run --rm -v "$(PWD)":/go/src/github.com/moznion/tool -w /go/src/github.com/moznion/tool golang:1.6 bash build.sh

ここでは docker hub で公開されている golang のイメージを利用している.
https://hub.docker.com/_/golang/

-v オプションを使ってカレントディレクトリに docker image 内の go path が通っている然るべきディレクトリをマウントする.
でもって,-w オプションでマウントしたディレクトリをワーキングディレクトリに設定し,上記のシェルスクリプトを image 内で実行する.

と,ワーキングディレクトリ以下の bin に成果物が吐き出され,ワーキングディレクトリはカレントディレクトリにマウントされているので,つまり成果物はカレントディレクトリ以下の bin に出力されてハッピーという寸法!

なお,--rm オプションを付けておかないと実行する度にゴミ container がどんどん溜まっていくのでつけたほうが良い.

Ref;

実際のコードはここに書いてある感じのやつ


追記

cgo 使わない,なおかつ go 1.5 以上であれば make だけで完結するとのこと!

Docker 使って golang で書いたツールの cross platform build をする - その手の平は尻もつかめるさ

別にdockerもシェルスクリプトもいらなくて makeだけで完結しそうな予感/アンサーソングしといた http://lestrrat.ldblog.jp/archives/48673706.html

2016/05/24 06:27
b.hatena.ne.jp

なお,gox 及び goxc については go 1.4 以下の便利ツールという立ち位置とのこと.

resque_exporter 書いた

prometheus golang resque

最近 Prometheus を使って各種メトリクスを取っていて,ふと resque の queue のステータス,つまり各 queue に 積まれている job の数を集計・可視化したくなったので,そのための exporter を書きました *1

実装としては,resque は <namespace>:queues という SET に全ての queue の名前を持っているので,SMEMBER を使って全 queue 名を取得してきて,
更にその各 queue 名に対応する <namespace>:queue:<queue_name> という LIST が queue そのものを表しているので,その LIST の長さを LLEN により取得することで各 queue の job 数を集計するという仕組みになっています (ここらへんがその実装).

なお,この exporter は resque 互換の job queue であれば問題なく動作するようになっていると思います (実際に我々は resque の java 実装である jesque でこれを使っています).


ところで golang で Prometheus の exporter を書くのは至って簡単で,

  1. prometheus/client_golang の Collector interface を実装する
  2. prometheus.MustRegister(exporter) と言う感じで Collector interface の実装を登録する
  3. http.Handle("/metrics", prometheus.Handler()) という感じで http 経由で export する口を作って serve

という感じでめいめい exporter を提供することが出来ます.シンプル!


なお補足ですが,色々な exporter の実装を見ていると scrapeFailures という counter を用意してあげて,そこに metrics 取得に失敗した回数を突っ込んでやるのがマナーっぽい感じでしたので,当 exporter でもそのようにしております.

*1:今までは fluentd + kibana という構成で似たようなことをやっていた

redis-script-manager 書いた

java redis

p5-Redis-ScriptJava 移植になります.
Redis を使ってて困った時にインターネットを徘徊してると @shogo82148 さんのブログにたどり着く事が多く,日頃お世話になってるわけですが,今回は Redisのトランザクション・スクリプト・ランキングを扱うPerlモジュールを公開しました - Shogo's Blog からの知見です.

上記の記事中には

EVALコマンドのドキュメントによると、 「EVALSHAで実行してみて NOSCRIPT No matching script で失敗したらEVALでやり直す」というのがおすすめらしいです。 EVALコマンドはSHA1ハッシュの登録も行ってくれるので、初回 NOSCRIPT になっても次回からはEVALSHAが成功します。

そんなに複雑なことではないのですが、毎回書くのも大変なのでモジュールとして切り出したのが Redis::Script です。 以下のようにスクリプトオブジェクトを作っておいて、パラメータを渡して実行します。

とあり,まさにこの度そういった utility 的なものを毎度毎度書くのもだるくなったので,ここは一丁 library 化しようということで今回 Java に移植した次第.
Java でやるなら,特定の script について SCRIPT LOAD を一度だけ行なうような singleton class を用意してごにょごにょやるという方法も考えましたが,基本的な実装は p5-Redis-Script を踏襲しています *1

redis-script-manager としては Jedislettuce の2つの Redis Client に対応しています.

ひとまずこれだけ対応しておけば自分で使う分は大丈夫だろう,という判断からこの2つに対応しました.
もしも他の Redis Client のサポートが必要になったら,redis-script-manager-core の抽象クラスである ScriptManager を継承して実装すれば同様の挙動を実現できるので,その時が来たら考えるぞ! という感じです.


ところでこういう細かい library を書いて使っていくのはあまり Java の文化っぽくない感じがあり,その背景には依存する jar が増えれば増える程地獄と化してきて厳しい,などといった状況があることと思います.確かにそうだ.
しかしだからといってコードをいちいちコピペして使い回すとかはあまりしたくないし,やっぱりかゆい所に手が届くパーツがあれば便利やん? みたいな感覚があるので,とりあえず外に出して使ってみるのが良いのではないかと思っているところです.

*1:むしろ ScriptManager ごと singleton にするという戦略はありかもしれない