I have to call 2 functions, the first one is returning a \/[String, Int], the other one is returning a Future[Int].
I need to accumulate the validation of this 2 function calls.
Here is my attempt :
import scala.concurrent.Future
import scalaz._, Scalaz._, Validation._
import scala.concurrent.ExecutionContext.Implicits.global
def positive(x: Int): \/[String,Int] = if(x<0) "ko".left else x.right
def inc(x: Int): Future[Int] = if (x >5) Future.successful(x +1) else Future.failed(new Exception("failed"))
def eval(x:Int, y:Int): Future[Validation[String, (Int, Int)]] = for {
v1 <- Future.successful(positive(x).validation)
v2 <- inc(y).map(x => success(x)).recover{case ex: Exception => failure(ex.getMessage)}
} yield ((v1 |#| v2){(_,_)})
) Is there a simpler way to do this, to avoid manual biding of future errors to success/failure?
2) I'd like to use ValidationNel to accumulate error messages (right now I retrieve messages like "Failure(kofailed)" with just a concatenation of the 2 messages) but I don't know if it's possible to get a ValidationNel from a \/ type.
Related
Let's say I have functions which return Future[Either[_, _] and I want to apply some of these functions in case of failures, that means apply them only to left side. The simplified example is:
def operation1: Future[Either[String, Int]] = Future.successful(Right(5))
def operation2: Future[Either[String, Int]] = Future.successful(Left("error"))
def operation2FallBackWork = Future.successful{
println("Doing some revert stuff")
Left("Error happened, but reverting was successful")
}
val res = for {
res1 <- EitherT.fromEither(operation1)
res2 <- EitherT.fromEither(operation2)//.leftFlatMap(operation2FallBackWork) -????
} yield res1 + res2
Await.result(res.toEither, 5 seconds)
How to achieve that?
The closest thing to a leftFlatMap is MonadError's handleError, which has exactly the signature you'd expect from something called leftFlatMap (although note that you'll need to change the fallback operation to an EitherT and provide a constant function instead of passing it as-is). You can use the EitherT instance directly like this:
import scala.concurrent.{ Await, Future }
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import scalaz._, Scalaz._
def operation1: Future[Either[String, Int]] = Future.successful(Right(5))
def operation2: Future[Either[String, Int]] = Future.successful(Left("error"))
def operation2FallBack: EitherT[Future, String, Int] = EitherT(
Future.successful {
println("Doing some revert stuff")
"Error happened, but reverting was successful".left
}
)
val E: MonadError[({ type L[x] = EitherT[Future, String, x] })#L, String] =
implicitly
val res = for {
a <- EitherT.fromEither(operation1)
b <- E.handleError(EitherT.fromEither(operation2))(_ => operation2FallBack)
} yield a + b
Await.result(res.toEither, 5.seconds)
You can also use the syntax provided by MonadError to make it look like EitherT has a handleError method, although it takes a bit more ceremony to get the Scala compiler to recognize that your operations have the right shape:
import scala.concurrent.{ Await, Future }
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import scalaz._, Scalaz._
type FE[x] = EitherT[Future, String, x]
def operation1: FE[Int] = EitherT(Future.successful(5.right))
def operation2: FE[Int] = EitherT(Future.successful("error".left))
def operation2FallBack: FE[Int] = EitherT(
Future.successful {
println("Doing some revert stuff")
"Error happened, but reverting was successful".left
}
)
val res = for {
a <- operation1
b <- operation2.handleError(_ => operation2FallBack)
} yield a + b
Await.result(res.toEither, 5.seconds)
I'd prefer this second version, but it's a matter of style and taste.
Assume we have EitherT[F, String, A].
The withFilter function of Scalaz uses the zero of String's Monoid in order to fill in Left if the filter fails.
Therefore, there is no meaningful error message.
How could I implement something of the form where Left would be "Not positive".
val a: Either[Future, String, Int] = -1.point[EitherT[Future, String, ?]]
val foo = for {
aa <- a
if aa >= 0
} yield aa
Are the filter and withFilter methods on EitherT just hacks to fulfill the for-comprehension demands?
You can use EitherT#ensure :
import scalaz._, Scalaz._
val xs: List[String \/ Int] =
List(1.right, 2.right, -5.right, "other error".left, 3.right, -4.right)
EitherT(xs).ensure("not positive")(_ > 0)
// EitherT(List(\/-(1), \/-(2), -\/(not positive), -\/(other error), \/-(3), -\/(not positive)))
Suppose I need to accumulate function failures. I can do it easily with scalaz:
val foo: String => Throwable\/Int = {s => \/.fromTryCatchNonFatal(s.toInt)}
val v1 = foo("abc").validation.toValidationNel
val v2 = foo("xyz").validation.toValidationNel
val v = (v1 |#| v2) {_ + _}
val e = v.toDisjunciton
Now I've got a function, which returns scala.util.Try instead of scalaz.\/
val f: String => Try[Int] = {s => Try(s.toInt)}
What is best way to accumulate the scala.util.Try failures ? Do I need to convert scala.util.Try to scalaz.Validation and visa versa manually?
Maybe this may help you:
def accumulate[T](tries:Try[T]*) = tries.collect {case Failure(t) => t}
scalaz 7.2.x contains https://github.com/scalaz/scalaz/blob/122e5f9dfc3fbe9189d1f817e703c78047fbb3ef/core/src/main/scala/scalaz/std/Try.scala#L14-L15 which you can use as an idea to create an implicit class which does the conversion to Validation and ValidationNel for you.
In the following snippet,
trait MyType1; trait MyType2
import scala.concurrent.Promise
val p1 = Promise[Option[MyType1]]()
val p2 = Promise[MyType2]()
I pass in p1 and p2 to another function where I complete the Promise using a successful Future. After the call to this function, I try to read the value in the Promise:
trait Test {
// get the Future from the promise
val f1 = p1.future
val f2 = p2.future
for {
someF1Elem <- f1
f1Elem <- someF1Elem
f2Elem <- f1Elem
} yield {
// do something with f1Elem and f2Elem
"..."
}
}
When I try to compile this, I get some compiler issues.
Error:(52, 19) type mismatch;
found : Option[Nothing]
required: scala.concurrent.Future[?]
flElem <- someF1Elem
^
IntelliJ shows no errors or what-so-ever and the types look aligned. But I'm not sure why the compiler is unhappy! Any clues?
Your for comprehension types must be consistent, so you cannot freely mix Option and Future in the way you do.
In your example, f1 returns a Future[Option[MyType1]] while f2 returns a Future[MyType2]
Remember that a for comprehension desugars to a series of flatMap/map and potentially withFilter.
Also the (simplified) signatures of flatMap for Future[A] and Option[A] are
def flatMap[B](f: A => Future[B]): Future[B]
def flatMap[B](f: A => Option[B]): Option[B]
The first two steps of the for-comprehension desugar to
f1.flatMap { someF1Elem =>
someF1Elem.flatMap { f1Elem => // this returns a `Option[MyType1]`
...
}
}
see the error now?
Now, to fix this there's a few approaches you can follow. One very handy is to use Monad Transformers, which allow you to combine (for instance) Future and Option into a single monad type OptionT.
Specifically you can go back and forth from Future[Option[A]] to OptionT[Future, A]. The basic idea is that you can flatMap on the latter and extract a value of type A.
But before that, you need to make your types of the "right shape", so that both are a Future[Option[Something]]
Here's an example using scalaz
import scalaz._; import Scalaz._ ; import scalaz.OptionT._
import scala.concurrent.{ Promise, Future }
import scala.concurrent.ExecutionContext.Implicits.global
trait MyType1
trait MyType2
val p1 = Promise[Option[MyType1]]()
val p2 = Promise[MyType2]()
val f1 = p1.future
val f2 = p2.future
val res = for {
f1Elem <- optionT(f1)
f2Elem <- f2.liftM[OptionT]
} yield {
println(s"$f1Elem $f2Elem")
}
optionT builds an OptionT[Future, A] given a Future[Option[A]], while liftM[OptionT] achieves the same when you have a Future[A]
The type of res is OptionT[Future, Unit] in this case, and you can get a Future[Option[Unit]] by calling run on it.
Looking at "How does yield work", your for comprehension is equivalent to
f1.flatMap(someF1Elem: Option[MyType1] =>
someF1Elem.flatMap(f1Elem =>
f1Elem.map(f2Elem =>
"..."
)
)
)
Basically what happens is this: flatMap on a Future is defined to take a function that returns another Future.
This gives you a similar error:
<console>:64: error: value map is not a member of MyType1
f1Elem.map(f2Elem =>
^
<console>:63: error: type mismatch;
found : Option[Nothing]
required: scala.concurrent.Future[?]
someF1Elem.flatMap(f1Elem =>
^
If you want the operation to perform asynchronously, i.e. really mapping the Future instances, you will have to get back to a Future[Option[X]]. As Kim suggested, you can nest the comprehension:
import scala.concurrent.{Future, Promise}
def test: Future[Option[String]] = {
val p1 = Promise[Option[MyType1]]()
val p2 = Promise[MyType2]()
// ... run code that completes the promises
val f1 = p1.future
val f2 = p2.future
for {
someF1Elem: Option[MyType1] <- f1
f2Elem <- f2
} yield {
for (f1Elem <- someF1Elem) yield "something"
}
}
You could also make the resulting Future fail when the option is not defined (NoSuchElementException)
def test: Future[String] = {
val p1 = Promise[Option[MyType1]]()
val p2 = Promise[MyType2]()
// ... run code that completes the promises
val f1 = p1.future
val f2 = p2.future
for {
someF1Elem: Option[MyType1] <- f1
f2Elem <- f2
} yield {
val f1Elem = someF1Elem.get // may cause an exception and fail the future
"something"
}
}
In a for comprehension, the expressions on the right hand side of the <- have to have compatible types. That is because for comprehensions are basically syntactic sugar for nested flatMap calls. To solve this problem, you can either have a for comprehension nested within another for comprehension or you can use methods like get or map on the Options.
Another option is to use monad transformers, but that's beyond the scope of this answer.
I am attempting to write a simple program using the State monad in scalaz that will modify some state based on input passed in by the user. How is the best accomplished. Currently I have:
import scalaz._
import Scalaz._
import effect._
import IO._
def acc = for {
i <- init
_ <- modify{s: Int => 100}
v <- readLn
_ <- modify{s: Int => v}
} yield ()
which throws:
<console>:25: error: polymorphic expression cannot be instantiated to expected type;
found : [B]scalaz.effect.IO[B]
required: scalaz.IndexedStateT[scalaz.Id.Id,Int,?,?]
You can't do this directly, but the monad transformer version isn't too terrible:
import scalaz._, Scalaz._, effect._, IO._
type IS[S, A] = StateT[IO, S, A]
type ISInt[A] = IS[Int, A]
val ms = MonadState[IS, Int]
import ms._
def acc = for {
i <- init
_ <- modify(s => 100)
v <- readLn.liftIO[ISInt]
_ <- modify(s => v.toInt)
} yield ()
This gives you a StateT[IO, Int, Unit] that you can turn into an IO action with acc.exec(whatever).
It's worth noting your code could be cleaned up a bit—the init is unnecessary, for example, and you might as well use put if you're throwing away the arguments in modify, etc. It's also worth noting that in practice something like an IORef might be more practical here.