Skip to content

Instantly share code, notes, and snippets.

@erichonorez
Created December 7, 2016 20:18
Show Gist options
  • Save erichonorez/61d7685daa8b714d5dc5709d54e63ffe to your computer and use it in GitHub Desktop.
Save erichonorez/61d7685daa8b714d5dc5709d54e63ffe to your computer and use it in GitHub Desktop.
Future & Option composition in scala
// Start writing your ScalaFiddle code here
import scala.util.{Success, Failure}
import scala.concurrent._
import scala.concurrent.ExecutionContext.Implicits.global
import scalaz._
import Scalaz._
import scalaz.OptionT._
type User = String
type UserDetail = String
type Url = String
trait Env
object Env extends Env
trait UserService[UserId, User, UserDetail, Url] {
type Query[A] = Kleisli[Future, Env, A]
def list: Query[List[User]]
def details(id: User): Query[Option[UserDetail]]
def pic(id: User): Query[Option[Url]]
def fetch(id: UserId): Query[Option[User]]
}
class UserServiceInterpreter extends UserService[String, User, UserDetail, Url] {
def list: Query[List[User]] = {
Kleisli { e =>
Future {
List("1", "2", "3")
}
}
}
def details(id: User): Query[Option[UserDetail]] = {
Kleisli { e =>
Future {
id match {
case "1" => Some("1")
case "2" => throw new IllegalStateException
case "3" => Some("33")
case _ => None
}
}
}
}
def pic(id: User): Query[Option[Url]] = {
Kleisli { e =>
Future {
id match {
case "1" => Some("u11")
case "2" => Some("u22")
case "3" => throw new IllegalStateException
case _ => None
}
}
}
}
def fetch(id: String): Query[Option[User]] = {
Kleisli { e =>
Future {
id match {
case "1" => Some("1")
case "2" => Some("2")
case "3" => Some("3")
case _ => None
}
}
}
}
}
object UserService extends UserServiceInterpreter
def list: Kleisli[Future, Env, List[User]] = {
Kleisli { e =>
Future {
List("1", "2", "3")
}
}
}
def details(id: User): Kleisli[Future, Env, UserDetail] = {
Kleisli { e =>
Future {
id match {
case "1" => "11"
case "2" => throw new IllegalStateException
case "3" => "33"
}
}
}
}
def pic(id: User): Kleisli[Future, Env, Url] = {
Kleisli { e =>
Future {
id match {
case "1" => "u11"
case "2" => "u22"
case "3" => throw new IllegalStateException
}
}
}
}
def op: Future[List[(User, UserDetail, Url)]] = {
for {
users <- list(Env)
details <- Future.traverse(users)(u => details(u)(Env) recover { case _ => "N/A" })
pics <- Future.traverse(users)(u => pic(u)(Env) recover { case _ => "N/A" })
} yield (users, details, pics).zipped.toList
}
def composition: Kleisli[Future, Env, List[(User, UserDetail, Url)]] = {
Kleisli { e =>
for {
users <- list(e)
details <- Future.traverse(users)(u => details(u)(e) recover { case _ => "N/A" })
pics <- Future.traverse(users)(u => pic(u)(e) recover { case _ => "N/A" })
} yield (users, details, pics).zipped.toList
}
}
def compositionWithSvc: Kleisli[Future, Env, List[(User, Option[UserDetail], Option[Url])]] = {
Kleisli { e =>
for {
users <- UserService.list(e)
details <- Future.traverse(users)(u => UserService.details(u)(e) recover { case _ => None })
pics <- Future.traverse(users)(u => UserService.pic(u)(e) recover { case _ => None })
} yield (users, details, pics).zipped.toList
}
}
op map { xs => xs.foreach(x => println(s"${x._1}, ${x._2}, ${x._3}")) }
composition(Env) map { xs => xs.foreach(x => println(s"${x._1}, ${x._2}, ${x._3}")) }
compositionWithSvc(Env) map { xs =>
xs.foreach(x => {
val user = x._1
val detail = x._2 getOrElse "N/A"
val pic = x._3 getOrElse "N/A"
println(s"${user}, ${detail}, ${pic}")
})
}
def test(id: String): Kleisli[Future, Env, Option[(User, UserDetail, Url)]] = {
Kleisli { e =>
(for {
user <- optionT(UserService.fetch(id)(e))
detail <- optionT(UserService.details(user)(e))
pic <- optionT(UserService.pic(user)(e))
} yield (user, detail, pic)).run
}
}
def test2(id: String): Kleisli[Future, Env, Option[(User, Option[UserDetail], Option[Url])]] = {
Kleisli { e =>
UserService.fetch(id)(e) flatMap { userO => userO match {
case None => Future { None }
case Some(user) => {
val detailsF = UserService.details(user)(e) recover { case _ => None }
val picF = UserService.pic(user)(e) recover { case _ => None }
(detailsF |@| picF)(Some(user, _, _))
}
}}
}
}
test("1")(Env) map { tupleO => tupleO match {
case None => println("1 = None")
case Some(tuple) => println(s"1 = ${tuple._1} ${tuple._2} ${tuple._3}")
}
}
test("4")(Env) map { tupleO => tupleO match {
case None => println("4 = None")
case Some(tuple) => println(s"4 = ${tuple._1} ${tuple._2} ${tuple._3}")
}
}
test2("3")(Env) map { tupleO => tupleO match {
case None => println("3 = None")
case Some(tuple) => println(s"3 = ${tuple._1} ${tuple._2} ${tuple._3}")
}
}
test("2")(Env) onComplete { f => f match {
case Failure(_) => println("2 failed")
case Success(tupleO) => tupleO match {
case None => println("2 = None")
case Some(tuple) => println(s"2 = ${tuple._1} ${tuple._2} ${tuple._3}")
}
}}
test2("2")(Env) map { tupleO => tupleO match {
case None => println("2 = None")
case Some(tuple) => println(s"2 = ${tuple._1} ${tuple._2} ${tuple._3}")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment