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.
Related
In Scala we have a by-name-parameters where we can write
def foo[T](f: => T):T = {
f // invokes f
}
// use as:
foo(println("hello"))
I now want to do the same with an array of methods, that is I want to use them as:
def foo[T](f:Array[ => T]):T = { // does not work
f(0) // invokes f(0) // does not work
}
foo(println("hi"), println("hello")) // does not work
Is there any way to do what I want? The best I have come up with is:
def foo[T](f:() => T *):T = {
f(0)() // invokes f(0)
}
// use as:
foo(() => println("hi"), () => println("hello"))
or
def foo[T](f:Array[() => T]):T = {
f(0)() // invokes f(0)
}
// use as:
foo(Array(() => println("hi"), () => println("hello")))
EDIT: The proposed SIP-24 is not very useful as pointed out by Seth Tisue in a comment to this answer.
An example where this will be problematic is the following code of a utility function trycatch:
type unitToT[T] = ()=>T
def trycatch[T](list:unitToT[T] *):T = list.size match {
case i if i > 1 =>
try list.head()
catch { case t:Any => trycatch(list.tail: _*) }
case 1 => list(0)()
case _ => throw new Exception("call list must be non-empty")
}
Here trycatch takes a list of methods of type ()=>T and applies each element successively until it succeeds or the end is reached.
Now suppose I have two methods:
def getYahooRate(currencyA:String, currencyB:String):Double = ???
and
def getGoogleRate(currencyA:String, currencyB:String):Double = ???
that convert one unit of currencyA to currencyB and output Double.
I use trycatch as:
val usdEuroRate = trycatch(() => getYahooRate("USD", "EUR"),
() => getGoogleRate("USD", "EUR"))
I would have preferred:
val usdEuroRate = trycatch(getYahooRate("USD", "EUR"),
getGoogleRate("USD", "EUR")) // does not work
In the example above, I would like getGoogleRate("USD", "EUR") to be invoked only if getYahooRate("USD", "EUR") throws an exception. This is not the intended behavior of SIP-24.
Here is a solution, although with a few restrictions compared to direct call-by-name:
import scala.util.control.NonFatal
object Main extends App {
implicit class Attempt[+A](f: => A) {
def apply(): A = f
}
def tryCatch[T](attempts: Attempt[T]*): T = attempts.toList match {
case a :: b :: rest =>
try a()
catch {
case NonFatal(e) =>
tryCatch(b :: rest: _*)
}
case a :: Nil =>
a()
case Nil => throw new Exception("call list must be non-empty")
}
def a = println("Hi")
def b: Unit = sys.error("one")
def c = println("bye")
tryCatch(a, b, c)
def d: Int = sys.error("two")
def e = { println("here"); 45 }
def f = println("not here")
val result = tryCatch(d, e, f)
println("Result is " + result)
}
The restrictions are:
Using a block as an argument won't work; only the last expression of the block will be wrapped in an Attempt.
If the expression is of type Nothing (e.g., if b and d weren't annotated), the conversion to Attempt is not inserted since Nothing is a subtype of every type, including Attempt. Presumably the same would apply for an expression of type Null.
As of Scala 2.11.7, the answer is no. However, there is SIP-24, so in some future version your f: => T* version may be possible.
Can someone help me convert this to a flatMap or for-comprehension? I know how to handle more trivial scenarios of nested option.
case class Person(name: String, signficantOther: Option[String])
val nightclubPeoples : Option[Seq[Person]] = ???
def significantOthers(nightClubPeoples : Option[Seq[Person]]) : List[String] = {
nightclubPeoples match {
case Some(x) => x map { y : Person =>
y.significantOther match {
case Some(z) => z
case None => "No Signficant Other"
}
}.toList
case None => Nil
}
}
I think I would use a fold:
nightclubPeoples.fold[List[String]](Nil)(_.flatMap(_.signficantOther)(collection.breakOut))
Sascha Kolberg's answer is a, as I think, better solution(just omit the case when signficantOther is empty, i.e "No Signficant Other" will not in the result List) for your question, however since
you demand "convert this to a flatMap or for-comprehension", so the code is as follows.
case class Person(name: String, signficantOther: Option[String])
val nightClubPeopleOpt : Option[Seq[Person]] = Some(Person("name", Some("1")) :: Person("name1", None) :: Nil)
def significantOthers(nightClubPeopleOpt : Option[Seq[Person]]) = {
for {
nightClubPeople <- nightClubPeopleOpt
} yield {
// scenario 1
// if signficantOther is empty, then it should be ignored
// Answer -> List(1)
nightClubPeople.flatMap(_.signficantOther).toList
// scenario 2
// if signficantOther is empty, then we use the default "No Signficant Other"
// Answer -> List(1, No Signficant Other)
nightClubPeople.map { nightClubPerson =>
nightClubPerson.signficantOther.getOrElse("No Signficant Other")
}.toList
}
}.getOrElse(Nil)
println(significantOthers(nightClubPeopleOpt))
Here you go:
nightclubPeoples.toSeq.flatMap(_.map(_.signficantOther.getOrElse("No Signficant Other")))
A relatively readable direct transformation would be:
def significantOthers(nightClubPeoples : Option[Seq[Person]]): List[String] = {
for {
nightClubPeoples <- nightClubPeoples.toList
person <- nightClubPeoples
} yield person.significantOther.getOrElse("No Significant Other")
}
The thing to note here is that we convert nightClubPeoples to a list up front, allowing us to use a for comprehension all along.
However, I doubt you really want to have "No Significant Other" appear in your list when signficantOther is None. Rather, it should be ignored, which would give the even simpler:
def significantOthers(nightClubPeoples : Option[Seq[Person]]): List[String] = {
for {
nightClubPeoples <- nightClubPeoples.toList
person <- nightClubPeoples
significantOther <- person.significantOther
} yield significantOther
}
I wonder how I can transform a List[Future[String\/String]] to Future[String\/String].
In fact, I just want to apply for each element of the list EitherT.apply.
What I want looks like this :
val result: EitherT[Future, String, String] =
for{
_ <- EitherT.apply(fun(arg1))
_ <- EitherT.apply(fun(arg2))
res <- EitherT.apply(fun(arg3))
} yield res
result.run
// ...
def fun(arg1: MyType): Future[String\/String] = ...
In this example, I would like to set as input the function fun and a list (of n elements) of arguments for the function fun (here : List(arg1, arg2, arg3)).
But I don't know how to manage with Futures.
Is it possible to keep the asynchrony ?
I have found a way to do it but without using EitherT.apply.
def seqCheck(elements: List[Element]): Future[String\/String] = elements match {
case element :: tail =>
fun(element) flatMap {
case \/-(_) =>
seqCheck(tail)
case -\/(e) => Future{
-\/(e)
}
}
case Nil =>
Future {
\/-("Ok")
}
}
It not as nice as I would like it to be but it seems to work.
If somebody knows a best way...
Given this RichFile class and its companion object:
class RichFile(filePath: String) {
val file = new File(filePath)
}
object RichFile {
def apply(filePath: String) = new RichFile(filePath)
def unapply(rf: RichFile) = {
if (rf == null || rf.file.getAbsolutePath().length() == 0)
None
else {
val basename = rf.file.getName()
val dirname = rf.file.getParent()
val ei = basename.indexOf(".")
if (ei >= 0) {
Some((dirname, basename.substring(0, ei), basename.substring(ei + 1)))
} else {
Some((dirname, basename, ""))
}
}
}
def unapplySeq(rf: RichFile): Option[Seq[String]] = {
val filePath = rf.file.getAbsolutePath()
if (filePath.length() == 0)
None
else
Some(filePath.split("/"))
}
}
Basically I want to extract all the components of a file path as a sequence. Why does wild-card match doesn't work in the following code? Specifically the first case statement I am getting the error star patterns must correspond with varargs parameters.
val l = List(
RichFile("/abc/def/name.txt"),
RichFile("/home/cay/name.txt"),
RichFile("/a/b/c/d/e"))
l.foreach { f =>
f match {
case RichFile(_*) => println((x, y))
case RichFile(a, b, c) => println((a, b, c))
}
}
I also want to match them just like we match Lists in Scala, something like this:
l.foreach { f =>
f match {
case a::b::"def"::tail => println((a, tail))
case RichFile(_*) => println((x, y))
case RichFile(a, b, c) => println((a, b, c))
}
}
How can I do that using unapplySeq?
It looks like the issue is just that you have both unapply and unapplySeq, so when you have just one argument in the case RichFile, scala is confused about which kind of unapply you are trying to do. The way to resolve this would be to have two objects, one with unapply and one with unapplySeq, so that the usage is unambiguous.
class RichFile(filePath: String) {
val file = new File(filePath)
override def toString = f"RichFile($filePath)"
}
object RichFile {
def apply(filePath: String) = new RichFile(filePath)
def unapply(rf: RichFile) = {
if (rf == null || rf.file.getAbsolutePath.isEmpty) None
else {
val basename = rf.file.getName
val dirname = rf.file.getParent
val (name, ext) = basename.split("\\.", 2) match {
case Array(name, ext) => (name, ext)
case Array(name) => (name, "")
}
Some((dirname, name, ext))
}
}
}
object RichFilePath {
def unapplySeq(rf: RichFile): Option[Seq[String]] = {
val filePath = rf.file.getAbsolutePath()
if (filePath.isEmpty) None
else Some(filePath.split("/"))
}
}
val l = List(
RichFile("/abc/def/name.txt"),
RichFile("/home/cay/name.txt"),
RichFile("/a/b/c/d/e"),
RichFile("/y/z"))
l.foreach { f =>
f match {
case RichFilePath(a, b, c) => println("RichFilePath -> " + (a, b, c))
case RichFile(f) => println("RichFile -> " + f)
}
}
prints:
RichFile -> (/abc/def,name,txt)
RichFile -> (/home/cay,name,txt)
RichFile -> (/a/b/c/d,e,)
RichFilePath -> (,y,z)
Regarding the :: syntax, you can't use :: because it's already defined and only works on Lists. Further, you wouldn't want :: because operators ending in : are right-associative. This makes sense for List matching because the item is on the left and the rest is on the right. For your application, I assume that you want matching to read the same way as the directory structure: with the filename on the right and the "rest" on the left. Thus, you can def define your own operator for this:
object --> {
def unapply(rf: RichFile): Option[(RichFile, String)] = {
if (rf.file.getAbsolutePath.isEmpty)
None
else {
val f = rf.file.getAbsoluteFile
Some((RichFile(f.getParent), f.getName))
}
}
}
l.foreach { f =>
f match {
case a --> b --> c => println(f"$a\t$b\t$c")
}
}
prints
RichFile(/abc) def name.txt
RichFile(/home) cay name.txt
RichFile(/a/b/c) d e
RichFile(/) y z
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"
}