Skip to content

Instantly share code, notes, and snippets.

@non
Last active September 8, 2016 12:28
Show Gist options
  • Select an option

  • Save non/efe348119443f4f8ebdc to your computer and use it in GitHub Desktop.

Select an option

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).
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)
}
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