Skip to content

Instantly share code, notes, and snippets.

@cranst0n
Last active April 27, 2016 15:37
Show Gist options
  • Save cranst0n/04582e26b5dacf80a5e6 to your computer and use it in GitHub Desktop.
Save cranst0n/04582e26b5dacf80a5e6 to your computer and use it in GitHub Desktop.
package freedom
import java.awt.{ Color, Graphics2D }
import java.awt.image.BufferedImage
import cats.data.Coproduct
import cats.free.{ Free, Inject }
import cats.{ ~>, Id }
object Freedom extends App {
import interaction._
import logging._
import rendering._
type InteractOrLog[A] = Coproduct[InteractionOp, LoggingOp, A]
type App[A] = Coproduct[RenderingOp, InteractOrLog, A]
def drawText(text: String)(implicit R: Rendering[App], I: Interaction[App], L: Logging[App]) = {
import I._, R._, L._
for {
_ <- tell("here's a message")
_ <- setColor(Color.orange)
_ <- debug("set the color")
_ <- fillRect(25, 25, 50, 50)
_ <- debug("filled the rect")
_ <- setColor(Color.blue)
_ <- drawString(text, 10, 10)
_ <- debug(s"rendered: $text")
} yield ()
}
val img = new BufferedImage(250, 250, BufferedImage.TYPE_INT_RGB)
// Be sure to annotate the types
val x: InteractOrLog ~> Id = DefaultInterpreter or ConsoleInterpreter
val interpreter: App ~> Id = new GInterpreter(img.createGraphics()) or x
drawText("Hello Free!") foldMap interpreter
// Dump the results for inspection
javax.imageio.ImageIO.write(img, "png", new java.io.File("/home/brandy/Desktop/free-image.png"))
}
object interaction {
sealed trait InteractionOp[A]
case class Tell(message: String) extends InteractionOp[Unit]
class Interaction[F[_]](implicit I: Inject[InteractionOp, F]) {
def tell(message: String) = Free.inject[InteractionOp, F](Tell(message))
}
object Interaction {
implicit def instance[F[_]](implicit I: Inject[InteractionOp, F]): Interaction[F] = new Interaction[F]()
}
object DefaultInterpreter extends (InteractionOp ~> Id) {
def apply[A](fa: InteractionOp[A]): Id[A] = {
fa match {
case Tell(message) => println(message)
}
}
}
}
object logging {
sealed trait LoggingOp[A]
case class Debug(message: String) extends LoggingOp[Unit]
class Logging[F[_]](implicit I: Inject[LoggingOp, F]) {
def debug(message: String) = Free.inject[LoggingOp, F](Debug(message))
}
object Logging {
implicit def instance[F[_]](implicit I: Inject[LoggingOp, F]): Logging[F] = new Logging[F]()
}
object ConsoleInterpreter extends (LoggingOp ~> Id) {
def apply[A](fa: LoggingOp[A]): Id[A] = {
fa match {
case Debug(message) => println(message)
}
}
}
}
object rendering {
import java.awt._
sealed trait RenderingOp[A]
case class FillRect(x: Int, y: Int, width: Int, height: Int) extends RenderingOp[Unit]
case class DrawString(str: String, x: Int, y: Int) extends RenderingOp[Unit]
case class SetColor(c: Color) extends RenderingOp[Unit]
class Rendering[F[_]](implicit I: Inject[RenderingOp, F]) {
def drawString(str: String, x: Int, y: Int) = Free.inject[RenderingOp, F](DrawString(str, x, y))
def fillRect(x: Int, y: Int, width: Int, height: Int) = Free.inject[RenderingOp, F](FillRect(x, y, width, height))
def setColor(c: Color) = Free.inject[RenderingOp, F](SetColor(c))
}
object Rendering {
implicit def instance[F[_]](implicit I: Inject[RenderingOp, F]): Rendering[F] = new Rendering[F]()
}
class GInterpreter(g: Graphics2D) extends (RenderingOp ~> Id) {
def apply[A](fa: RenderingOp[A]): Id[A] = {
fa match {
case DrawString(str, x, y) => g.drawString(str, x, y)
case FillRect(x, y, width, height) => g.fillRect(x, y, width, height)
case SetColor(c) => g.setColor(c)
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment