Last active
September 8, 2016 12:28
-
-
Save non/efe348119443f4f8ebdc to your computer and use it in GitHub Desktop.
From an anonymously published paper I found online: the Doge monad (translated to Scala).
This file contains hidden or 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
| package shibe | |
| object Doge { | |
| def such[A](a: A): Doge[A] = Such(a) | |
| def so[A](a1: A, a2: A): Doge[A] = So(a1, a2) | |
| def wow[A]: Doge[A] = Wow() | |
| def pure[A](a: A): Doge[A] = such(a) // added for stew on #scala | |
| } | |
| case class Such[A] private[shibe] (a: A) extends Doge[A] | |
| case class Many[A] private[shibe] (d1: Doge[A], d2: Doge[A]) extends Doge[A] | |
| case class So[A] private[shibe] (a1: A, a2: A) extends Doge[A] | |
| case class Wow[A] private[shibe] () extends Doge[A] | |
| sealed trait Doge[A] { | |
| def ap[B](f: Doge[A => B]): Doge[B] = | |
| f match { | |
| case Wow() => Wow() | |
| case Such(f) => map(f) | |
| case Many(df1, df2) => ap(df1) many ap(df2) | |
| case So(f1, f2) => map(f1) many map(f2) | |
| } | |
| def map[B](f: A => B): Doge[B] = | |
| this match { | |
| case Such(a) => Such(f(a)) | |
| case Many(d1, d2) => d1.map(f) many d2.map(f) | |
| case So(a1, a2) => So(f(a1), f(a2)) | |
| case Wow() => Wow() | |
| } | |
| def flatMap[B](f: A => Doge[B]): Doge[B] = | |
| this match { | |
| case Such(a) => f(a) | |
| case Many(d1, d2) => d1.flatMap(f) many d2.flatMap(f) | |
| case So(a1, a2) => f(a1) many f(a2) | |
| case Wow() => Wow() | |
| } | |
| def many(that: Doge[A]): Doge[A] = (this, that) match { | |
| case (Such(a1), Such(a2)) => So(a1, a2) | |
| case _ => Many(this, that) | |
| } | |
| } | |
| object Test { | |
| import Doge._ | |
| val a = so("production", "ready") many such("!") | |
| val b = such(wow[String] many _) many such(much => such("recursive")) | |
| val c = such("flatMap").flatMap(such) | |
| val doge = wow many such("doge" :: "forever" :: Nil) | |
| val x = such(3) many so(1, 2) many wow | |
| val y = such("monads") many wow many so("laws", "purity") | |
| val z = such(such("nesting") many wow) | |
| } |
This file contains hidden or 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
| package shibe | |
| import org.scalatest.matchers.ShouldMatchers | |
| import org.scalatest._ | |
| import prop._ | |
| import org.scalacheck.{Arbitrary, Gen} | |
| import Arbitrary.arbitrary | |
| class ILSDogeLaws extends DogeLaws[Int, Long, String] | |
| class DIBDogeLaws extends DogeLaws[Double, Int, Byte] | |
| class SSSDogeLaws extends DogeLaws[String, String, String] | |
| abstract class DogeLaws[A: Arbitrary, B: Arbitrary, C: Arbitrary] | |
| extends PropSpec with Matchers with GeneratorDrivenPropertyChecks { | |
| implicit def arbDoge[T: Arbitrary]: Arbitrary[Doge[T]] = | |
| Arbitrary { | |
| val genWow: Gen[Wow[T]] = Gen.value(Wow[T]) | |
| val genSuch: Gen[Such[T]] = for { a <- arbitrary[T] } yield Such(a) | |
| val genSo: Gen[So[T]] = for { a1 <- arbitrary[T]; a2 <- arbitrary[T] } yield So(a1, a2) | |
| def genMany(sz: Int): Gen[Doge[T]] = for { | |
| n <- Gen.choose(sz / 3, sz / 2) | |
| d1 <- sizedDoge(sz / 2) | |
| d2 <- sizedDoge(sz / 2) | |
| } yield d1 many d2 | |
| def sizedDoge(sz: Int): Gen[Doge[T]] = | |
| if(sz <= 0) Gen.frequency((1, genWow), (1, genSuch), (1, genSo)) | |
| else Gen.frequency((1, genWow), (1, genSuch), (1, genSo), (1, genMany(sz))) | |
| Gen.sized(sz => sizedDoge(sz)) | |
| } | |
| property("left identity") { | |
| forAll { (a: A, f: A => Doge[B]) => | |
| Doge.such(a).flatMap(f) shouldBe f(a) | |
| } | |
| } | |
| property("right identity") { | |
| forAll { (da: Doge[A]) => | |
| da.flatMap(Doge.such) shouldBe da | |
| } | |
| } | |
| property("associativity") { | |
| forAll { (da: Doge[A], f: A => Doge[B], g: B => Doge[C]) => | |
| da.flatMap(f).flatMap(g) shouldBe da.flatMap(a => f(a).flatMap(g)) | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment