CLOVER🍀

That was when it all began.

Scalaz OptionW

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などがわかんないままなので、ちゃんと見直さないといけないですね。