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を大量にやりとりする系のアプリケーションには多少のパフォーマンス改善が期待できる、はず。
Jenkinsで外部パラメータで与えたブランチを対象にビルドできるようにしておくと凄惨性あがって墓ドル
テストが終わるまでの時間で書いてみる。
Jenkinsでジョブを実行させるときに、外部パラメータで任意のブランチを対象にビルドできると墓ドル。
例えば、自分のローカルブランチをマージするまえに、テストが通るか確認したい場合とか。
そんなのローカルでテストすりゃーいいじゃんって言われるかもしれないが、 テスト全部通すのに時間が掛かるようになってると、とりあえずCIに実行を投げておいてあとで確認するほうがずっと効率がいい。
F.Y.I: Building github branches with Jenkins
ジョブの設定
「ビルドのパラメータ化」にチェックをつけて、以下のようにbranchって名前のパラメータを設定しておく。
「ソースコード管理システム」で「Branches to build」のところに、設定したパラメータである"$branch"を入れておく。
ジョブの設定は以上。上記の方法はGitの場合だけど、他のVCSでも似たようなことできると思う多分。
ジョブの実行
ビルドを実行したいブランチをpushした上で、「ビルド実行」をクリックするとブランチを入力するように言われるのでよろしくやる。
自分は、ブラウザから実行させるのがタルいので、以下の用にcurlでjenkinsにリクエストを送る事でジョブをkickしている。墓・ドル。
$ curl -v http://<your jenkins host>/job/<job name>/buildWithParameters\?branch\=<branch name> * About to connect() <your jenkins host> port 80 (#0) * Trying <your jenkins ip>... connected * Connected to <your jenkins host> (192.168.1.171) port 80 (#0) > GET /job/yuroyoro-build/buildWithParameters?branch=origin/features/ci_test HTTP/1.1 > User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.12.9.0 zlib/1.2.3 libidn/1.18 libssh2/1.2.2 > Host: <your jenkins host> > Accept: */* > < HTTP/1.1 302 Found < Date: Thu, 20 Dec 2012 08:25:55 GMT < Server: Winstone Servlet Engine v0.9.10 < Location: http://<your jenkins host>/job/yuroyoro-build/ < Content-Length: 0 < X-Powered-By: Servlet/2.5 (Winstone/0.9.10) < Via: 1.1 <your jenkins host> < Content-Type: text/plain < * Connection #0 to host <your jenkins host> left intact * Closing connection #0
注意点として、[branch name]は'origin/features/ci_test'のように指定しないといけない。
実行結果の確認
ジョブの結果は、Growl pluginを利用して通知を受け取るようにしている。墓$。
実行ログは、jenkins.gemを使ってCLIから確認している。'jenkins [job name]'で最新の実行ログを取ってきてくれる。墓。
jenkins | RubyGems.org | your community gem host
cowboyd/jenkins.rb-history · GitHub
こんな感じで、ビルド実行ブランチをパラメータ化しておくことで、いつでも好きなときに、メインラインにマージ前のブランチのテストをCI鯖に任せることが可能になった。
curlとGrowl pluginとjenkins.gemのおかげで、「ジョブ投入 → 終了通知 → 結果確認」をすべてCLI内で完結させることができて、凄惨性あがって墓ドル。
.gitconfigでFizzBuzz
.gitconfigのtipsを公開するのが流行ってるみたいなので。
git config alias.fizzbuzz "!f() { seq "$@" | awk '$0=NR%15?NR%5?NR%3?$0:\"Fizz\":\"Buzz\":\"FizzBuzz\"' ;}; f"
( ꒪⌓꒪) git fizzbuzz <num> · 7a4ddcd · yuroyoro/dotfiles · GitHub
git fizzbuzz
ozaki@mbp-4 $ git fizzbuzz 30 1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz Fizz 22 23 Fizz Buzz 26 Fizz 28 29 FizzBuzz
Freeモナド in Scala
噂のFreeモナドをScalaで写経してみた。
Freeモナドは、取り込む型SのFunctorと組み合わせて、Functorの特性に応じたモナドを得ることができるものらしい。
そろそろFreeモナドに関して一言いっとくか - fumievalの日記
Freeモナドって何なのさっ!? - capriccioso String Creating(Object something){ return My.Expression(something); }
Haskell for all: Why free monads matter
stackless scala with free monad
独習 Scalaz: 18日目 | eed3si9n
( ゚д゚) 「ナンデ!?モナドナンデ!?」
モナドがセブンセンシズならFreeモナドはエイトセンシズくらいなのでかなり小宇宙を高めないと理解が追いつかないので、まずは写経してみた。
Freeモナドの定義
そのままScalaに移植するにあたって、型SはFunctorである必要があるので、Functor型クラスを用意し、flatMapにimplicit parameterでFunctor型クラスのインスタンスを注入する形式にする。
// type class: Functor trait Functor[F[_]] { def map[A, B](m: F[A])(f: A => B): F[B] } // data Free f a = Pure a | Free (f (Free f a)) // instance Functor f => Monad (Free f) where // return = Pure // Pure a >>= k = k a // Free fm >>= k = Free (fmap (>>=k) fm) f a)) // sealed trait FreeM[S[+_], +A] { def flatMap[B](f: A => FreeM[S, B])(implicit s: Functor[S]): FreeM[S, B] def map[B](f: A => B)(implicit s: Functor[S]): FreeM[S, B] = flatMap(a => Pure(f(a))) } case class Pure[S[+_], +A](a: A) extends FreeM[S, A] { def flatMap[B](f: A => FreeM[S, B])(implicit s: Functor[S]): FreeM[S, B] = f(a) } case class Free[S[+_], +A](k: S[FreeM[S, A]]) extends FreeM[S, A]{ def flatMap[B](f: A => FreeM[S, B])(implicit s: Functor[S]): FreeM[S, B] = Free(s.map(k)(_.flatMap(f))) }
haskellのをそのまま移植しただけで、特に難しいことはやってない。
サンプルのCharIOを写経する
そろそろFreeモナドに関して一言いっとくか - fumievalの日記 のCharIOを写経してみる。Haskell版ではIO[Char]になるが、ScalaにはIOモナド無いのでそのままCharでやる。
まずは、CharIO型の定義。これはそのまま。
// data CharIO a = GetCh (Char -> a) | PutCh Char a sealed trait CharIO[+A] case class GetCh[+A](f:Char => A) extends CharIO[A] case class PutCh[+A](c:Char, a:A) extends CharIO[A]
つぎに、CharIOのFunctorインスタンスを定義する。
// instance Functor CharIO where // fmap f (GetCh g) = GetCh (f . g) // fmap f (PutCh c x) = PutCh c (f x) implicit val charIOFunctor = new Functor[CharIO] { def map[A, B](a: CharIO[A])(f: A => B): CharIO[B] = a match { case GetCh(g) => GetCh(f compose g) case PutCh(c, x) => PutCh(c, f(x)) } }
これもそのまま。
続いて、Free[CharIO, Char]を返すユーティリティ関数を定義する。
// getCh :: Free CharIO Char // getCh = Free $ GetCh $ \ch -> Pure ch // // putCh :: Char -> Free CharIO () // putCh ch = Free $ PutCh ch (Pure ()) // def getCh:FreeM[CharIO, Char] = Free(GetCh({ch => Pure(ch)})) def putCh(ch:Char):FreeM[CharIO, Unit] = Free(PutCh(ch, Pure(())))
同じっすね。
では、サンプルどおりに、doの中でこれらの関数を用いてFree[CharIO, Char]を組み合わせてみる。
// mapM_ def mapFreeM[S[+_]:Functor, A](f:A => FreeM[S,Unit], s:Seq[A]):FreeM[S,Unit] = s.toList match { case x::xs => xs.foldLeft(f(x)){(m:FreeM[S,Unit],c:A) => m.flatMap{unit => f(c)} } case Nil => Pure(()) } // ex0 = do // mapM_ putCh "Hello, Haskeller! Please input a character:" // ch <- getCh // mapM_ putCh "The ordinal of the character is:" // mapM_ putCh (show (ord ch)) // mapM_ putCh ".\n Thank you!\n" } val io:FreeM[CharIO, Unit] = for{ _ <- mapFreeM(putCh, "Hello, Scala Programmer! Please input a character:") ch <- getCh _ <- mapFreeM(putCh, "The ordinal of the character is:") _ <- mapFreeM(putCh, ch.toInt.toString) _ <- mapFreeM(putCh, ".\n Thank you!\n") } yield ()
ScalaにはmapM_がないので、代わりにSeq[A]にそれぞれ(f: A => FreeM[S, Unit])を適用しつつflatMapでたたみ込むようmapFreeMを用意した。
で、Scalaにおけるモナド糖衣構文はforなので、これにFreeモナドを与えてみる。 "for{_ <- m ... } yield ()" って記法ェ……。
これで、Free[CharIO, Char]を結合させたもio:FreeM[CharIO, Unit]が得られた。これは、いわばSのFunctorによってflatMapで合成されたCharIOのLinkedListと言えなくもない。
あとは、このio:FreeM[CharIO, Unit]を渡り歩きながら処理を行うインタプリター関数を定義すればいいわけだ。
// runStdIO :: Free CharIO a -> IO a // runStdIO (Pure a) = return a // runStdIO (Free (GetCh f)) = getChar >>= \ch -> runStdIO (f ch) // runStdIO (Free (PutCh ch cont)) = putChar ch >> runStdIO cont def runCharIO(free:FreeM[CharIO, Unit]):Unit = free match { case Pure(a) => a case Free(GetCh(f)) => runCharIO(f(readChar())) case Free(PutCh(ch, cont)) => print(ch);runCharIO(cont) }
実行してみる。
scala> runCharIO(io)
Hello, Scala Programmer! Please input a character:The ordinal of the character is:97.
Thank you!