Can different monads be used in for-comprehensions? Here's the code that uses map
case class Post(id: Int, text: String)
object PostOps {
def find(id: Int) : Option[Post] = if (id == 1) Some(Post(1, "text")) else None
def permitted(post: Post, userId: Int) : Try[Post] = if (userId == 1) Success(post) else Failure(new UnsupportedOperationException)
def edit(id: Int, userId : Int, text: String) = find(id).map(permitted(_, userId).map(_.copy(text = text))) match {
case None => println("Not found")
case Some(Success(p)) => println("Success")
case Some(Failure(_)) => println("Not authorized")
}
}
The straightforward version of for-comprehension doesn't work for obvious reasons, but is it possible to make it work with some additional code? I know it's possible in C# so it would be weird if it is not in Scala.
You can only use one type of monad in a for comprehension, since it is just syntactic sugar for flatMap and map.
If you have a stack of monads (eg Future[Option[A]]) you could use a monad transformer, but that does not apply here.
A solution for your case could be to use one monad : go from Option to Try or go from both Option and Try to Either[String, A].
def tryToEither[L, R](t: Try[R])(left: Throwable => L): Either[L, R] =
t.transform(r => Success(Right(r)), th => Success(Left(left(th)))).get
def edit(id: Int, userId: Int, text: String) = {
val updatedPost = for {
p1 <- find(id).toRight("Not found").right
p2 <- tryToEither(permitted(p1, userId))(_ => "Not Authorized").right
} yield p2.copy(text = text)
updatedPost match {
case Left(msg) => println(msg)
case Right(_) => println("success")
}
}
You could define an error type instead of using String, this way you can use Either[Error, A].
sealed trait Error extends Exception
case class PostNotFound(userId: Int) extends Error
case object NotAuthorized extends Error
I assume you mean the fact that you now have an Option[Try[Post]]
find(id).map(permitted(_, userId).map(_.copy(text = text))) match {
case None => println("Not found")
case Some(Success(p)) => println("Success")
case Some(Failure(_)) => println("Not authorized")
}
Could be done as a for a few ways.
Nesting fors:
for {
post <- find(id)
} yield {
for {
tryOfPost <- permitted(post, userId)
} yield {
tryOfPost.copy(text = text)
}
}
Convert Option to a Try so you're using a single type, this has the disadvantage of losing the difference between an error in the Try and a None from the Option. credit here for how to go from Option to Try.
for {
post <- find(id).fold[Try[Post]](Failure[Post](new OtherException))(Success(_))
permittedPost <- permitted(post, userId)
} yield {
permittedPost.copy(text = text)
}
You could also look into the OptionT monad transformer in scalaz to create a type which is an OptionTTry.
Fundamentally, though, Monads don't compose this way, at least not generically.
Related
With this code println will be executed only for specified exception. I'm wondering if it's possible to negate that line to make it executed for all other exceptions that are not specified. I know it's possible using 2 cases, but I want to know if it can be done with one case.
val myHandler: PartialFunction[Throwable, Unit] = {
case e # (_: MappingException | _: ParseException | _: SomeOtherException) =>
println("Got it")
}
AFAIk you can not do this with a single match, but you can create your own custom Extractor in case you need to replicate this behaviour in multiple places.
import scala.reflect.ClassTag
final class Not[A : ClassTag] {
def unapply(any: Any): Boolean = any match {
case _: A => false
case _ => true
}
}
object Not {
def apply[A : ClassTag]: Not[A] = new Not
}
which you can use like this:
final val NotAnInt = Not[Int]
10 match {
case NotAnInt() => false
case _ => true
}
// res: Boolean = true
"10" match {
case NotAnInt() => true
case _ => false
}
// res: Boolean = true
However, keep in mind this will have all the limitation of any type check, like not being able to differentiate between a List[Int] from a List[String] due erasure; and being considered a bad practice.
I would suggest looking into a typeclass approach, for example, I believe Shapeless provides a negation one.
You can see the code running here.
Well you've already identified what is probably the more readable way to do it.
val myHandler: PartialFunction[Throwable, Unit] = {
case e # (_: MappingException | _: ParseException | _: SomeOtherException) =>
throw e
case _ =>
println("Got it")
}
This is probably how I'd write this in actual production code. It's sensible and it's clear at a glance.
But you asked for one case, so let's give that a go. Since we want to check against several types, we'll need to be able to represent them as a list. There are countless Scala libraries that make this prettier, but for our purposes we'll just roll our own.
trait TList {
def isMember(x: Any): Boolean
}
object Nil extends TList {
def isMember(x: Any) = false
}
case class Cons[H : ClassTag](val tail: TList) extends TList {
def isMember(x: Any) = {
x match {
case _: H => true
case _ => tail.isMember(x)
}
}
}
So we can represent classical Lisp-style singly-linked lists and check whether an arbitrary Any value has a type anywhere in the list. Now let's negate it and write an unapply method.
case class NotMember(val types: TList) {
def unapply(elem: Any): Boolean =
!types.isMember(elem)
}
Then our handler looks like
val test = NotMember(
Cons[MappingException](Cons[ParseException](Cons[SomeOtherException](Nil)))
)
val myHandler: PartialFunction[Throwable, Unit] = {
case test() =>
println("Got it")
}
Again, if you really want to go down this road, you'll want to grab a library to make the type-level stuff manageable. But it's definitely possible. The only question is whether it's worth it for your use case.
I want to apply a sequence of transformations to a String but stopping when there is an error. This is an example of a more general pattern (a kind of Chain of Responsibility pattern or Visitor pattern)
If it is possible, I want to avoid using Cats or Scalaz at the moment. If you know how to do it on plain Scala and also Cats/Scalaz I will happy to see the code in the answer ;)
So following is my approach (assertions at the end of the code), but It is not stopping when an error is found. Basically is skipping the execution of the transformation X times.
type Error = String
sealed trait Transformer {
def transform(txt:String) : Either[Error, String]
}
object Transformer1 extends Transformer {
override def transform(txt: String): Either[Error, String] = Right(s"${txt}_One")
}
object Transformer2 extends Transformer {
override def transform(txt: String): Either[Error, String] = Right(s"${txt}_Two")
}
object Transformer3 extends Transformer {
override def transform(txt: String): Either[Error, String] = Right(s"${txt}_Three")
}
object TransformerError extends Transformer {
override def transform(txt: String): Either[Error, String] = Left("Error!!!!")
}
def transform(txt: String, transformers: Seq[Transformer]): Either[Error, String] =
transformers.foldLeft(Right(txt):Either[Error, String])( (result, t) => result match {
case Right(txt) => t.transform(txt)
case error => error
} )
val tOk = Seq(Transformer1, Transformer2, Transformer3)
val tError = Seq(Transformer1, TransformerError, Transformer3)
assert(transform("Whatever", tOk) == Right("Whatever_One_Two_Three"))
assert(transform("Whatever", tError) == Left("Error!!!!"))
Any suggestion?
Thanks!!
In Scala 2.12, Either is right-biased, so for-yield would do the trick.
for {
v1 <- Transformer1.transform("Whatever")
v2 <- Transformer2.transform(v1)
v3 <- Transformer3.transform(v2)
} yield {
v3
}
evaluates to Right(Whatever_One_Two_Three), while
for {
v1 <- Transformer1.transform("Whatever")
v2 <- TransformerError.transform(v1)
v3 <- Transformer3.transform(v2)
} yield {
v3
}
evaluates to Left(Error!!!!)
However, if you would like to return a result with all the transformations applied until an error was reached, that is,
assert(transform("Whatever", tError) == Right("Whatever_One"))
then the following refactoring of transform function might work:
def transform(txt: String, transformers: Seq[Transformer]): Either[Error, String] = {
type Current = Either[Error, String]
type Previous = Either[Error, String]
def foldLeftWithEarlyReturn: Tuple2[Current, Previous] = {
transformers.foldLeft[Tuple2[Current, Previous]](Right(txt) , Right(txt)){
(result, t) => result match {
case ( Right(txt) , Right(previousTxt) ) => ( t.transform(txt) , Right(txt) )
case ( Left(error) , Right(previousTxt) ) => return ( Right(previousTxt), Left(error) )
case e => e
}
}
}
if (foldLeftWithEarlyReturn._1.isLeft)
foldLeftWithEarlyReturn._2 // this means last transformation in sequence resulted in Left, so return previous
else
foldLeftWithEarlyReturn._1
}
When processing a collection, if you want early termination you often have to turn to recursion.
def transform(txt :String
,transformers :Seq[Transformer]
): Either[Error, String] = transformers match {
case Seq() => Right(txt)
case hd +: tl => hd.transform(txt).fold(Left(_), transform(_, tl))
}
A tail-recursive version is also possible, if a little less concise.
#tailrec
def transform(txt :String
,transformers :Seq[Transformer]
): Either[Error, String] = transformers match {
case Seq() => Right(txt)
case hd +: tl =>
val rslt = hd.transform(txt)
if (rslt.isLeft) rslt else transform(rslt.toSeq.head, tl)
}
Pure Scala
Probably, the simplest way to make your code short-circuiting, is to use return statement. It returns the result from the innermost named function it's contained in:
def transform(txt: String, transformers: Seq[Transformer]): Either[Error, String] =
transformers.foldLeft(Right(txt):Either[Error, String])( (result, t) =>
result match {
case Right(txt) => t.transform(txt)
case error => return error
} )
So the return error statement in this code would immediately return the first Left encountered from the transform function.
Cats
In cats you don't really have to do anything special. It short-circuits certain calls for some monads automatically, because monads have to implement tailRecM, and some monads (including Either) implement it in a lazy way to avoid doing useless flatMaps.
import cats.implicits._
def transformCats(txt: String, transformers: List[Transformer]): Either[Error, String] = {
// It seems this is needed to help Scala with type inference.
type Result[T] = Either[Error, T]
// foldLeftM is implemented in terms of tailRecM,
// and thus is short-circuiting for Either
transformers.foldLeftM(txt)((result, tf) => tf.transform(result): Result[String])
}
There're map/flatMap methods, there're also recover/recoverWith methods in the Scala Future standard API.
Why there's no collectWith ?
The code of the collect method is pretty simple :
def collect[S](pf: PartialFunction[T, S])(implicit executor: ExecutionContext): Future[S] =
map {
r => pf.applyOrElse(r, (t: T) => throw new NoSuchElementException("Future.collect partial function is not defined at: " + t))
}
The code of the collectWith method is then easy to imagine :
def collectWith[S](pf: PartialFunction[T, Future[S]])(implicit executor: ExecutionContext): Future[S] =
flatMap {
r => pf.applyOrElse(r, (t: T) => throw new NoSuchElementException("Future.collect partial function is not defined at: " + t))
}
I know that I can implement it and "extend" the Future standard API easily thanks to this article : http://debasishg.blogspot.fr/2008/02/why-i-like-scalas-lexically-scoped-open.html
I done that in my project :
class RichFuture[T](future: Future[T]) {
def collectWith[S](pf: PartialFunction[T, Future[S]])(implicit executor: ExecutionContext): Future[S] =
future.flatMap {
r => pf.applyOrElse(r, (t: T) => throw new NoSuchElementException("Future.collect partial function is not defined at: " + t))
}
}
trait WithRichFuture {
implicit def enrichFuture[T](person: Future[T]): RichFuture[T] = new RichFuture(person)
}
Maybe my needs for that does not justify to implement it in the standard API ?
Here is why I need this method in my Play2 project :
def createCar(key: String, eligibleCars: List[Car]): Future[Car] = {
def handleResponse: PartialFunction[WSResponse, Future[Car]] = {
case response: WSResponse if response.status == Status.CREATED => Future.successful(response.json.as[Car])
case response: WSResponse
if response.status == Status.BAD_REQUEST && response.json.as[Error].error == "not_the_good_one" =>
createCar(key, eligibleCars.tail)
}
// The "carApiClient.createCar" method just returns the result of the WS API call.
carApiClient.createCar(key, eligibleCars.head).collectWith(handleResponse)
}
I don't know how to do that without my collectWith method.
Maybe it's not the right way to do something like this ?
Do you know a better way ?
EDIT:
I have maybe a better solution for the createCar method that does not requires the collectWith method :
def createCar(key: String, eligibleCars: List[Car]): Future[Car] = {
for {
mayCar: Option[Car] <- Future.successful(eligibleCars.headOption)
r: WSResponse <- carApiClient.createCar(key, mayCar.get) if mayCar.nonEmpty
createdCar: Car <- Future.successful(r.json.as[Car]) if r.status == Status.CREATED
createdCar: Car <- createCar(key, eligibleCars.tail) if r.status == Status.BAD_REQUEST && r.json.as[Error].error == "not_the_good_one"
} yield createdCar
}
What do you think about this second solution ?
Second edit:
Just for information, here is my final solution thanks to #Dylan answer :
def createCar(key: String, eligibleCars: List[Car]): Future[Car] = {
def doCall(head: Car, tail: List[Car]): Future[Car] = {
carApiClient
.createCar(key, head)
.flatMap( response =>
response.status match {
case Status.CREATED => Future.successful(response.json.as[Car])
case Status.BAD_REQUEST if response.json.as[Error].error == "not_the_good_one" =>
createCar(key, tail)
}
)
}
eligibleCars match {
case head :: tail => doCall(head, tail)
case Nil => Future.failed(new RuntimeException)
}
}
Jules
How about:
def createCar(key: String, eligibleCars: List[Car]): Future[Car] = {
def handleResponse(response: WSResponse): Future[Car] = response.status match {
case Status.Created =>
Future.successful(response.json.as[Car])
case Status.BAD_REQUEST if response.json.as[Error].error == "not_the_good_one" =>
createCar(key, eligibleCars.tail)
case _ =>
// just fallback to a failed future rather than having a `collectWith`
Future.failed(new NoSuchElementException("your error here"))
}
// using flatMap since `handleResponse` is now a regular function
carApiClient.createCar(key, eligibleCars.head).flatMap(handleResponse)
}
Two changes:
handleResponse is no longer a partial function. The case _ returns a failed future, which is essentially what you were doing in your custom collectWith implementation.
use flatMap instead of collectWith, since handleResponse now suits that method signature
edit for extra info
If you really need to support the PartialFunction approach, you could always convert a PartialFunction[A, Future[B]] to a Function[A, Future[B]] by calling orElse on the partial function, e.g.
val handleResponsePF: PartialFunction[WSResponse, Future[Car]] = {
case ....
}
val handleResponse: Function[WSResponse, Future[Car]] = handleResponsePF orElse {
case _ => Future.failed(new NoSucheElementException("default case"))
}
Doing so would allow you to adapt an existing partial function to fit into a flatMap call.
(okay, technically, it already does, but you'd be throwing MatchErrors rather than your own custom exceptions)
Here is the code
class Result[A] {
def map[B](f: (Result[A] => Result[B]), xResult: Result[A]) = {
xResult match {
case Success(x) => Success(f(xResult))
case Failure(errs) => Failure(errs)
}
}
def apply[B](fResult: Result[A], xResult: Result[B]) = {
(fResult, xResult) match {
case (Success(f), Success(x)) => Success((f, x))
case (Failure(errs), Success(a)) => Failure(errs)
case (Success(a), Failure(errs)) => Failure(errs)
case (Failure(errs), Failure(errs2)) => Failure(List(errs, errs2))
}
}
}
case class Success[A](a: A) extends Result[A] {}
case class Failure[A](a: A) extends Result[A] {}
def createCustomerId(id: Int) = {
if (id > 0)
Success(id)
else
Failure("CustomerId must be positive")
}
Here are the questions
1) result from type inference from method createCustomer is like this
Product with Serializable with Result[_ >: Int with String]
I dont have trouble with the "Product with Serializable" stuff, the thing im wondering is how to do something meaningfull with the result, and just out of curiosity how to initialize a type like that.
suppose i have another method as shown below
def createCustomer(customerId: Result[_ >: Int with String], email: Result[String]) = {
how can i read/do something with "customerId" argument
}
how to initialize a type like that
val x: Result[Int with String] = ???
notes:
i know my Result class doesnt have any generic "getter"
I know that perhaps things could be easier in method "createCustomerId" if when id > 0 i returned Success(id.ToString), but i really want to know whats going on with this lower type and how i could take advantage (if possible) in the future
The problem is that your type definition states that both Success and Failure take the same argument type. However, you actually want to put some other type of argument into Failure while still keeping covariance. Here is the simplest fix:
class Result[A] {
def map[B](f: (Result[A] => Result[B]), xResult: Result[A]) = {
xResult match {
case Success(x) => Success(f(xResult))
case Failure(errs) => Failure(errs)
}
}
def apply[B](fResult: Result[A], xResult: Result[B]) = {
(fResult, xResult) match {
case (Success(f), Success(x)) => Success((f, x))
case (Failure(errs), Success(a)) => Failure(errs)
case (Success(a), Failure(errs)) => Failure(errs)
case (Failure(errs), Failure(errs2)) => Failure(List(errs, errs2))
}
}
}
case class Success[A](a: A) extends Result[A] {}
case class Failure[A, B](a: B) extends Result[A] {}
object TestResult extends App {
def createCustomerId(id: Int): Result[Int] = {
if (id > 0)
Success(id)
else
Failure("CustomerId must be positive")
}
println(createCustomerId(0))
println(createCustomerId(1))
}
prints:
Failure(CustomerId must be positive)
Success(1)
If you don't annotate return type for createCustomerId with Result[Int] compiler will infer Result[_ <: Int] with Product with Serializable which is probably not ideal but not too bad.
I want to check the type of the parameters of a method, but I don't know the best way to do this. See my code:
class X {
def x(a: Int, b: String) {}
}
val methods = classOf[X].getDeclaredMethods
methods map { m =>
m.getParameterTypes.toList map { t =>
println(t.getName)
// I don't know how to write the following
if ( the type of t is Int) { do something}
else if( the type of t is String ) { do something}
else { }
}
}
Please note the comment in the code. I don't know how to check the types in scala way.
I've tried:
t match {
case _:String => println("### is a string")
case _:Int => println("### is an int")
case _ => println("### ?")
}
But it can't be compiled.
I can use java-way to check:
if (t.isAssignableFrom(classOf[String])) // do something
else if(t.isAssignableFrom(classOf[Int])) // do something
else {}
It seems we should use it in scala, right?
UPDATE:
If I want to use match, I should write like this:
t match {
case i if i.isAssignableFrom(classOf[Int]) => println("### is an Int")
case s if s.isAssignableFrom(classOf[String]) => println("### is a String")
case _ => println("###?")
}
Is it the best answer?
I could make it work with t as a type by defining the cases as constants. It wouldn't compile with the class literals as the case expression. Try:
val S = classOf[String]
val I = classOf[Int]
t match {
case S => println("### is a string")
case I => println("### is an int")
case _ => println("### ?")
}
You can use ClassManifest.fromClass to correctly handle the coercion of primitives to AnyVals, and any other such troubles you might have encountering boxed vs unboxed types when getting funky with reflection.
Like this:
import reflect.ClassManifest
class Wibble { def method(a:Int, b: String) = () }
for(method <- classOf[Wibble].getDeclaredMethods; paramType <- method.getParameterTypes) {
ClassManifest.fromClass(paramType) match {
case m if m <:< ClassManifest.Int => println("Interiffic")
case m if m <:< ClassManifest.Float => println("Floaty, like butterflies")
case m if m <:< ClassManifest.Double => println("Or Quits...")
//todo: all the other AnyVal types...
case m if m <:< classManifest[String] => println("bleeding edge physics, yeah baby!")
//...and a default condition
}
}