How to wait multiple messages properly and become new state after that - scala

I have an actor, that should receive two messages and after that, become new initialised state. I wrote some code, but it seems very ugly:
def waitInitialisation(#Nullable one: Integer, #Nullable two: String): Receive = {
case _one: Int =>
if (two == null)
context.become(waitInitialisation(_one, two))
else {
doSomething()
context.become(initialised(_one, two))
}
case _two: String =>
if (one == null)
context.become(waitInitialisation(one, _two))
else {
doSomething()
context.become(initialised(one, _two))
}
}
def initialised(one: Int, two: String): Receive = ???
override def receive: Receive = waitInitialisation(null, null)
So problems, what I see: null checking and duplicate code. How I can simplify my implementation and make it properly?

#chunjef already gave a great direction in using Options which is the way to go in Scala. I'm letting below two other options in which I use pattern matching to make the code a bit more beautiful.
Before you take a look at the two solutions please bear in mind that usually calling .get on an Option is not recommended and will possibly get you some compiler warnings. Anyways, we're always sure to make the right call in our examples because we're checking beforehand if the option isDefined.
Oh, and when working with values that might come null - like operating with Java APIs - always use Option's apply, not Some's apply.
The first one defines essentially the same method but structured a bit differently:
def waitInit(one: Option[Int], two: Option[String]): Receive = {
case value: Int if two.isDefined =>
context.become(initialised(value, two.get))
case value: Int =>
context.become(waitInit(Option(value), two))
case value: String if one.isDefined =>
context.become(initialised(one.get, value))
case value: String =>
context.become(waitInit(one, Option(value)))
}
override val receive = waitInit(None, None)
The second one splits this logic in two pieces so you can follow on it easier:
def waitOne(two: Option[String]): Receive = {
case one: Int if two.isDefined =>
context.become(initialised(one, two.get))
case one: Int =>
context.become(waitOne(two) orElse waitTwo(Option(one)))
}
def waitTwo(one: Option[Int]): Receive = {
case two: String if one.isDefined =>
context.become(initialised(one.get, two))
case two: String =>
context.become(waitOne(Option(two)) orElse waitTwo(one))
}
override val receive: Receive =
waitOne(None) orElse waitTwo(None)
That's it, I didn't put some code here (like the definition of initialised) essentially because it's the same.
Enjoy :)

Using Option is the idiomatic way in Scala to handle nulls:
def waitInitialisation(one: Option[Int], two: Option[String]): Receive = {
case _one: Int =>
two match {
case Some(s) =>
doSomething()
context.become(initialised(_one, s))
case None =>
context.become(waitInitialisation(Option(_one), None))
}
case _two: String =>
one match {
case Some(i) =>
doSomething()
context.become(initialised(i, _two))
case None =>
context.become(waitInitialisation(None, Option(_two)))
}
}
def initialised(one: Int, two: String): Receive = ???
def receive = waitInitialisation(None, None)
As for "code duplication," I wouldn't get hung up on the number of become calls. Your actor can be in one of the four following states:
waitInitialisation(None, None)
waitInitialisation(Some, None)
waitInitialisation(None, Some)
initialised
You probably could implement the state changes with the FSM trait, but that would be overkill for your case. The way you've structured your actor is simple and clear.

You can also do that with an additional message.
With that solution, adding or changing messages for the init will be easy :
case class InitState(
one : Option[Int],
two : Option[String],
three : Option[Boolean]
)
{
def fire() : Unit = {
context.become(waitInit(this))
self ! this
}
}
def waitInit(st : InitState = InitState(None, None, None)) : Receive = {
case i : Int =>
st.copy(one = Some(i)).fire()
case s : String =>
st.copy( two = Some(s)).fire()
case b : Boolean =>
st.copy(three = Some(b)).fire()
case InitState(Some(i : Int), Some(s : String), Some(b : Boolean)) =>
context.become(afterInit(i, s, b))
case _ : InitState =>
}
def afterInit(one : Int, two : String, three : Boolean) : Receive = ???
def receive = waitInit()
As chunjef wrote, for not so simple cases, the good choice will be using FSM.

akka-contrib provided this Aggregator pattern that is similar to what you are looking for: https://github.com/akka/akka/blob/master/akka-contrib/src/main/scala/akka/contrib/pattern/Aggregator.scala
This code is now deprecated but you can copy it into your project.
Here you can find how it works (this documentation is pretty old): http://doc.akka.io/docs/akka/2.3.0/contrib/aggregator.html
The main idea is to use expect or expectOnce to receive certain messages. Once that has happened, you can do whatever else.

Related

How to write a custom decoder for [Option[Option[A]] in Circe?

I had written a Reads converter in play-json for Option[Option[A]] that had the following behavior:
//given this case class
case class MyModel(field: Option[Option[String]])
//this JSON -- maps to --> this MyModel:
//"{ \"field\": \"value\" }" --> MyModel(field = Some(Some("value")))
//"{ \"field\": null, ... }" --> MyModel(field = Some(None))
//"{ }" --> MyModel(field = None)
So, providing the value mapped to Some[Some[A]], providing null mapped to Some[None] (i.e. Some[Option.empty[A]]), and not providing the value mapped to just None (i.e. Option.empty[Option[A]]). Here's the play-json converter:
def readOptOpt[A](implicit r: Reads[A]): Reads[Option[Option[A]]] = {
Reads[Option[Option[A]]] { json =>
path.applyTillLast(json).fold(
identity,
_.fold(_ => JsSuccess(None), {
case JsNull => JsSuccess(Some(None))
case js => r.reads(js).repath(path).map(a => Some(Some(a)))
})
)
}
}
Now I am converting my play-json code to Circe, but I can't figure out how to write a Decoder[Option[Option[A]] that has the same behavior. That is, I need
def optOptDecoder[A](implicit d: Decoder[A]): Decoder[Option[Option[A]] = ??? //help!
Any ideas on how I can make this work? Thanks
I figured this out:
There were two problems:
1) How to deal with the case where the field was completely missing from the JSON. Turns out you have to use Decoder.reattempt in your custom decoder, following Circe's decodeOption code, which works.
2) How to have the compiler recognize cases of Option[Option[A]] when your decoder code is sitting in a helper object (or wherever). Turns out if you're using semi-auto derivation, you can create an implicit in the companion object and that will override the defaults:
//companion object
object MyModel {
implicit def myModelOptOptDecoder[A](implicit d: Decoder[A]): Decoder[Option[Option[A]]] =
MyHelperObject.optOptDecoder
implicit val myModelDecoder: Decoder[MyModel] = deriveDecoder
}
Anyway, I don't think this will be much help to anybody in the future, so unless I get any upvotes in the next few hours I think I'll just delete this.
Edit2: Okay it was answered so I won't delete it. Stay strong, esoteric circe question, stay strong...
An Option[Option[A]] is a bit odd. I understand and mostly agree with the reasoning, but I think it's weird enough that it may warrant just replacing it with your own class (and writing a decoder for that). Something like:
sealed trait OptionalNull[+A] {
def toOption: Option[Option[A]]
}
object NotPresent extends OptionalNull[Nothing] {
override def toOption = None
}
object PresentButNull extends OptionalNull[Nothing] {
override def toOption = Some(None)
}
case class PresentNotNull[A](value: A) extends OptionalNull[A] {
override def toOption = Some(Some(value))
}
This has the additional benefit of not having to worry about implicit precedence and stuff like that. Might simplify your decoder.
Here is another solution I found (This is not my gist):
sealed trait UpdateOrDelete[+A]
case object Delete extends UpdateOrDelete[Nothing]
final case class UpdateOptionalFieldWith[A](value: A) extends UpdateOrDelete[A]
object UpdateOrDelete {
implicit def optionalDecoder[A](implicit decodeA: Decoder[A]): Decoder[UpdateOptionalField[A]] =
Decoder.withReattempt {
// We're trying to decode a field but it's missing.
case c: FailedCursor if !c.incorrectFocus => Right(None)
case c =>
Decoder.decodeOption[A].tryDecode(c).map {
case Some(a) => Some(UpdateOptionalFieldWith(a))
case None => Some(Delete)
}
}
// Random UUID to _definitely_ avoid collisions
private[this] val marker: String = s"$$marker-${UUID.randomUUID()}-marker$$"
private[this] val markerJson: Json = Json.fromString(marker)
implicit def optionalEncoder[A](implicit encodeA: Encoder[A]): Encoder[UpdateOptionalField[A]] =
Encoder.instance {
case Some(Delete) => Json.Null
case Some(UpdateOptionalFieldWith(a)) => encodeA(a)
case None => markerJson
}
def filterMarkers[A](encoder: Encoder.AsObject[A]): Encoder.AsObject[A] =
encoder.mapJsonObject { obj =>
obj.filter {
case (_, value) => value =!= markerJson
}
}
}

How to properly use future/actorSelection/resolveOne in Play app?

Below is my code:
def runAsync(crewType: String) = Action.async {
val temp: Future[Result] = Future(Crew.findCaptainByCrewType(crewType) match {
case None =>
BadRequest(s"Invalid crew name provided: $crewType")
case Some(crew) =>
system.actorSelection(s"/user/${crew.cptName}").resolveOne().map { actorRef =>
Ok(println("hi hi"))
}
})
temp
}
I don't understand why this doesn't work?
My objective is to have user pass in a name, which then I try to find an actor with that name using the actorSelection and resolveOne. I assume I am using it incorrectly?!
ActorSelection.resolveOne() returns a Future[ActorRef], and because you are inside a Future(...) you get a Future[Future[Result]] in case of a valid crewname.
You can solve this by using flatMap in which case you should also return a Future in case of an invalid crewname (None).
Unrelated: You can also leave out the temp value and can replace the pattern matching on Option by Option.fold.
def runAsync(crewType: String) = Action.async {
Future(Crew.findCaptainByCrewType(crewType)).flatMap( _.fold(
Future.successful(BadRequest(s"Invalid crew name provided: $crewType"))
)( crew =>
system.actorSelection(s"/user/${crew.cptName}").resolveOne().map {
actorRef => Ok(println("hi hi")) // not sure you want println here
}
))
}

How to implement receive () in Akka Actor

I am in the process of converting Akka UntypedActors in Java code to their Scala equivalent.
However, I am having trouble understanding how to correctly implement the receive() abstract method. The ScalaDoc is a little confusing and most of the examples I see just involve String messages!
My Actor can support multiple message types and this is my solution so far:
override def receive = {
case message if message.isInstanceOf[ClassOne] => {
// do something after message.asInstanceOf[ClassOne]
}
case message if message.isInstanceOf[ClassTwo] => {
// do something after message.asInstanceOf[ClassTwo]
}
case message => unhandled(message)
}
Is there a better way to achieve the above?
override def receive = {
case c: ClassOne =>
// do something after message.asInstanceOf[ClassOne]
case c: ClassTwo =>
// do something after message.asInstanceOf[ClassTwo]
case message => unhandled(message)
}
If you're using case classes, you can get more sophisticated.
case class ClassOne(x: Int, y: String)
case class ClassTwo(a: Int, b: Option[ClassOne])
override def receive = {
case ClassOne(x, y) =>
println(s"Received $x and $y")
case ClassTwo(a, Some(ClassOne(x, y)) if a == 42 =>
// do something
case ClassTwo(a, None) =>
case c # ClassOne(_, "foo") => // only match if y == "foo", now c is your instance of ClassOne
}
All sorts of fun stuff.
receive's type is really just a PartialFunction[Any,Unit], which means you can use Scala's pattern match expressions - in fact, you're already doing it, just not entirely succinctly. A terser equivalent that would also let you handle the type of the match for each case:
def receive = {
case classOneMessage : ClassOne => {
// do something
}
case classTwoMessage : ClassTwo => {
// do something
}
case _ => someCustomLogicHereOtherWiseThereWillBeASilentFailure
//you can, but you don't really need to define this case - in Akka
//the standard way to go if you want to process unknown messages
//within *this* actor, is to override the Actor#unhandled(Any)
//method instead
}
Read the tour article, and the already-linked tutorial for more info on pattern matching, especially in the context of using the feature together with case classes - this pattern is applied regularly when working with Akka, for example here in the Akka manual when handling the ActorIdentity case class.
receive is a regular partial function in Scala. You can write something like this in your case:
case class Example(number: Int, text: String)
override def receive = {
case message: ClassOne =>
// do something with ClassOne instance
case message: ClassTwo =>
// do something with ClassTwo instance
case Example(n, t) =>
println(t * n)
case Example(n, t) if n > 10 =>
println("special case")
}
You don't have to include a special case for unhandled messages unless your application logic requires you to handle all possible messages.
First two cases just match by type of a message and subtypes will be matched as well. Last one not only matches the type Example but also "deconstructs" it using pattern matching.

waiting for "recursive" futures in scala

a simple code sample that describes my problem:
import scala.util._
import scala.concurrent._
import scala.concurrent.duration._
import ExecutionContext.Implicits.global
class LoserException(msg: String, dice: Int) extends Exception(msg) { def diceRoll: Int = dice }
def aPlayThatMayFail: Future[Int] = {
Thread.sleep(1000) //throwing a dice takes some time...
//throw a dice:
(1 + Random.nextInt(6)) match {
case 6 => Future.successful(6) //I win!
case i: Int => Future.failed(new LoserException("I did not get 6...", i))
}
}
def win(prefix: String): String = {
val futureGameLog = aPlayThatMayFail
futureGameLog.onComplete(t => t match {
case Success(diceRoll) => "%s, and finally, I won! I rolled %d !!!".format(prefix, diceRoll)
case Failure(e) => e match {
case ex: LoserException => win("%s, and then i got %d".format(prefix, ex.diceRoll))
case _: Throwable => "%s, and then somebody cheated!!!".format(prefix)
}
})
"I want to do something like futureGameLog.waitForRecursiveResult, using Await.result or something like that..."
}
win("I started playing the dice")
this simple example illustrates what i want to do. basically, if to put it in words, i want to wait for a result for some computation, when i compose different actions on previous success or failed attampts.
so how would you implement the win method?
my "real world" problem, if it makes any difference, is using dispatch for asynchronous http calls, where i want to keep making http calls whenever the previous one ends, but actions differ on wether the previous http call succeeded or not.
You can recover your failed future with a recursive call:
def foo(x: Int) = x match {
case 10 => Future.successful(x)
case _ => Future.failed[Int](new Exception)
}
def bar(x: Int): Future[Int] = {
foo(x) recoverWith { case _ => bar(x+1) }
}
scala> bar(0)
res0: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$DefaultPromise#64d6601
scala> res0.value
res1: Option[scala.util.Try[Int]] = Some(Success(10))
recoverWith takes a PartialFunction[Throwable,scala.concurrent.Future[A]] and returns a Future[A]. You should be careful though, because it will use quite some memory when it does lots of recursive calls here.
As drexin answered the part about exception handling and recovering, let me try and answer the part about a recursive function involving futures. I believe using a Promise will help you achieve your goal. The restructured code would look like this:
def win(prefix: String): String = {
val prom = Promise[String]()
def doWin(p:String) {
val futureGameLog = aPlayThatMayFail
futureGameLog.onComplete(t => t match {
case Success(diceRoll) => prom.success("%s, and finally, I won! I rolled %d !!!".format(prefix, diceRoll))
case Failure(e) => e match {
case ex: LoserException => doWin("%s, and then i got %d".format(prefix, ex.diceRoll))
case other => prom.failure(new Exception("%s, and then somebody cheated!!!".format(prefix)))
}
})
}
doWin(prefix)
Await.result(prom.future, someTimeout)
}
Now this won't be true recursion in the sense that it will be building up one long stack due to the fact that the futures are async, but it is similar to recursion in spirit. Using the promise here gives you something to block against while the recursion does it's thing, blocking the caller from what's happening behind the scene.
Now, if I was doing this, I would probable redefine things like so:
def win(prefix: String): Future[String] = {
val prom = Promise[String]()
def doWin(p:String) {
val futureGameLog = aPlayThatMayFail
futureGameLog.onComplete(t => t match {
case Success(diceRoll) => prom.success("%s, and finally, I won! I rolled %d !!!".format(prefix, diceRoll))
case Failure(e) => e match {
case ex: LoserException => doWin("%s, and then i got %d".format(prefix, ex.diceRoll))
case other => prom.failure(new Exception("%s, and then somebody cheated!!!".format(prefix)))
}
})
}
doWin(prefix)
prom.future
}
This way you can defer the decision on whether to block or use async callbacks to the caller of this function. This is more flexible, but it also exposes the caller to the fact that you are doing async computations and I'm not sure that is going to be acceptable for your scenario. I'll leave that decision up to you.
This works for me:
def retryWithFuture[T](f: => Future[T],retries:Int, delay:FiniteDuration) (implicit ec: ExecutionContext, s: Scheduler): Future[T] ={
f.recoverWith { case _ if retries > 0 => after[T](delay,s)(retryWithFuture[T]( f , retries - 1 , delay)) }
}

scala style - how to avoid having lots of nested map

Very often i end up with lots of nested .map and .getOrElse when validating several consecutives conditions
for example:
def save() = CORSAction { request =>
request.body.asJson.map { json =>
json.asOpt[Feature].map { feature =>
MaxEntitiyValidator.checkMaxEntitiesFeature(feature).map { rs =>
feature.save.map { feature =>
Ok(toJson(feature.update).toString)
}.getOrElse {
BadRequest(toJson(
Error(status = BAD_REQUEST, message = "Error creating feature entity")
))
}
}.getOrElse {
BadRequest(toJson(
Error(status = BAD_REQUEST, message = "You have already reached the limit of feature.")
))
}
}.getOrElse {
BadRequest(toJson(
Error(status = BAD_REQUEST, message = "Invalid feature entity")
))
}
}.getOrElse {
BadRequest(toJson(
Error(status = BAD_REQUEST, message = "Expecting JSON data")
))
}
}
You get the idea
I just wanted to know if there's some idiomatic way to keep it more clear
If you hadn't had to return a different message for the None case this would be an ideal use-case for for comprehension. In your case , you probably want to use the Validation monad, as the one you can find in Scalaz. Example ( http://scalaz.github.com/scalaz/scalaz-2.9.0-1-6.0/doc.sxr/scalaz/Validation.scala.html ).
In functional programming, you should not throw exceptions but let functions which can fail return an Either[A,B], where by convention A is the type of result in case of failure and B is the type of result in case of success. You can then match against Left(a) or Right(b) to handle, reespectively, the two cases.
You can think of the Validation monad as an extended Either[A,B] where applying subsequent functions to a Validation will either yield a result, or the first failure in the execution chain.
sealed trait Validation[+E, +A] {
import Scalaz._
def map[B](f: A => B): Validation[E, B] = this match {
case Success(a) => Success(f(a))
case Failure(e) => Failure(e)
}
def foreach[U](f: A => U): Unit = this match {
case Success(a) => f(a)
case Failure(e) =>
}
def flatMap[EE >: E, B](f: A => Validation[EE, B]): Validation[EE, B] = this match {
case Success(a) => f(a)
case Failure(e) => Failure(e)
}
def either : Either[E, A] = this match {
case Success(a) => Right(a)
case Failure(e) => Left(e)
}
def isSuccess : Boolean = this match {
case Success(_) => true
case Failure(_) => false
}
def isFailure : Boolean = !isSuccess
def toOption : Option[A] = this match {
case Success(a) => Some(a)
case Failure(_) => None
}
}
final case class Success[E, A](a: A) extends Validation[E, A]
final case class Failure[E, A](e: E) extends Validation[E, A]
Your code now can be refactored by using the Validation monad into three validation layers. You should basically replace your map with a validation like the following:
def jsonValidation(request:Request):Validation[BadRequest,String] = request.asJson match {
case None => Failure(BadRequest(toJson(
Error(status = BAD_REQUEST, message = "Expecting JSON data")
)
case Some(data) => Success(data)
}
def featureValidation(validatedJson:Validation[BadRequest,String]): Validation[BadRequest,Feature] = {
validatedJson.flatMap {
json=> json.asOpt[Feature] match {
case Some(feature)=> Success(feature)
case None => Failure( BadRequest(toJson(
Error(status = BAD_REQUEST, message = "Invalid feature entity")
)))
}
}
}
And then you chain them like the following featureValidation(jsonValidation(request))
This is a classic example of where using a monad can clean up your code. For example you could use Lift's Box, which is not tied to Lift in any way. Then your code would look something like this:
requestBox.flatMap(asJSON).flatMap(asFeature).flatMap(doSomethingWithFeature)
where asJson is a Function from a request to a Box[JSON] and asFeature is a function from a Feature to some other Box. The box can contain either a value, in which case flatMap calls the function with that value, or it can be an instance of Failure and in that case flatMap does not call the function passed to it.
If you had posted some example code that compiles, I could have posted an answer that compiles.
I tried this to see if pattern matching offered someway to adapt the submitted code sample (in style, if not literally) to something more coherent.
object MyClass {
case class Result(val datum: String)
case class Ok(val _datum: String) extends Result(_datum)
case class BadRequest(_datum: String) extends Result(_datum)
case class A {}
case class B(val a: Option[A])
case class C(val b: Option[B])
case class D(val c: Option[C])
def matcher(op: Option[D]) = {
(op,
op.getOrElse(D(None)).c,
op.getOrElse(D(None)).c.getOrElse(C(None)).b,
op.getOrElse(D(None)).c.getOrElse(C(None)).b.getOrElse(B(None)).a
) match {
case (Some(d), Some(c), Some(b), Some(a)) => Ok("Woo Hoo!")
case (Some(d), Some(c), Some(b), None) => BadRequest("Missing A")
case (Some(d), Some(c), None, None) => BadRequest("Missing B")
case (Some(d), None, None, None) => BadRequest("Missing C")
case (None, None, None, None) => BadRequest("Missing D")
case _ => BadRequest("Egads")
}
}
}
Clearly there are ways to write this more optimally; this is left as an exercise for the reader.
I agree with Edmondo suggestion of using for comprehension but not with the part about using a validation library (At least not anymore given the new features added to scala standard lib since 2012). From my experience with scala, dev that struggle to come up with nice statement with the standard lib will also end up doing the same of even worst when using libs like cats or scalaz. Maybe not at the same place, but ideally we would solve the issue rather than just moving it.
Here is your code rewritten with for comprehension and either that is part of scala standard lib :
def save() = CORSAction { request =>
// Helper to generate the error
def badRequest(message: String) = Error(status = BAD_REQUEST, message)
//Actual validation
val updateEither = for {
json <- request.body.asJson.toRight(badRequest("Expecting JSON data"))
feature <- json.asOpt[Feature].toRight(badRequest("Invalid feature entity"))
rs <- MaxEntitiyValidator
.checkMaxEntitiesFeature(feature)
.toRight(badRequest("You have already reached the limit"))
} yield toJson(feature.update).toString
// Turn the either into an OK/BadRequest
featureEither match {
case Right(update) => Ok(update)
case Left(error) => BadRequest(toJson(error))
}
}
Explanations
Error handling
I'm not sure how much you know about either but they are pretty similar in behaviour as Validation presented by Edmondo or Try object from the scala library. Main difference between those object regard their capability and behaviour with errors, but beside that they all can be mapped and flat mapped the same way.
You can also see that I use toRight to immediately convert the option into Either instead of doing it at the end. I see that java dev have the reflex to throw exception as far as they physically can, but they mostly do so because the try catch mechanism is unwieldy: in case of success, to get data out of a try block you either need to return them or put them in a variable initialized to null out of the block. But this is not the case is scala: you can map a try or an either, so in general, you get a more legible code if you turn results into error representation as soon as have identified it as they are identified as incorrect.
For comprehension
I also know that dev discovering scala are often quite puzzled by for comprehension. This is quite understandable as in most other language, for is only used for iteration over collections while is scala, it seem to use usable on a lot of unrelated types. In scala for is actually more nicer way to call the function flatMap. The compiler may decide to optimize it with map or foreach but it remain correct assume that you will get a flatMap behavior when you use for.
Calling flatMap on a collection will behave like the for each would in other language, so scala for may be used like a standard for when dealing with collection. But you can also use it on any other type of object that provide an implementation for flatMap with the correct signature. If your OK/BadRequest also implement the flatMap, you may be able to use in directly in the for comprehension instead of usong an intermediate Either representation.
For the people are not at ease with using for on anything that do not look like a collection, here is is how the function would look like if explicitly using flatMap instead of for :
def save() = CORSAction { request =>
def badRequest(message: String) = Error(status = BAD_REQUEST, message)
val updateEither = request.body.asJson.toRight(badRequest("Expecting JSON data"))
.flatMap { json =>
json
.asOpt[Feature]
.toRight(badRequest("Invalid feature entity"))
}
.flatMap { feature =>
MaxEntitiyValidator
.checkMaxEntitiesFeature(feature)
.map(_ => feature)
.toRight(badRequest("You have already reached the limit"))
}
.map { rs =>
toJson(feature.update).toString
}
featureEither match {
case Right(update) => Ok(update)
case Left(error) => BadRequest(toJson(error))
}
}
Note that in term of parameter scope, for behave live if the function where nested, not chained.
Conclusion
I think that more than not using the right framework or the right language feature, the main issue with the code your provided is how errors are dealt with. In general, you should not write error paths as after thought that you pile up at the end of the method. If you can deal with the error immediately as they occur, that allow you to move to something else. On the contrary, the more you push them back, the more you will have code with inextricable nesting. They are actually a materialization of all the pending error cases that scala expect you to deal with at some point.