Skip to content

Instantly share code, notes, and snippets.

@nomisRev
Forked from kciesielski/freemonads.scala
Created July 19, 2018 11:41
Show Gist options
  • Save nomisRev/5eb787fa9cd110c5db66a1fca663d8a9 to your computer and use it in GitHub Desktop.
Save nomisRev/5eb787fa9cd110c5db66a1fca663d8a9 to your computer and use it in GitHub Desktop.
Free Monads example
package com.softwaremill.freemonads
import cats.free.Free
import cats.~>
import cats._, cats.std.all._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
sealed trait External[A]
case class Tickets(count: Int) extends AnyVal
case class InvokeTicketingService(count: Int) extends External[Tickets]
case class UserTicketsRequest(ticketCount: Int)
object GetTicketsExample {
def purchaseTickets(input: UserTicketsRequest): Free[External, Option[Tickets]] = {
if (input.ticketCount > 0) {
// creates a "Suspend" node
Free.liftF(InvokeTicketingService(input.ticketCount)).map(Some(_))
} else {
Free.pure(None)
}
}
def bonusTickets(purchased: Option[Tickets]): Free[External, Option[Tickets]] = {
if (purchased.exists(_.count > 10)) {
Free.liftF(InvokeTicketingService(1)).map(Some(_))
} else {
Free.pure(None)
}
}
def formatResponse(purchased: Option[Tickets], bonus: Option[Tickets]): String =
s"Purchased tickets: $purchased, bonus: $bonus"
val input = UserTicketsRequest(11)
val logic: Free[External, String] = for {
purchased <- purchaseTickets(input)
bonus <- bonusTickets(purchased)
} yield formatResponse(purchased, bonus)
val externalToServiceInvoker = new (External ~> Future) {
override def apply[A](e: External[A]): Future[A] = e match {
case InvokeTicketingService(c) => serviceInvoker.run(s"/tkts?count=$c")
}
}
val result = logic.foldMap(externalToServiceInvoker)
result.foreach(println)
object serviceInvoker {
def run(path: String) = {
Future {
Tickets(11)
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment