Scala for comprehension with Future, List and Option - scala

I am building a reactive site in Scala and Play Framework, and my data models are such that I often need to compose Future and Option, and build Future of List / Set from previous values to get the result I need.
I wrote a simple app with a fake data source that you can copy and paste and it should compile. My question is, how can I get the result back, in my case UserContext, in a consumable form. Currently, I am getting back Future[Option[Future[UserContext]]].
I want to do this in pure Scala to learn the language better, so I am avoiding Scalaz at the moment. Although I know I should eventually use that.
package futures
import scala.concurrent.{Future, ExecutionContext}
// http://www.edofic.com/posts/2014-03-07-practical-future-option.html
case class FutureO[+A](future: Future[Option[A]]) extends AnyVal {
def flatMap[B](f: A => FutureO[B])(implicit ec: ExecutionContext): FutureO[B] = {
FutureO {
future.flatMap { optA =>
optA.map { a =>
f(a).future
} getOrElse Future.successful(None)
}
}
}
def map[B](f: A => B)(implicit ec: ExecutionContext): FutureO[B] = {
FutureO(future.map(_ map f))
}
}
// ========== USAGE OF FutureO BELOW ============= \\
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
object TeamDB {
val basketballTeam = Team(id = 111, player_ids = Set(111, 222))
val baseballTeam = Team(id = 222, player_ids = Set(333))
def findById(teamId: Int): Future[Option[Team]] = Future.successful(
teamId match {
case 111 => Some(basketballTeam)
case 222 => Some(baseballTeam)
case _ => None
}
)
}
object PlayerDB {
val basketballPlayer1 = Player(id = 111, jerseyNumber = 23)
val basketballPlayer2 = Player(id = 222, jerseyNumber = 45)
val baseballPlayer = Player(id = 333, jerseyNumber = 5)
def findById(playerId: Int): Future[Option[Player]] = Future.successful(
playerId match {
case 111 => Some(basketballPlayer1)
case 222 => Some(basketballPlayer2)
case 333 => Some(baseballPlayer)
case _ => None
}
)
}
object UserDB {
// user1 is on BOTH the baseball and basketball team
val user1 = User(id = 111, name = "Michael Jordan", player_ids = Set(111, 333), team_ids = Set(111, 222))
// user2 is ONLY on the basketball team
val user2 = User(id = 222, name = "David Wright", player_ids = Set(222), team_ids = Set(111))
def findById(userId: Long): Future[Option[User]] = Future.successful(
userId match {
case 111 => Some(user1)
case 222 => Some(user2)
case _ => None
}
)
}
case class User(id: Int, name: String, player_ids: Set[Int], team_ids: Set[Int])
case class Player(id: Int, jerseyNumber: Int)
case class Team(id: Int, player_ids: Set[Int])
case class UserContext(user: User, teams: Set[Team], players: Set[Player])
object FutureOptionListTest extends App {
val result = for {
user <- FutureO(UserDB.findById(userId = 111))
} yield for {
players: Set[Option[Player]] <- Future.traverse(user.player_ids)(x => PlayerDB.findById(x))
teams: Set[Option[Team]] <- Future.traverse(user.team_ids)(x => TeamDB.findById(x))
} yield {
UserContext(user, teams.flatten, players.flatten)
}
result.future // returns Future[Option[Future[UserContext]]] but I just want Future[UserContext] or UserContext
}

You have created FutureO which combines the effects of Future and Option (if you are looking into Scalaz this compares with OptionT[Future, ?]).
Remembering that for ... yield is analogous to FutureO.map, the result type will always be FutureO[?] (and Future[Option[?]] if you do result.future).
The problem is you want to return a Future[UserContex] instead of a Future[Option[UserContext]]. Essentially you want to loose the Option context, so somewhere you need to explicitly handle if the user exists or not.
A possible solution in this case could be to leave out the FutureO since you are only using it once.
case class NoUserFoundException(id: Long) extends Exception
// for comprehension with Future
val result = for {
user <- UserDB.findById(userId = 111) flatMap (
// handle Option (Future[Option[User]] => Future[User])
_.map(user => Future.successful(user))
.getOrElse(Future.failed(NoUserFoundException(111)))
)
players <- Future.traverse(user.player_ids)(x => PlayerDB.findById(x))
teams <- Future.traverse(user.team_ids)(x => TeamDB.findById(x))
} yield UserContext(user, teams.flatten, players.flatten)
// result: scala.concurrent.Future[UserContext]
If you had multiple functions returning a Future[Option[?]], you probably would like to use FutureO, in this case you could create an extra function Future[A] => FutureO[A], so you can use your functions in the same for comprehension (all in the FutureO monad):
def liftFO[A](fut: Future[A]) = FutureO(fut.map(Some(_)))
// for comprehension with FutureO
val futureO = for {
user <- FutureO(UserDB.findById(userId = 111))
players <- liftFO(Future.traverse(user.player_ids)(x => PlayerDB.findById(x)))
teams <- liftFO(Future.traverse(user.team_ids)(x => TeamDB.findById(x)))
} yield UserContext(user, teams.flatten, players.flatten)
// futureO: FutureO[UserContext]
val result = futureO.future flatMap (
// handle Option (Future[Option[UserContext]] => Future[UserContext])
_.map(user => Future.successful(user))
.getOrElse(Future.failed(new RuntimeException("Could not find UserContext")))
)
// result: scala.concurrent.Future[UserContext]
But as you can see, you will always need to handle the "option context" before you can return a Future[UserContext].

To expand on Peter Neyens' answer, often I'll put a bunch of monad -> monad transformations in a special implicit class and import them as I need them. Here we have two monads, Option[T] and Future[T]. In this case, you are treating None as being a failed Future. You could probably do this:
package foo {
class OptionOps[T](in: Option[T]) {
def toFuture: Future[T] = in match {
case Some(t) => Future.successful(t)
case None => Future.failed(new Exception("option was none"))
}
}
implicit def optionOps[T](in: Option[T]) = new OptionOps[T](in)
}
Then you just import it import foo.optionOps
And then:
val a: Future[Any] = ...
val b: Option[Any] = Some("hi")
for {
aFuture <- a
bFuture <- b.toFuture
} yield bFuture // yields a successful future containing "hi"

Related

Scala futures using Future.sequence but having issues with returning a tuple

I am having some trouble trying to return a tuple when I am using Future.sequence below:
type UserId = Int
type GameId = Int
type Score = Double
case class Game(id: GameId)
def findGame(id: GameId): Future[Game] = ???
def findUserGames(id: UserId): Future[Seq[(GameId, Score)]] = ???
def findGames(id: UserId): Future[Seq[(Game, Score)]] = {
val userGames = findUserGames(id)
// ******* I am stuck here
val games: Future[Seq[Game]] = userGames.flatMap(x => Future.sequence(x.map(y => findGame(y._1))))
}
How can I return a tuple of (Game, Score) when the Future.sequence call is just returning me a Seq[Game]?
Also, if we didn't have Future.sequence, how would we be able to mimic what it does?
i.e. converting a List[Future[Game]] into a Future[List[Game]] ?
You can try this:
def findGames(id: UserId): Future[Seq[(Game, Score)]] = {
findUserGames(id).flatMap(gameIdToScoreSeq =>
Future.traverse(gameIdToScoreSeq)(gameIdToScore =>
findGame(gameIdToScore._1).map((_, gameIdToScore._2))
)
)
}
just by keeping track of what is what by name, as a oneliner, you can do
def findGames(id: UserId)(implicit ec: ExecutionContext): Future[Seq[(Game, Score)]] =
findUserGames(id).flatMap(games =>
Future.sequence(games.map { case (gameid, score) =>
findGame(gameid).map(game => game -> score)
})
)
You just need one more map:
val games: Future[Seq[(Game, Score)]] = userGames.flatMap(x => Future.sequence(x.map(y => findGame(y._1).map(z => (z, y._2)))))
As you can see it's getting a little bit convoluted. You could make your code more readable (arguably) by using for-comprehensions and pattern matching:
val games2: Future[Seq[(Game, Score)]] = for {
games <- userGames
result <- Future.sequence(games.map {
case (gameId, score) =>
for {
game <- findGame(gameId)
} yield (game, score)
})
} yield result

Convert match expressions so I can combine them using combinators like orElse

I currently have something that looks like:
Class UserLocation {
def handleUser1(user: User): Unit = user.location match {
case ... => ..
case ... => ..
case _ => ..
}
}
Class UserLocation2 {
def handleUser2(user: User): Unit = user.location match {
case ... => ..
case ... => ..
case _ => ..
}
}
Class UserLocation3 {
def handleUser3(user: User): Unit = user.location match {
case ... => ..
case ... => ..
case _ => ..
}
}
How can I convert the above to a partial function so I can do something like:
def handleUser(user: User): Unit = handleUser1(user) orElse handleUser2(user) orElse handleUser3(user)
There are a few problems with the your layout and your goal. For one thing, all the handleUser methods are hidden in their own class so the goal, as stated, is impossible because they aren't all in scope at the same time. They might be pulled into scope by making the classes implicit, but for that each class needs a constructor parameter.
So here's one possible solution achieved by dropping all the individual classes.
case class User(location: String)
val handleUser1: PartialFunction[User,Unit] = {
case User("Bern") => println("there")
case User("NYC") => println("here")
}
val handleUser2: PartialFunction[User,Unit] = {
case User("Spain") => println("there")
case User("USA") => println("here")
}
val handleUser3: PartialFunction[User,Unit] = {
case User("moon") => println("far")
case User("earth") => println("near")
case User(_) => println("unknown") // the only default
}
val handleUser = handleUser1 orElse handleUser2 orElse handleUser3
handleUser(User("Bern")) // "there"
handleUser(User("moon")) // "far"
handleUser(User("Boon")) // "unknown"
You can define the functions as PartialFunctions and then compose them using orElse:
val f :PartialFunction[Int, Int] = {
case 1 => 1
}
val f2 :PartialFunction[Int, Int] = {
case 2 => 2
}
val f3 :PartialFunction[Int, Int] = {
case 3 => 3
}
def g = f orElse f2 orElse f3
g(1) // 1
g(2) // 2
g(3) // 3
g(4) // scala.MatchError
P.S
#Suma - Thanks for the input.

Stacking Free Monads

I'm learning about the Free monads, and I've put together a simple example in Scala where I use them to define two domain specific languages.
The first monad deals with the side effects of a repository. I have implemented an interpreter that uses the state monad to manage the state, but in a real program I'd use a database.
The second monad deals with IO.
import cats.data.State
import cats.{Id, ~>}
import cats.free.Free
import cats.free.Free.liftF
final case class Todo(title: String, body: String)
def represent(todo: Todo) = s"${todo.title}: ${todo.body}"
sealed trait CRUDActionA[T]
final case class Find(key: String) extends CRUDActionA[Option[Todo]]
final case class Add(data: Todo) extends CRUDActionA[Unit]
type CRUDAction[T] = Free[CRUDActionA, T]
def find(key: String): CRUDAction[Option[Todo]] = liftF[CRUDActionA, Option[Todo]](Find(key))
def add(data: Todo): CRUDAction[Unit] = liftF[CRUDActionA, Unit](Add(data))
type TodosState[A] = State[List[Todo], A]
val repository: CRUDActionA ~> TodosState = new (CRUDActionA ~> TodosState) {
def apply[T](fa: CRUDActionA[T]): TodosState[T] = fa match {
case Add(todo) => State.modify(todos => todos :+ todo)
case Find(title) => State.inspect(todos => todos find (_.title == title))
}
}
sealed trait IOActionA[T]
final case class Out(str: String) extends IOActionA[Unit]
type IOAction[T] = Free[IOActionA, T]
def out(str: String): IOAction[Unit] = liftF[IOActionA, Unit](Out(str))
val io: IOActionA ~> Id = new (IOActionA ~> Id) {
override def apply[A](fa: IOActionA[A]): Id[A] = fa match {
case Out(todo) => println(todo)
}
}
Then, I can put together these two "programs"
def addNewTodo: Free[CRUDActionA, Option[Todo]] = for {
_ <- add(Todo(title = "Must do", body = "Must do something"))
todo <- find("Must do")
} yield todo
def outProgram(todo: Todo): IOAction[Unit] = for {
_ <- out(represent(todo))
} yield ()
And run them doing
val (_, mayBeTodo) = (addNewTodo foldMap repository run List()).value
outProgram(mayBeTodo.get).foldMap(io)
I understand this is far from ideal, and I'd like to write a program as and an interpreter that supports:
def fullProgram = for {
_ <- add(Todo(title = "Must do", body = "Must do something"))
todo <- find("Must do") // This is an option!!!
_ <- out(represent(todo)) // But represent expects a Todo
} yield ()
So the questions are:
How can I stack the two monads together into a "fullProgram"
How can I compose the two interpreters into a new interpreter?
How do I deal with the Option[Todo] returned by find, and then passed to
represent
Answer to questions 1 & 2:
type TodoApp[A] = Coproduct[IOActionA, CRUDActionA, A]
class CRUDActions[F[_]](implicit I: Inject[CRUDActionA, F]) {
def find(key: String): Free[F, Option[Todo]] = Free.inject[CRUDActionA, F](Find(key))
def add(data: Todo): Free[F, Unit] = Free.inject[CRUDActionA, F](Add(data))
}
object CRUDActions {
implicit def crudActions[F[_]](implicit I: Inject[CRUDActionA, F]): CRUDActions[F] = new CRUDActions[F]
}
class IOActions[F[_]](implicit I: Inject[IOActionA, F]) {
def out(str: String): Free[F, Unit] = Free.inject[IOActionA, F](Out(str))
}
object IOActions {
implicit def ioActions[F[_]](implicit I: Inject[IOActionA, F]): IOActions[F] = new IOActions[F]
}
def fullProgram(implicit C : CRUDActions[TodoApp], I : IOActions[TodoApp]): Free[TodoApp, Unit] = {
for {
_ <- C.add(Todo(title = "Must do", body = "Must do something"))
todo <- C.find("Must do")
_ <- I.out(represent(todo.get))
} yield ()
}
object ConsoleCatsInterpreter extends (IOActionA ~> Id) {
def apply[A](i: IOActionA[A]) = i match {
case Out(prompt) => println(prompt).asInstanceOf[A]
}
}
object MutableListCrudInterpreter extends (CRUDActionA ~> Id) {
val data = new ListBuffer[Todo]
override def apply[A](fa: CRUDActionA[A]): Id[A] = fa match {
case Add(todo) => data.append(todo).asInstanceOf[A]
case Find(title) => data.find( _.title == title).asInstanceOf[A]
}
}
val interpreter: TodoApp ~> Id = ConsoleCatsInterpreter or MutableListCrudInterpreter
fullProgram.foldMap(interpreter)

Slick 3.0 many-to-many query with the join as an iterable

I've created a many-to-many collection using Slick 3.0, but I'm struggling to retrieve data in the way I want.
There is a many-to-many relationship between Events and Interests. Here are my tables:
case class EventDao(title: String,
id: Option[Int] = None)
class EventsTable(tag: Tag)
extends Table[EventDao](tag, "events") {
def id = column[Int]("event_id", O.PrimaryKey, O.AutoInc)
def title = column[String]("title")
def * = (
title,
id.?) <> (EventDao.tupled, EventDao.unapply)
def interests = EventInterestQueries.query.filter(_.eventId === id)
.flatMap(_.interestFk)
}
object EventQueries {
lazy val query = TableQuery[EventsTable]
val findById = Compiled { k: Rep[Int] =>
query.filter(_.id === k)
}
}
Here's EventsInterests:
case class EventInterestDao(event: Int, interest: Int)
class EventsInterestsTable(tag: Tag)
extends Table[EventInterestDao](tag, "events_interests") {
def eventId = column[Int]("event_id")
def interestId = column[Int]("interest_id")
def * = (
eventId,
interestId) <> (EventInterestDao.tupled, EventInterestDao.unapply)
def eventFk = foreignKey("event_fk", eventId, EventQueries.query)(e => e.id)
def interestFk = foreignKey("interest_fk", interestId, InterestQueries.query)(i => i.id)
}
object EventInterestQueries {
lazy val query = TableQuery[EventsInterestsTable]
}
And finally Interests:
case class InterestDao(name: String,
id: Option[Int] = None)
class InterestsTable(tag: Tag)
extends Table[InterestDao](tag, "interests") {
def id = column[Int]("interest_id", O.PrimaryKey, O.AutoInc)
def name = column[String]("name")
def name_idx = index("idx_name", name, unique = true)
def * = (
name,
id.?) <> (InterestDao.tupled, InterestDao.unapply)
def events = EventInterestQueries.query.filter(_.interestId === id)
.flatMap(_.eventFk)
}
object InterestQueries {
lazy val query = TableQuery[InterestsTable]
val findById = Compiled { k: Rep[Int] =>
query.filter(_.id === k)
}
}
I can query and retrieve tuples of (event.name, interest) with the following:
val eventInterestQuery = for {
event <- EventQueries.query
interest <- event.interests
} yield (event.title, interest.name)
Await.result(db.run(eventInterestQuery.result).map(println), Duration.Inf)
So this is what I currently have.
What I want is to be able to populate a case class like:
case class EventDao(title: String,
interests: Seq[InterestDao],
id: Option[Int] = None)
The trouble is that if I update my case class like this, it messes up my def * projection in EventsTable. Also, I'll have to rename the EventsTable.interests filter to something like EventsTable.interestIds which is a bit ugly but I could live with if necessary.
Also, I can't find a way of writing a for query that yields (event.name, Seq(interest.name)). Anyway, that's just a stepping stone to me being able to yield a (EventDao, Seq(InterestDao)) tuple which is what I really want to return.
Does anyone know how I can achieve these things? I also want to be able to 'take' a certain number of Interests, so for some queries all would be returned, but for others only the first 3 would be.
So after reading this page and chatting on the mailing list, I finally got it working:
val eventInterestQuery = for {
event <- EventQueries.query
interest <- event.interests
} yield (event, interest)
Await.result(db.run(eventInterestQuery.result
// convert the interests to a sequence.
.map {
_.groupBy(_._1)
.map {
case (k,v) => (k, v.map(_._2))
}.toSeq
}
), Duration.Inf)
The only issue with groupBy is you lose order. You could fold the result. I've written this helper for my current project:
def foldOneToMany[A, B](in: Seq[(A, Option[B])], eq: (A, B) => Boolean)
(f: (A, B) => A): Seq[A] =
in.foldLeft(List.empty[A]) {
case (head :: tail, (_, Some(rel))) if eq(head, rel) =>
f(head, rel) :: tail
case (r, (el, Some(rel))) => f(el, rel) :: r
case (r, (el, None)) => el :: r
}.reverse
It could do with some love. Now it takes in a function A,B => Boolean to determine if B belongs to A and a function A,B => A that adds B to A.
Virtualeyes also has a point. In Postgres you could use array_agg function to use a little less bandwidth from the db.

How to check if there's None in List[Option[_]] and return the element's name?

I have multiple Option's. I want to check if they hold a value. If an Option is None, I want to reply to user about this. Else proceed.
This is what I have done:
val name:Option[String]
val email:Option[String]
val pass:Option[String]
val i = List(name,email,pass).find(x => x match{
case None => true
case _ => false
})
i match{
case Some(x) => Ok("Bad Request")
case None => {
//move forward
}
}
Above I can replace find with contains, but this is a very dirty way. How can I make it elegant and monadic?
Edit: I would also like to know what element was None.
Another way is as a for-comprehension:
val outcome = for {
nm <- name
em <- email
pwd <- pass
result = doSomething(nm, em, pwd) // where def doSomething(name: String, email: String, password: String): ResultType = ???
} yield (result)
This will generate outcome as a Some(result), which you can interrogate in various ways (all the methods available to the collections classes: map, filter, foreach, etc.). Eg:
outcome.map(Ok(result)).orElse(Ok("Bad Request"))
val ok = Seq(name, email, pass).forall(_.isDefined)
If you want to reuse the code, you can do
def allFieldValueProvided(fields: Option[_]*): Boolean = fields.forall(_.isDefined)
If you want to know all the missing values then you can find all missing values and if there is none, then you are good to go.
def findMissingValues(v: (String, Option[_])*) = v.collect {
case (name, None) => name
}
val missingValues = findMissingValues(("name1", option1), ("name2", option2), ...)
if(missingValues.isEmpty) {
Ok(...)
} else {
BadRequest("Missing values for " + missingValues.mkString(", ")))
}
val response = for {
n <- name
e <- email
p <- pass
} yield {
/* do something with n, e, p */
}
response getOrElse { /* bad request /* }
Or, with Scalaz:
val response = (name |#| email |#| pass) { (n, e, p) =>
/* do something with n, e, p */
}
response getOrElse { /* bad request /* }
if ((name :: email :: pass :: Nil) forall(!_.isEmpty)) {
} else {
// bad request
}
I think the most straightforward way would be this:
(name,email,pass) match {
case ((Some(name), Some(email), Some(pass)) => // proceed
case _ => // Bad request
}
A version with stone knives and bear skins:
import util._
object Test extends App {
val zero: Either[List[Int], Tuple3[String,String,String]] = Right((null,null,null))
def verify(fields: List[Option[String]]) = {
(zero /: fields.zipWithIndex) { (acc, v) => v match {
case (Some(s), i) => acc match {
case Left(_) => acc
case Right(t) =>
val u = i match {
case 0 => t copy (_1 = s)
case 1 => t copy (_2 = s)
case 2 => t copy (_3 = s)
}
Right(u)
}
case (None, i) =>
val fails = acc match {
case Left(f) => f
case Right(_) => Nil
}
Left(i :: fails)
}
}
}
def consume(name: String, email: String, pass: String) = Console println s"$name/$email/$pass"
def fail(is: List[Int]) = is map List("name","email","pass") foreach (Console println "Missing: " + _)
val name:Option[String] = Some("Bob")
val email:Option[String]= None
val pass:Option[String] = Some("boB")
val res = verify(List(name,email,pass))
res.fold(fail, (consume _).tupled)
val res2 = verify(List(name, Some("bob#bob.org"),pass))
res2.fold(fail, (consume _).tupled)
}
The same thing, using reflection to generalize the tuple copy.
The downside is that you must tell it what tuple to expect back. In this form, reflection is like one of those Stone Age advances that were so magical they trended on twitter for ten thousand years.
def verify[A <: Product](fields: List[Option[String]]) = {
import scala.reflect.runtime._
import universe._
val MaxTupleArity = 22
def tuple = {
require (fields.length <= MaxTupleArity)
val n = fields.length
val tupleN = typeOf[Tuple2[_,_]].typeSymbol.owner.typeSignature member TypeName(s"Tuple$n")
val init = tupleN.typeSignature member nme.CONSTRUCTOR
val ctor = currentMirror reflectClass tupleN.asClass reflectConstructor init.asMethod
val vs = Seq.fill(n)(null.asInstanceOf[String])
ctor(vs: _*).asInstanceOf[Product]
}
def zero: Either[List[Int], Product] = Right(tuple)
def nextProduct(p: Product, i: Int, s: String) = {
val im = currentMirror reflect p
val ts = im.symbol.typeSignature
val copy = (ts member TermName("copy")).asMethod
val args = copy.paramss.flatten map { x =>
val name = TermName(s"_$i")
if (x.name == name) s
else (im reflectMethod (ts member x.name).asMethod)()
}
(im reflectMethod copy)(args: _*).asInstanceOf[Product]
}
(zero /: fields.zipWithIndex) { (acc, v) => v match {
case (Some(s), i) => acc match {
case Left(_) => acc
case Right(t) => Right(nextProduct(t, i + 1, s))
}
case (None, i) =>
val fails = acc match {
case Left(f) => f
case Right(_) => Nil
}
Left(i :: fails)
}
}.asInstanceOf[Either[List[Int], A]]
}
def consume(name: String, email: String, pass: String) = Console println s"$name/$email/$pass"
def fail(is: List[Int]) = is map List("name","email","pass") foreach (Console println "Missing: " + _)
val name:Option[String] = Some("Bob")
val email:Option[String]= None
val pass:Option[String] = Some("boB")
type T3 = Tuple3[String,String,String]
val res = verify[T3](List(name,email,pass))
res.fold(fail, (consume _).tupled)
val res2 = verify[T3](List(name, Some("bob#bob.org"),pass))
res2.fold(fail, (consume _).tupled)
I know this doesn't scale well, but would this suffice?
(name, email, pass) match {
case (None, _, _) => "name"
case (_, None, _) => "email"
case (_, _, None) => "pass"
case _ => "Nothing to see here"
}