Last active
August 29, 2015 14:09
-
-
Save awekuit/38554e66de6c326674cd to your computer and use it in GitHub Desktop.
Scalaz の Free と Coyoneda で Option (Maybe) Monad 的なもの。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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