Skip to content

Instantly share code, notes, and snippets.

@awekuit
Last active August 29, 2015 14:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save awekuit/38554e66de6c326674cd to your computer and use it in GitHub Desktop.
Save awekuit/38554e66de6c326674cd to your computer and use it in GitHub Desktop.
Scalaz の Free と Coyoneda で Option (Maybe) Monad 的なもの。
/**
* Free + Coyoneda で Option(Maybe) Monad 的なもの.
*
* OptionをFreeで実現するメリットは特に無いですが
* Coyoneda経由のFreeまでのリフトの流れ
* インタプリタの働きなどを見る学習用です
*
* twitter: @awekuit
*/
import scala.language.higherKinds
import scalaz.Free.FreeC
import scalaz._
import Scalaz._
// Option(Maybe) Monad 的なもの
trait Box[+A]
case class SomeBox[+A](x: A) extends Box[A]
case object EmptyBox extends Box[Nothing]
// インタプリタその1. 直接Aを返す(EmptyBoxは単位元を返す事にする)
def run1[A: Monoid](program: FreeC[Box, A]): A = {
program.resume match {
// Suspendの場合
case -\/(coyo) =>
coyo.fi match {
case SomeBox(x) => run1(coyo.k(x))
case EmptyBox => mzero[A]
}
// Return(Pure)の場合
case \/-(x) => x
}
}
// インタプリタその2. Box[A]を返す
def run2[A](program: FreeC[Box, A]): Box[A] = {
program.resume match {
// Suspendの場合
case -\/(coyo) =>
coyo.fi match {
case SomeBox(x) => run2(coyo.k(x))
case EmptyBox => EmptyBox
}
// Return(Pure)の場合
case \/-(x) => SomeBox(x)
}
}
// インタプリタその3. run2を ~.resume.fold を使うパターンに(外側のmatchが不要になる)
def run3[A](program: FreeC[Box, A]): Box[A] = {
program.resume.fold(
// Suspendの場合
coyo => coyo.fi match {
case SomeBox(x) => run3(coyo.k(x))
case EmptyBox => EmptyBox
},
// Return(Pure)の場合
x => SomeBox(x)
)
}
// インタプリタその4. Maybeへの自然変換(変換後のMonad実装が使われる)
// 自然変換でscalazのエコシステムに乗せると便利
def hakointr: Box ~> Maybe = new (Box ~> Maybe) {
def apply[A](t: Box[A]): Maybe[A] = t match {
case SomeBox(x) => Maybe.Just(x)
case EmptyBox => Maybe.Empty()
}
}
// お試し
val t = for {
a <- Free.liftFC[Box, Int] (SomeBox(2))
b <- Free.liftFC[Box, Int] (SomeBox(5))
c <- Free.liftFC[Box, String](SomeBox("7"))
// c <- Free.liftFC[Box, String](EmptyBox)
} yield a * b + c.toInt
val res = t.map(_.toString + "! Yeah!")
// 結果
run1(res) // res0: String = 17! Yeah!
run2(res) // res1: Box[String] = SomeBox(17! Yeah!)
run3(res) // res2: Box[String] = SomeBox(17! Yeah!)
Free.runFC(res)(hakointr) // res3: scalaz.Maybe[String] = Just(17! Yeah!)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment