Skip to content

Instantly share code, notes, and snippets.

@andyscott
Last active October 26, 2016 18:42
Show Gist options
  • Save andyscott/8831c5590685b958100d65a5b22c2ac4 to your computer and use it in GitHub Desktop.
Save andyscott/8831c5590685b958100d65a5b22c2ac4 to your computer and use it in GitHub Desktop.
import cats._
import monix.eval.Task
import monix.execution.Scheduler
import scala.concurrent.Await
import scala.concurrent.duration._
object FizzBuzzer {
def main(args: Array[String]): Unit = {
val taskIO: IOOp ~> Task =
new (IOOp ~> Task) {
import IOOp._
override def apply[A](op: IOOp[A]): Task[A] = op match {
case PrintFizz ⇒ Task(println("Fizz"))
case PrintBuzz ⇒ Task(println("Buzz"))
case PrintFizzBuzz ⇒ Task(println("FizzBuzz"))
case PrintNum(n) ⇒ Task(println(s"$n"))
}
}
val io = new IOOps(taskIO)
val math = new MathOps(new DefaultMathOpInterpreter)
def fizzBuzz(current: Long, zero: Long, three: Long, five: Long, hundo: Long): Task[Unit] =
for {
mod3isZero ← math.mod(current, three).map(_ == zero)
mod5isZero ← math.mod(current, five).map(_ == zero)
_ ← (mod3isZero, mod5isZero) match {
case (true, true) ⇒ io.printFizzBuzz()
case (true, false) ⇒ io.printFizz()
case (false, true) ⇒ io.printBuzz()
case _ ⇒ io.printNum(current)
}
next ← math.increment(current)
continue ← math.gt(next, hundo).map(!_)
_ ← {
if (continue) fizzBuzz(next, zero, three, five, hundo)
else Task(())
}
} yield ()
val program = for {
`0` ← math.zero()
`1` ← math.increment(`0`)
`2` ← math.increment(`1`)
`3` ← math.increment(`2`)
`4` ← math.increment(`3`)
`5` ← math.increment(`4`)
`10` ← math.times(`2`, `5`)
`100` ← math.times(`10`, `10`)
_ ← fizzBuzz(`1`, `0`, `3`, `5`, `100`)
} yield ()
val future = program.runAsync(Scheduler.global)
Await.result(future, 10.seconds)
}
}
class IOOps[F[_]](lift: IOOp ~> F) {
import IOOp._
def printFizz(): F[Unit] = lift(PrintFizz)
def printBuzz(): F[Unit] = lift(PrintBuzz)
def printFizzBuzz(): F[Unit] = lift(PrintFizzBuzz)
def printNum[N: Numeric](n: N): F[Unit] = lift(PrintNum(n))
}
sealed trait IOOp[A]
object IOOp {
case object PrintFizz extends IOOp[Unit]
case object PrintBuzz extends IOOp[Unit]
case object PrintFizzBuzz extends IOOp[Unit]
case class PrintNum[N: Numeric](n: N) extends IOOp[Unit]
}
class MathOps[F[_]](lift: MathOp ~> F) {
import MathOp._
def zero(): F[Long] = lift(Zero())
def increment(v: Long): F[Long] = lift(Increment(v))
def mod(dividend: Long, divisor: Long): F[Long] = lift(Mod(dividend, divisor))
def times(x: Long, y: Long): F[Long] = lift(Times(x, y))
def gt(x: Long, y: Long): F[Boolean] = lift(Gt(x, y))
}
sealed trait MathOp[A]
object MathOp {
case class Zero() extends MathOp[Long]
case class Increment(v: Long) extends MathOp[Long]
case class Mod(dividend: Long, divisor: Long) extends MathOp[Long]
case class Times(x: Long, y: Long) extends MathOp[Long]
case class Gt(x: Long, y: Long) extends MathOp[Boolean]
}
class DefaultMathOpInterpreter extends (MathOp ~> Task) {
import MathOp._
override def apply[A](rawOp: MathOp[A]): Task[A] = rawOp match {
case op: Zero ⇒ handleZero(op)
case op: Increment ⇒ handleIncrement(op)
case op: Mod ⇒ handleMod(op)
case op: Times ⇒ handleTimes(op)
case op: Gt ⇒ handleGt(op)
}
def handleZero(op: Zero): Task[Long] =
Task(0L)
def handleIncrement(op: Increment): Task[Long] =
Task(op.v + 1L)
def handleMod(op: Mod): Task[Long] = Task {
op.dividend % op.divisor
}
def handleTimes(op: Times): Task[Long] = Task {
op.x * op.y
}
def handleGt(op: Gt): Task[Boolean] =
Task(op.x > op.y)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment