ScalazのValidationもFSharpxのValidationもApplicative
ドラゴンズドグマが楽しみだったり、しおりを温めていたScala実践プログラミングの読書を再開したりな今日この頃。
ご多聞に漏れずわたくしも五月病なので軽めのネタで。とゆーかですね、FSharpxのステマです。
FSharpxのValidationでFizzBuzz
元ネタ
ScalazのValidationでFizzBuzz
http://d.hatena.ne.jp/terazzo/20111015/1318692810
続・ScalazのValidationでFizzBuzz
http://d.hatena.ne.jp/terazzo/20111018/1318959813
そもそものきっかけはこのあたり
2012-05-08 17:02:40 via web
ScalazのValidationの謎
http://d.hatena.ne.jp/terazzo/20111022/1319295098
勉強になります。ということで、「ScalazのValidationはモナドではない」であっていました。しかしながら、Scalazの場合は「動作を変えてモナドのインスタンスにすることもできる」んですね。でもそれってどうなの?エラー情報をaccumulateしないValidationって一体何。とっても意味ないんじゃー感がするんですが。できるというだけでやらないですね。
で、FSharpxというF#のOSSライブラリでも同じくValidationが実装されていまして、エラーをListMonoidとして扱ってエラー情報をaccumulateします。もちろんFSharpxのValidationもモナドではなくアプリカティブ(Applicative)として実装されています。ちょっと関連する以下のようなつぶやきも踏まえつつ、FSharpxと友達になるべくこれでFizzBuzzしてみましょう。
Choice<'T1,'T2>をEither<'T,'U>として使うべきか否かはfsharpxの中の人達の間でも議論が交わされたみたいだけど、非同期ワークフローで採用されているわけだし、結局は"使う"に落ち着いて、新たにEither作らない方向でってゆー。名前びみょいけどねえ
名前がびみょすぎる・・・
Choice<'T1,'T2>なのか・・・名前ェ・・・
2012-05-07 11:48:52 via web
タプルの汎用はそこそこありだけど Choice<> を生で使うのは極力避けたい派、というかダメじゃね。
FSharpxの中の人的には、Choice<'T1,'T2>が生で使われちゃうこともある程度許容しているような雰囲気もなくはないですが、
生でヤっちゃうといろいろとアレということで、判別供用体のChoice1Of2とChoice2Of2を申し訳程度にラップしておく。
let success = Choice1Of2 let failure = Choice2Of2 open FSharpx.Validation let createChoice d s = fun n -> if n % d = 0 |> not then success n else failure [s] let fizz = createChoice 3 "Fizz" let buzz = createChoice 5 "Buzz" let (<*) a b = lift2 (fun x _ -> x) a b let fizzbuzz n = fizz n <* buzz n |> function | Success n -> string n | Failure e -> List.fold (fun a b -> b + a) "" e [1..100] |> Seq.iter (fun x -> fizzbuzz x |> printfn "%s") System.Console.ReadLine () |> ignore
って、ちょっと待って。FSharpxにおいてもValidationはちゃーんとApplicative考慮されていますし、当然のようにプログラムをApplicativeスタイルで書くための(<*)演算子はValidationモジュールにすでに定義済みです。上でわざわざ書いたのは、lift2していますよということを強調したかっただけでした。あとついでといっちゃーなんですが、お気に入りの「にっこり演算子」もおまけで追加しておきましょう。
let success = Choice1Of2 let failure = Choice2Of2 let (^-^) x f = x f (* にっこり *) open FSharpx.Validation let createChoice d s = fun n -> if n % d = 0 |> not then success n else failure [s] let fizz = createChoice 3 "Fizz" let buzz = createChoice 5 "Buzz" let fizzbuzz n = fizz n <* buzz n |> function | Success n -> string n | Failure e -> List.fold (fun a b -> b + a) "" e [1..100] |> Seq.iter ^-^ fun x -> fizzbuzz x |> printfn "%s" System.Console.ReadLine () |> ignore
シンプル。Validation本来の使い方とはちとズレていますが、サンプルとしてはイメージがつかみやすく結構わかり良いんじゃないでしょうか。元ネタのterazzoさんナイスですね。
参考
Scala の Either についての考察http://d.hatena.ne.jp/xuwei/20110927/1317156625
Scalaz 6.0.4 と Haskell (GHC7.4.1) を比べてみることによってScalazのclassを分類して理解してみる
http://d.hatena.ne.jp/xuwei/20120204/1328377968