2012年、明けましておめでとうございます。更新ペースはマイペースだと思いますが、頑張って書いていこうと思いますので、今年もどうぞよろしくお願い致します。
さて、前々回〜前回の間より、さらに間隔が空いてしまいましたが、今度はOptionWです。
Optionの拡張だから簡単かな?と思っていたら、これがかなりハマりました…。
?
これはわかりやすいですね。BooleanWの時とほぼ同じで、Conditionalトレイトが返るので「|」メソッドと共に使用します。
println("Some(10) ? => " + { some(10) ? "ten" | "zero" }) println("None[Int] ? => " + { none[Int] ? "ten" | "zero" })
実行結果。
Some(10) ? => ten
None[Int] ? => zero
cata
なに、このメソッド名?と思うところですが、元のOptionがSomeであれば第1引数にSomeの中身が渡されるのでそこから結果を返し、Noneであれば第2引数の結果が返ります。
println("Some(10) cata => " + { some(10) cata (s => s, 0) }) println("Nome[Int] cata => " + { none[Int] cata (s => s, 0) })
実行結果。
Some(10) cata => 10 Nome[Int] cata => 0
err
変換前のOptionがNoneの場合は、RuntimeExceptionがスローされます。引数には、例外に含まれるメッセージを指定できます。Someだった場合は、Someの中身が返ります。
println("Some(10) err => " + { some(10) err ("This is Some") }) println("None[Int] err => " + { try { none[Int] err ("This is None") } catch { case e: RuntimeException => e.getMessage } })
実行結果。
Some(10) err => 10 None[Int] err => This is None
fold
cataメソッドと同じです。
println("Some(10) fold => " + { some(10) fold (s => s, 0) }) println("None[Int] fold => " + { none[Int] fold (s => s, 0) })
実行結果。
Some(10) fold => 10 None[Int] fold => 0
cataとそっくりだなーと思っていたら、実装もそうなっていました。
OptionW.scala
/** Alias for `cata` */ def fold[X](some: A => X, none: => X): X = cata(some, none)
foldLift
これは相当ハマりました。定義が
/** * Returns the given value if None, otherwise lifts the Some value and passes it to the given function. */ def foldLift[F[_], B](b: => B, k: F[A] => B)(implicit p: Pure[F]): B = value match { case None => b case Some(a) => k(a.η[F]) }
となっているのですが、Fって何…?と考えて出てきたものは、Functorだ…。
う〜ん、これはまだ知らないなぁ。とりあえず、OptionはFunctorとして扱えるようなので、これを元に書いてみました。というか、これ自体は次のfoldLiftOptをマネしたものです。
元がNoneの時は第1引数の結果が返り、Someの時は第2引数にそれが渡ってくるので、そこから結果を返します。
println("Some(10) foldLift => " + { some(10).foldLift[Option, Int](0, s => s.get) }) println("None[Int] foldLift => " + { none[Int].foldLift[Option, Int](0, s => s.get) })
実行結果。
Some(10) foldLift => 10 None[Int] foldLift => 0
foldLiftOpt
foldLiftでFunctorをOptionにしたものです。先で使ったfoldLiftとほぼ同じサンプルになります。
println("Some(10) foldLiftOpt => " + { some(10) foldLiftOpt (0, s => s.get) }) println("None[Int] foldLiftOpt => " + { none[Int] foldLiftOpt (0, s => s.get) })
実行結果。
Some(10) foldLiftOpt => 10 None[Int] foldLiftOpt => 0
fst
FirstOptionが返ってきます。valueでOptionが取り出せます。
println("Some(10) fst => " + { some(10).fst.value.getOrElse(0) }) println("None[Int] fst => " + { none[Int].fst.value.getOrElse(0) })
実行結果。
Some(10) fst => 10 None[Int] fst => 0
ifNone
元のOptionがNoneだった場合に、引数で渡した関数を実行します。
some(10) ifNone println("Some(10) ifNone => LNot None") none[Int] ifNone println("None[Int] ifNone => None")
実行結果。
None[Int] ifNone => None
iterDoneOr
これも苦戦したものの1つです。ここで初めてIterVが登場するのですが、これがあんまりよくわかっていません。
とりあえず、Scalazに同梱されているExampleIteratee.scalaと以下のサイトを参考に書いてみました。
http://basking-cat.blogspot.com/2011/12/iterv.html
使用する前に、暗黙のパラメータを定義しておきます。IterVをInt, Listとしているので、IterV#runの結果はListになります。
import scalaz.IterV._ implicit val StreamEnumerator = new Enumerator[Stream] { def apply[E, A](e: Stream[E], i: IterV[E, A]): IterV[E, A] = e match { case Stream() => i case x #:: xs => i.fold(done = (_, _) => i, cont = k => apply(xs, k(El(x)))) } } println("Some(10) iterDoneOr => " + { some(10).iterDoneOr(Nil, s => collect[Int, List].apply(Stream(s))).run }) println("None[Int] iterDoneOr => " + { none[Int].iterDoneOr(Nil, s => collect[Int, List].apply(Stream(s))).run })
実行結果。
Some(10) iterDoneOr => List(10) None[Int] iterDoneOr => List()
lst
fstの反対。LastOptionが返ります。
println("Some(10) lst => " + { some(10).lst.value | 0 }) println("None[Int] lst => " + { none[Int].lst.value | 0 })
こちらは、ちょっとScalazっぽくしてみまhした。
実行結果。
Some(10) lst => 10 None[Int] lst => 0
orEmpty
Someの時はPureが返り、Noneの時はEmptyが返ります。PureとEmptyについては、まだ分かってません…。
println("Some(10) orEmpty => " + { some(10).orEmpty[List] }) println("None[Int] orEmpty => " + { none[Int].orEmpty[List] })
実行結果。
Some(10) orEmpty => List(10) None[Int] orEmpty => List()
orZero
Someの時はSomeの中身が、Noneの時はZeroが返ります。
println("Some(10) orZero => " + { some(10) orZero }) println("None[Int] orZero => " + { none[Int] orZero })
実行結果。
Some(10) orZero => 10 None[Int] orZero => 0
some
Foldが返ります。Foldはnoneというメソッドを持ち、元のOptionがNoneだった場合はnoneに渡した関数が実行されます。Someだった場合は、someメソッドに渡した関数の中身が実行されます。
println("Some(10) some => " + { some(10).some(_ * 10).none(5) }) println("None[Int] some => " + { none[Int].some(_ * 10).none(5) })
実行結果。
Some(10) some => 100 None[Int] some => 5
toFailure
Noneの場合がEither.Rightとして扱われます。SomeはEither.Leftになります。
println("Some(10) toFailure => " + { some(10).toFailure("Fail").either.left.get }) println("None[Int] toFailure => " + { none[Int].toFailure("Fail").either.right.get })
実行結果。
Some(10) toFailure => 10 None[Int] toFailure => Fail
toSuccess
toFailureの反対。
println("Some(10) toSuccess => " + { some(10).toSuccess("Success").either.right.get }) println("None[Int] toSuccess => " + { none[Int].toSuccess("Success").either.left.get })
実行結果。
Some(10) toSuccess => 10 None[Int] toSuccess => Success
unary_~
Option#getOrElseですが、Noneの場合はZeroが返ります。
println("~some(10) => " + ~some(10)) println("~None[Int]= => " + none[Int])
実行結果。
~some(10) => 10 ~None[Int]= => None
|
Option#getOrElseと同義です。
println("some(10) | => " + { some(10) | 0 }) println("None[Int] | => " + { none[Int] | 0 })
実行結果。
some(10) | => 10 None[Int] | => 0
なかなかてこずりました。Functor、Monad、IterVなどがわかんないままなので、ちゃんと見直さないといけないですね。