Possible ways to write for comprehension to handle multiple options - scala

I have a function defined which return Option .
The below code works fine
def func :Option[Result]= {
(for {
src <- ele2
id <- ele
} yield {
src match {
case "test2" => Option(id.value).map(_.take(3))
.filterNot(_ == "de").map(id => Result(id))
case _ => None
}
}).getOrElse(None)
}
If I remove getOrElse(None) I get compile error that Iterable[Option[Result]] does not match expected type Option[Result]
I want to get rid of getOrElse(None).Is there a possible way to write the code

How about:
val res: Option[Result] = for {
src <- ele2
if src == "test2"
id <- ele
} yield Result(id.value)

For comprehensions in Scala is just syntactic sugar for flatMap and map. In some situations it is better to rewrite your for comprehensions using flatMap and map.
def func: Option[Result] = {
ele2.flatMap { src =>
ele.flatMap { id =>
src match {
case "test2" => Option(id.value).map(_.take(3))
.filterNot(_ == "de").map(id => Result(id))
case _ => None
}
}
}
}
The other option is just extract logic from yield to function
def func: Option[Result] = {
def innerLogic(src: String, id: Demo): Option[Result] = {
src match {
case "test2" => Option(id.value).map(_.take(3))
.filterNot(_ == "de").map(id => Result(id))
case _ => None
}
}
for {
src <- ele2
id <- ele
res <- innerLogic(src, id)
} yield {
res
}
}

Related

How to convert List[zio.Task[List[A]]] to zio.Task[[List[A]]

I need to process a set of Ids and return the result as zio.Task[List[RelevantReadingRow]]
def getBaselinesForRequestIds(baseLineReqIds: Set[String]): Task[List[RelevantReadingRow]] =
dynamoConnection
.run(
table.getAll("baseline_req_id" in baseLineReqIds)
)
.flatMap(_.toList.separate match {
case (err :: _, _) => ZIO.fail(new Throwable(describe(err)))
case (Nil, relevantReadings) => ZIO.succeed(relevantReadings)
})
The code above works but I need to process them in batches of 25 element as mutch.
I have try this but then I get a List of zio.Task
def getBaselinesForRequestIds(baseLineReqIds: Set[String]): Task[List[RelevantReadingRow]] = {
val subSet = baseLineReqIds.grouped(25).toList
val res = for {
rows <- subSet.map(reqIds => dynamoConnection
.run(
table.getAll("baseline_req_id" in reqIds)
).flatMap(e => e.toList.separate match {
case (err :: _, _) => ZIO.fail(new Throwable(describe(err)))
case (Nil, relevantReadings) => ZIO.succeed(relevantReadings)
}))
} yield rows
res // this is List[zio.Task[List[RelevantReadingRow]]]
}
I dont know how to convert back to a zio.Task[List[RelevantReadingRow]]
any sugestion?
You can use ZIO.collectAll to convert List[Task] to Task[List], I thought it was ZIO.sequence..., maybe I'm getting confused with cats...
Following example works with Zio2
package sample
import zio._
object App extends ZIOAppDefault {
case class Result(value: Int) extends AnyVal
val data: List[Task[List[Result]]] = List(
Task { List(Result(1), Result(2)) },
Task { List(Result(3)) }
)
val flattenValues: Task[List[Result]] = for {
values <- ZIO.collectAll { data }
} yield values.flatten
val app = for {
values <- flattenValues
_ <- Console.putStrLn { values }
} yield()
def run = app
}
In particular for your sample ...
and assuming that 'separate' it's just an extension method to collect some errors (returns a tuple of error list and result list), and ignoring the method 'describe' to turn an err into a throwable
https://scastie.scala-lang.org/jgoday/XKxVP2ECSFOv4chgSFCckg/7
package sample
import zio._
object App extends ZIOAppDefault {
class DynamoMock {
def run: Task[List[RelevantReadingRow]] = Task {
List(
RelevantReadingRow(1),
RelevantReadingRow(2),
)
}
}
case class RelevantReadingRow(value: Int) extends AnyVal
implicit class ListSeparate(list: List[RelevantReadingRow]) {
def separate: (List[String], List[RelevantReadingRow]) =
(Nil, list)
}
def getBaselinesForRequestIds(baseLineReqIds: Set[String]): Task[List[RelevantReadingRow]] = {
val dynamoConnection = new DynamoMock()
val subSet = baseLineReqIds.grouped(25).toList
val res: List[Task[List[RelevantReadingRow]]] = for {
rows <- subSet.map(reqIds => dynamoConnection
.run.flatMap(e => e.toList.separate match {
case (err :: _, _) => ZIO.fail(new Throwable(err))
case (Nil, relevantReadings) => ZIO.succeed(relevantReadings)
}))
} yield rows
for {
rows <- ZIO.collectAll(res)
} yield rows.flatten
}
val app = for {
values <- getBaselinesForRequestIds(Set("id1", "id2"))
_ <- Console.putStrLn { values }
} yield()
def run = app
}
So, here is an alternative solution based on #jgoday answer
def getBaselinesForRequestIds(baseLineReqIds: Set[String]): Task[List[RelevantReadingRow]] =
for {
values <- ZIO.foreachPar(baseLineReqIds.grouped(25).toList) {
reqIds =>
dynamoConnection
.run(
table.getAll("baseline_req_id" in reqIds)
).flatMap(e => e.toList.separate match {
case (err :: _, _) => ZIO.fail(new Throwable(describe(err)))
case (Nil, relevantReadings) => ZIO.succeed(relevantReadings)
})
}
} yield values.flatten

composed for comprehension in scala

I have a list of tasks in a for-comprehension:
def main = {
List("en", "es", "de").foreach(c => execAll(c))
}
def execAll(country: String): Future[Unit] = {
for {
_ <- repo.exec1(country)
_ <- repo.exec2(country)
_ <- repo.exec3(country)
_ <- repo.exec4(country)
_ <- repo.exec5(country)
} yield ()
}
The problem now is because I need to execute functions exec4 and exec5 only for "en" and "es" countries.
I tried to add the functions in a list of futures by condition (if country == "en" don't add it)
val all = for {
_ <- repo.exec1(country)
_ <- repo.exec2(country)
_ <- repo.exec3(country)
} yield ()
val enOrEsOnly = for {
_ <- repo.exec4(country)
_ <- repo.exec5(country)
} yield ()
country match {
case "de" => all
case other => all + enOrEsOnly // invalid code
}
Can find a solution using for-comprehension here? Or can just use a list of futures here? I don't need their results. thanks
Or I can just use an if to solve it:
if (country != "de") {
repo.exec4(country)
repo.exec5(country)
}
What about replace execAll by:
def execAll(country: String): Future[Unit] = {
for {
_ <- repo.exec1(country)
_ <- repo.exec2(country)
_ <- repo.exec3(country)
if country != "de"
_ <- repo.exec4(country)
_ <- repo.exec5(country)
} yield ()
}
I would rather add an extra layer over repo that has the validations, that way your code will be the same, and in that extra layer you would do something like this:
final class Domain(repo: Repository) {
def exec4(country: String): Future[Unit] =
country match {
case "en" | "es" => repo.exec4(country)
case _ => Future.unit
}
}

Running more than one Future in Action.async

I tried in this way but it always go to case Nil.
def findByLastName(lastName:String)=Action.async {
val cursor = Json.obj("lastName" -> lastName)
StudentDaoAndEntity.findAllStudent(cursor) flatMap { lastName =>
ExaminationDao.findStudent(cursor) flatMap { lastName =>
LibraryDao.findStudent(cursor) map {
{
case Nil => Ok("Student Not Found")
case l: Seq[JsObject] => Ok(Json.toJson(l))
}
}
}
}
}
And my defined functions in Database are:
In StudentDaoAndEntity:
def findAllStudent(allStd: JsObject): Future[Seq[JsObject]] = {
// gather all the JsObjects in a list
collection.find(allStd).cursor[JsObject].collect[List]()
}
In LibraryDao:
def findStudent(allStd: JsObject): Future[Seq[JsObject]] = {
// gather all the JsObjects in a list
collection.find(allStd).cursor[JsObject].collect[List]()
}
In ExaminationDao:
def findStudent(allStd: JsObject): Future[Seq[JsObject]] = {
// gather all the JsObjects in a list
collection.find(allStd).cursor[JsObject].collect[List]()
}
The issue is with shadowing the lastName argument inside each lambda (as comments already pointed out). That being said, you can use a for-comprehension to make your code more readable
def findByLastName(lastName:String) = Action.async {
val cursor = Json.obj("lastName" -> lastName)
for {
_ <- StudentDaoAndEntity.findAllStudent(cursor)
_ <- ExaminationDao.findStudent(cursor)
students <- LibraryDao.findStudent(cursor)
} yield students match {
case Nil => Ok("Student Not Found")
case l: Seq[JsObject] => Ok(Json.toJson(l))
}
}
This is what I want:
def findByLastName(lastName:String)=Action.async {
request =>
val cursor = Json.obj("lastName" -> lastName)
StudentDaoAndEntity.findAllStudent(cursor) flatMap {
student =>
ExaminationDao.findStudent(cursor) flatMap {
examination =>
LibraryDao.findStudent(cursor) map {
{
case Nil => Ok("Student Not Found")
case library: Seq[JsObject] =>
val finalResult = student ++ examination ++ library
Ok(JsArray(finalResult))
}
}
}
}
}

Using for-comprehension, Try and sequences in Scala

Let's say you've got a bunch of methods:
def foo() : Try[Seq[String]]
def bar(s:String) : Try[String]
and you want to make a for-comprhension:
for {
list <- foo
item <- list
result <- bar(item)
} yield result
of course this won't compile since Seq cannot be used with Try in this context.
Anyone has a nice solution how to write this clean without breaking it into separate two for's?
I've came across this syntax problem for the thirds time and thought that it's about time to ask about this.
IMHO: Try and Seq is more than what you need to define a monad transformer:
Code for library:
case class trySeq[R](run : Try[Seq[R]]) {
def map[B](f : R => B): trySeq[B] = trySeq(run map { _ map f })
def flatMap[B](f : R => trySeq[B]): trySeq[B] = trySeq {
run match {
case Success(s) => sequence(s map f map { _.run }).map { _.flatten }
case Failure(e) => Failure(e)
}
}
def sequence[R](seq : Seq[Try[R]]): Try[Seq[R]] = {
seq match {
case Success(h) :: tail =>
tail.foldLeft(Try(h :: Nil)) {
case (Success(acc), Success(elem)) => Success(elem :: acc)
case (e : Failure[R], _) => e
case (_, Failure(e)) => Failure(e)
}
case Failure(e) :: _ => Failure(e)
case Nil => Try { Nil }
}
}
}
object trySeq {
def withTry[R](run : Seq[R]): trySeq[R] = new trySeq(Try { run })
def withSeq[R](run : Try[R]): trySeq[R] = new trySeq(run map (_ :: Nil))
implicit def toTrySeqT[R](run : Try[Seq[R]]) = trySeq(run)
implicit def fromTrySeqT[R](trySeqT : trySeq[R]) = trySeqT.run
}
and after you can use for-comrehension (just import your library):
def foo : Try[Seq[String]] = Try { List("hello", "world") }
def bar(s : String) : Try[String] = Try { s + "! " }
val x = for {
item1 <- trySeq { foo }
item2 <- trySeq { foo }
result <- trySeq.withSeq { bar(item2) }
} yield item1 + result
println(x.run)
and it works for:
def foo() = Try { List("hello", throw new IllegalArgumentException()) }
// x = Failure(java.lang.IllegalArgumentException)
You can take advantage of the fact that Try can be converted to Option, and Option to Seq:
for {
list <- foo.toOption.toSeq // toSeq needed here, as otherwise Option.flatMap will be used, rather than Seq.flatMap
item <- list
result <- bar(item).toOption // toSeq not needed here (but allowed), as it is implicitly converted
} yield result
This will return a (possibly empty, if the Trys failed) Seq.
If you want to keep all the exception detail, you'll need a Try[Seq[Try[String]]]. This can't be done with a single for comprehension, so you're best sticking with plain map:
foo map {_ map bar}
If you want to mingle your Trys and Seqs in a different way, things get fiddlier, as there's no natural way to flatten a Try[Seq[Try[String]]]. #Yury's answer demonstrates the sort of thing you'd have to do.
Or, if you're only interested in the side effects of your code, you can just do:
for {
list <- foo
item <- list
result <- bar(item)
} result
This works because foreach has a less restrictive type signature.
A Try can be converted to an Option, which you can than use in a for-comprehension. E.g.
scala> def testIt() = {
| val dividend = Try(Console.readLine("Enter an Int that you'd like to divide:\n").toInt)
| dividend.toOption
| }
testIt: ()Option[Int]
scala> for (x <- testIt()) println (x * x)
Enter an Int that you'd like to divide:
scala> for (x <- testIt()) println (x * x)
Enter an Int that you'd like to divide:
1522756
First time I entered "w", then second time 1234.

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"
}