一時的にブランチを切り替えてコマンドを実行するgit-switchっての作った話
gitで、一瞬だけ他のブランチに切り替えてコマンド実行したいときに毎回checkoutとかすんのタルいし、workspaceに変更があったらgit-nowしたりstashしたりしないとブランチ切り替えられなくてタルいので、一時的に他のブランチに切り替えて指定したコマンドを実行するラッパーを書いた。書いた。
dotfiles/bin/git-switch at master · yuroyoro/dotfiles · GitHub
git switch [--repository=<path>] [--new-workdir=<path>] <branch> <command...>
`git switch foo_master git show`ってやると、fooブランチに切り替えて`git show`を実行する。コマンドは別になんでもいいので、`git switch hogehoge rspec spec/hoge_spec.rb`のように、他のブランチでrspec流したり可能です。
以下は、一時的にcomposable_procに移動してgit show -sを実行した様子。
ozaki@mbp-4 ( ꒪⌓꒪) $ git br composable_proc * trunk ozaki@mbp-4 ( ꒪⌓꒪) $ git switch composable_proc git show -s Already on 'composable_proc' commit 20c1b806490233db0467ac4332ba073edca7889f Author: Tomohito Ozaki <ozaki@yuroyoro.com> Date: Sun Mar 10 02:34:12 2013 +0900 proc.c: Add Proc#flip
内部的に、git-new-workdirを使っているので、以下を参考にgit-new-workdirが使えるようにしておく必要があるです。
git-new-workdir が便利 - #生存戦略 、それは - subtech
デフォルトの動作では、/tmp以下に現在のリポジトリと同じディレクトリ名でgit-new-workdirして、そこでコマンドを実行するようになっている。すでにnew-workdirする先が存在する場合は再利用する。
workdirはオプション'--new-workdir=
注意点として、gitのコマンド実行とシグナルハンドラの関係上、シグナルをもらうプロセスがforkされるようなコマンドの実行には注意する必要がある。例えば、pryはCTRL-CによるSIGINTをtrapするが、fork元のgitプロセスはSIGINTでプロセスを終了する。そのため、git-switchではSIGINTをトラップして、forkしたコマンドの子プロセスを頑張ってぬっころすように実装している。このへんうまいことやる方法知りたい。
ワークライフバランス()向上のために定時すぎたらダイアログが表示されるようにした
仕事してると、ついつい「このバグ直した帰ろう」とか「このテストが通ったら帰ろう」とかやってるうちにいつの間にか終電近くまで作業していて( ꒪⌓꒪)ってなることが多いので、定時すぎたら一時間毎にムカつく心温まるメッセージを表示するようにAppleScript書いた。
20時から24時までの一時間毎に、こんな感じで唐突にメッセージが表示されてフォーカスを奪っていき、ワークライフバランス()の改善を促し精神に負荷をかける。
set hour to hours of (current date) try tell application (path to frontmost application as text) if hour = 20 then set msg to "( ꒪⌓꒪) もういい時間だしさっさと帰れよカスwww" display dialog msg else if hour = 21 then set msg to "( ꒪⌓꒪)おいダラダラ作業してんじゃねーよwwwクズがwww" display dialog msg else if hour = 22 then set msg to "こんな時間まで作業してるとかwwww生産性m9( ꒪⌓꒪)" display dialog msg else if hour = 23 then set msg to "( ꒪⌓꒪)そろそろ帰宅難民なんじゃね?社蓄乙www" display dialog msg else if hour = 24 then set msg to "( ꒪⌓꒪)なにムキになって仕事してんの?社蓄www" display dialog msg end if end tell end try
Script Editor.appでこんなの書いて、~/bin/fxxk_it_alert.scpt ってファイルで保存。次に~/Library/LaunchAgents/fxxk_it_alert.plistを以下の内容で作成した。
cronだと簡単に設定できるところをクソみたいな長大なxmlを書かねばならず、launchd の StartCalendarInterval はクソだという結論を得た。
設計を型にエンコードするということ
動的型付け vs 静的型漬けのアレでもんにょりしてたのをついったーに放出して会話してたらなんとなく自分なりの考えがまとまったので貼っておく。
……(ファントムタイプとか型安全ビルダーパターンとかで、型レベルにロジックをエンコードした結果、ライブラリ利用者がテストを書くまでもなく適切にライブラリを利用できるようになる、という意味での「静的型でコンパイラは最強のテスティングフレームワーク」という視点があると思う)
— 蒸発プログラマさん (@yuroyoro) 2013年3月11日
@yuroyoro 最初のメジャーなテスティングフレームワークがJUnitなので違和感を感じます。
— ぎゃばんぱみゅぱみゅさん (@ledsun) 2013年3月11日
@ledsun メジャーかそうでないかで言われると反論しようがないですが、捉え方というか視点として、ビジネスロジックを型レベルにエンコードして、テストの手前(コンパイラ)でチェックしようという思想があるという話です。
— 蒸発プログラマさん (@yuroyoro) 2013年3月11日
@ledsun @ledsun 例えば、型安全ビルダーパターンを使えば、ビルダーパターンでのメソッド呼び出し順を型レベルでチェックさせることができるので、そのビルダーを利用者が適切な順序で呼び出しているかコンパイラにチェックさせることができます。
— 蒸発プログラマさん (@yuroyoro) 2013年3月11日
@ledsun さきほどのビルダーパターンの例では、利用者がテストを書いて確認するところを、一足前にコンパイラがチェックしてくれるので、ライブラリの使い方を間違ってしまう心配がなくなります。実例として、ScalaでのMongoDBライブラリのRogueがこのようなことをしています
— 蒸発プログラマさん (@yuroyoro) 2013年3月11日
@yuroyoro なるほどそんな考え方があるんですね。ビルダーパターンはライブラリーやフレームワーク作る人からするとすごくありがたい機能に思えます。一方、すべてのアプリケーションプログラマが使いこなすのは難しそうなので、流行るのかどうか心配です。
— ぎゃばんぱみゅぱみゅさん (@ledsun) 2013年3月11日
@ledsun 現時点では、このような手法はなかなか受け入れられる環境ではないと思いますが、言語やツールが進化して敷居がさがればいいなーと思います。例えば、多相型(Generics)などは色々な言語に取り入れられ認知もされつつあり、この方向での言語の進化は確かにあると感じます
— 蒸発プログラマさん (@yuroyoro) 2013年3月11日
「最後にこの言葉を送る。
guud rack【グッドラック】
俺の好きな言葉だ。」
— 蒸発プログラマさん (@yuroyoro) 2013年3月11日
まとめると
- コンパイラがテストしてくれるように型を書きたい
- 型に対して、データ型のラベル以上の意味(設計の意図とか)を持たせたい
- そうすると、自分以外の人が自分が書いたコードを使うときに、正しい使い方を自分やドキュメントに代わって、コンパイラが教えてくれるようになる
- そういうことが簡単にできるように言語とか進化するといいよね
現実的になんでもかんでもコンパイラにやらせようとすると難しいところもあると思う。
「アカデミックだ」「現場では使えない」とか単に否定するのは簡単だ。
静的な型漬けの言語でコード書いたからって、上記のようにすべてのロジックを型レベルにエンコードすることなど不可能だし、制約が強すぎると感じるかも知れないし、この制約が逆に足かせになる性質のプロダクトがあることもわかっている。
でも、型推論とかGenericsとかは、アカデミックな研究と実用のニーズが相まって、
現実のプログラミング言語に取り入れられつつあるし、
静的な型漬けを好む人達が見据えているプログラミング言語の進化の方向性として、こういう考え方がありますよ、って知って欲しい。
git symbolic-ref を利用して、ブランチに対して別名をつけることができて墓ドル
何が便利かというと、今git-flowで運用しているのだけど、リリースブランチを常に同じブランチ名で参照できるようになってうれしい。
例えば、次回にリリースブランチが'release/1.17.0'だとすると、'git symbolic-ref rc release/1.17.0'とすることで、'rc'という名前で参照できる。
エイリアス先の'release/1.17.0'のHEADが先に進んでも、rcも常に同じHEADを参照するので、一貫して'rc'という名前で扱える。
$ git symbolic-ref rc refs/heads/release/1.17.0
これだけだとありがたみがない。が、無事に1.17.0がリリースされて、その次のリリースブランチがrelease/1.18.0になったときでも、'git symbolic-ref rc release/1.18.0'でエイリアスを張り直せばいい。リリースブランチは、常に一貫して'rc'という名前でアクセスできる。
この機能、git-flowに入れたい。
svn - Is it possible to alias a branch in Git? - Stack Overflow
httpstatus コマンドで、HTTP のステータスコードをすばやくしらべる!
一般的な Web Programmer ならば、HTTP Status code はすべて暗記していると聞きました。
しかし、僕は初心者なので、なかなか覚えきれていないので、HTTPのステータスコードをさがすのに便利なツールを用意しました。
httpstatus.hs です。インストール方法は 適当にコンパイルしてください。
Scala2.10.0のDependent method typesと型クラスを組み合わせた『The Magnet Pattern』がヤバい件
これが……型の力かッ……!!
spray | Blog » The Magnet Patternという記事で、「The Magnet Pattern」というデザインパターンが紹介されている。
これは、メソッドオーバーロードで解決していた問題を、型クラスとDependent method typesを組み合わせて置き換えることで、オーバーロードの際の様々な制約(Type Erasureなど)を突破し、より柔軟な拡張性を得ることができるというもの。このパターンでは、引数の型に応じて異なる結果型を返すようにできる。
この記事で、今まで何のために使われるのかわからんかったDependent method typesの有効性が理解でき、あらためて型の力を思い知った。
以前に"Generalized type constraints"(Scalaで<:<とか=:=を使ったgeneralized type constraintsがスゴすぎて感動した話 - ( ꒪⌓꒪) ゆるよろ日記) を知った時以来の感動だったので、勢いで書いてみた。
Dependent method types ってなんぞ?
簡単に言うと、引数の値に応じて結果型が変わるメソッドを定義できるということ。
Scala 2.10 に dependent method types というのが入るらしいよ - scalaとか・・・
以下の例では、fメソッドは、引数のFooトレイトのResult型に応じて結果型が変わる
trait Foo{ type Result def bar:Result } // 引数のFooトレイトのResult型に応じて結果型が変わる def f(obj:Foo):obj.Result = obj.bar
ResultをStringで定義してるstringFooオブジェクトと、Intで定義しているintFooオブジェクトを用意し、
val stringFoo = new Foo { type Result = String def bar:Result = "foo" } val intFoo = new Foo { type Result = Int def bar:Result = 99 }
fメソッドにそれぞれ渡すと、引数の値に応じて結果型が変わっていることがわかる。もちろん型安全なので、全てコンパイル時に型チェックが行われる。
scala> f(stringFoo) res0: stringFoo.Result = foo scala> f(intFoo) res1: intFoo.Result = 99 scala> f(stringFoo).getClass res2: Class[_ <: stringFoo.Result] = class java.lang.String scala> f(intFoo).getClass res3: Class[intFoo.Result] = int
The Magnet Patternが解決する問題
引数の型に応じてメソッドの振る舞いを変えたい場合、一つの手段としてメソッドをオーバーロードする、という手がある。 しかし 元記事では、オーバーロードでは以下のような問題が発生する、と述べている。
- type erasureにより引数の型が衝突する場合がある
- メソッドからFunctionオブジェクトに"lift"できない(println _のような)
- package objectでは使えない(2.9以前)
- 似たようなコードが多発
- デフォルト引数の利用に制限がある
- 引数の型推論に制限がある
The Magnet Patternは、この問題のいくつかを解決し、さらなるメリットをもたらす
- type erasureによる型の衝突は解決
- Functionオブジェクトへのliftは、全ての結果型が同一であれば可能
- package objectでも定義できる
- 実装をDRYにできる
- 危険なimplicit conversionの定義を避けることが出来る
- 引数の型に応じて異なる結果型を返すことが可能
このパターンは、拡張性に柔軟をもたらす一方で、DrakSideもあると。
- オーバーロードに比べて実装が細かく見通しが悪くなる
- 名前付きパラメータは利用できない
- by-name(名前渡し)パラメータとimplict conversionの組み合わせで重複してby-nameパラメータが評価されることがある
- Magnet Parttenで定義するメソッドは引数宣言が必須
- デフォルト引数は定義できない
- 引数に対しての型推論はできない(引数に無名関数を渡す場合などで)
The Magnet Patternの例
元記事では、例としてSprayにおけるURLルーティングを定義するDSLの実装をあげている。 非常にわかりやすいのでそっちを見てもらうのがいいのだけど、それだとあんまりなので例を書いてみる。
ここでの例は、3個のIntまたはStringから日付変換を行う関数toDateを考える。
通常のオーバーロードでの実装
object M { def toDate(year:Int, month:Int, date:Int): java.util.Date = { val c = java.util.Calendar.getInstance c.set(year, month - 1, date, 0, 0, 0) c.getTime } def toDate(s:String): java.util.Date = (new java.text.SimpleDateFormat("yyyy/MM/dd")).parse(s) }
まぁ見たとおりです。
scala> M.toDate(2013,1,21) res40: java.util.Date = Mon Jan 21 00:00:00 JST 2013 scala> M.toDate("2013/01/21") res41: java.util.Date = Mon Jan 21 00:00:00 JST 2013
The Magnet Patternで書き換えてみる
The Magnet Patternでは、オーバーロードを行う代わりに、メソッドの引数にある型(仮にmagnet型と呼ぶ)を一つだけとるようにする。 そして、そのmagnet型へのimplicit convesionを定義することで、オーバーロードと同様に引数の型に応じた振る舞いを定義できる。
さらに、The Magnet Patternでは引数の型に応じて結果型を変えることが可能である。 "magnet型"に抽象型で結果型を持たせ、Depenent method typesを利用して、引数がIntの場合はDateではなくCalndarを返すようにしている。
scala> :paste // Entering paste mode (ctrl-D to finish) // toDateは引数に"magnet型"を取るようにする。変換の実装はconvertメソッドに任せる // 結果型は、"magnet型"が持つ抽象型Resultに依存させている def toDate(magnet:DateMagnet):magnet.Result = magnet.convert // toDate関数で使われる"magnet型"のtrait trait DateMagnet{ type Result // 変換結果の結果型は抽象型で持つ def convert():Result // 変換を行うメソッド } // "magnet型"のコンパニオンオブジェクトに、 // それぞれの引数の型に合わせた"magnet型インスタンス"を返すimplicit defを // 定義しておく object DateMagnet { // 3つのIntからDateMagnetのインスタンスへ implicit def fromInt(tuple:(Int, Int, Int)) = new DateMagnet { type Result = java.util.Calendar def convert():Result = { val (year, month, date) = tuple val c = java.util.Calendar.getInstance c.set(year, month - 1, date, 0, 0, 0) c } } // StringからDateMagnetのインスタンスへ implicit def fromString(s:String) = new DateMagnet { type Result = java.util.Date def convert():Result = (new java.text.SimpleDateFormat("yyyy/MM/dd")).parse(s) } } // Exiting paste mode, now interpreting. warning: there were 2 feature warnings; re-run with -feature for details toDate: (magnet: DateMagnet)magnet.Result defined trait DateMagnet defined module DateMagnet scala> toDate(2013, 1, 21) res0: java.util.Calendar = java.util.GregorianCalendar[time=?,areFieldsSet=false,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Tokyo",offset=32400000,dstSavings=0,useDaylight=false,transitions=10,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2013,MONTH=0,WEEK_OF_YEAR=4,WEEK_OF_MONTH=4,DAY_OF_MONTH=21,DAY_OF_YEAR=25,DAY_OF_WEEK=6,DAY_OF_WEEK_IN_MONTH=4,AM_PM=1,HOUR=10,HOUR_OF_DAY=0,MINUTE=0,SECOND=0,MILLISECOND=88,ZONE_OFFSET=32400000,DST_OFFSET=0] scala> toDate("2013/01/21") res1: java.util.Date = Mon Jan 21 00:00:00 JST 2013
実装のポイントは、
- 引数は"magnet型"を一つとるようにする
- implicit conversionで引数の型に応じた"magnet型"のインスタンスへ変換する
- 型に応じた振る舞いは、"magnet型"のメソッドに実装する
- Dependent method typesを利用することで、"magnet型"の抽象型を差し替えて結果型を変えることができる
上記4点である。ひとことでいうと、オーバーロードで実装されている型毎の処理を型クラスに移した、といえる。
まとめ
メソッドのオーバーロードでは、Type Erasureによる制約などがあるが、 The Magnet Patternではimplicit convesionを定義することであとで対応する型を増やしたり、 結果型を変えたりと、様々な柔軟性を得ることができる。 一方で、実装が複雑になる、コードの見通しが悪くなる、などのデメリットもある。
興味深いのは、このThe Magnet Patternは型クラスやDepenent method typesを利用した、従来のOOPでは実現できない新しいプログラミングパラダイムにおけるデザインパターンであるという事実だ。
デザインパターンもGoFからまだまだ進化していると言える。
これが……型の力だッッッ!!!!
Rails3でMultiJsonのBackendをyajlに変更してJSONのエンコード/デコードのパフォーマンスを改善する
yajl(Yet Another JSON Library)っていう高速なJSONライブラリがあって、
こいつをrubyから使えるようにするyajl-rubyってgemがあって、これをMultiJsonのBackendに変更することで、RailsにおけるJSON処理の高速化が期待できるデス。
素のjson.gemと、yajl-rubyとで適当なActiveRecordオブジェクトからJSONへのエンコードと、その逆のデコードで簡単にベンチってみると、約2倍の差があることが分かる。
-------------------------------------------------------------------------------- Benchmark of json encoding/decoding json_gem vs yajl -------------------------------------------------------------------------------- | json_gem | yajl | json_gem/yajl | --Single ActiveRecord Object ----------------------------------------------------- encode x10000 | 12.130 | 6.167 | 1.97x | decode x10000 | 1.085 | 0.437 | 2.48x | --Array ActiveRecord Objects----------------------------------------------------- encode x10000 | 508.319 | 225.235 | 2.26x | decode x10000 | 39.069 | 19.869 | 1.97x |
MultiJsonのBackendをyajlに変えたら素のjson.gemの2倍のパフォーマンスになった件
MultiJsonは、yajl-rubyがあると自動的にそっちを見るようになってるので、gem 'yajl-ruby'するだけでjsonの処理が高速化する、とおもいきや……
ActiveSupport::JSONのコードを見てみると、JSONのデコード時にはMultiJsonを利用するようになっているが、エンコードする際にはActiveSupport独自の実装でエンコードするようになっている。この理由としては、ActiveSupportのAPIと他のライブラリの実装で互換性がないかもしれない、という話みたいだ。
Endoding with yajl-ruby for rails 3 · Issue #40 · brianmario/yajl-ruby · GitHub
とはいえ、オブジェクトをActiveSupportのas_jsonでHashにしてしまって、それをyajlでJSONにエンコードすれば問題ないはず。ということで、このようなパッチを書いた。
MultiJson.engine = :yajl unless MultiJson.engine == MultiJson::Adapters::Yajl module ActiveSupport module JSON def self.encode(value, options = nil) hash = ActiveSupport::JSON::Encoding::Encoder.new(options).as_json(value) MultiJson.encode(hash) end end end
JSONを大量にやりとりする系のアプリケーションには多少のパフォーマンス改善が期待できる、はず。