Scala - Return value from an anonymous function - scala

I've a named function that as an Either[A, List[B]] as return type. Inside that named function, I have a anonymous function that I'd like to make it return a value to the named function. Instead I keep getting the error "Non local returns are no longer supported; use scala.util.control.NonLocalReturns instead".
I did some research but wasn't able to find a solution.
Am I missing something? Is there any to workaround this problem?
Here's my code
def readEvents(SchedulingNode: Node): Either[Error, List[Event]] =
val events: List[Either[Error, Event]] = SchedulingNode.child
.filter(n => n.label == EVENT_NODE_NAME)
.map(n => {
val head: Head = n.attribute(HEAD_ATTRIBUTE) match {
case None => return Left(EmptyProperty("head")) //Error occurs here
case Some(s) => Head.from(s.toString()) match
case Left(e: Error) => return Left(e); //Here also
case Right(h: Head) => h
}
Event.from(head, 1) match {
case Left(e: Error) => Left(e);
case Right(ev: Event) => Right(ev)
}
}).toList
//Rest of the code...

Related

How to put error logs in code when using pattern matching?

I have a function convert date time to epoch, and I want to put some logs where it failed. Such as case _ => and .getOrElse(JNothing) , how can I do that?
def time_to_epoch(params: List[JValue]): JValue = params match {
case JString(timestamp) :: JString(pattern) :: JString(timezone) :: Nil =>
Try {
***
}.getOrElse(JNothing) // for saying something similar to can't convert time to epoch
case _ =>
JNothing // Add a (ErrorMessage(xxxxxxx)) for saying number of parameters are invalid and what are expected inline function forma
}
def time_to_epoch(params: List[JValue]): JValue = params match {
case JString(timestamp) :: JString(pattern) :: JString(timezone) :: Nil =>
Try {
//***
}.getOrElse {
log.error("Cant ...")
JNothing
}
case _ =>
log.error("Number ...")
JNothing
}
For functional programming solution, there's treelog you can use, but it's really not necessary in your case, unless you need the function to be strictly pure.
If time_to_epoch can fail then it is better to return a Try rather than a special failure value like JNothing.
case class MyException(msg: String) extends Exception
def time_to_epoch(params: List[JValue]): Try[JValue] = params match {
case JString(timestamp) :: JString(pattern) :: JString(timezone) :: Nil =>
Try {
???
}
case _ =>
Failure(MyException("Invalid parameters"))
}
You can print the error information using match:
time_to_epoch(???) match {
case Failure(e) =>
println(s"Operation failed: $e")
case _ =>
}
The advantage of this is that it is easy to chain multiple Try operations without having to test for failure at each step. It allows the code to focus on the main code path while still handling errors correctly.

Testing exceptions in Scala

I want to test (in Intellij Idea testing framework) the following code:
def eval(t: Term): Term = t match {
case t if isVal(t) => t
case If(t1,t2,t3) if True == eval(t1) => eval(t2)
case If(t1,t2,t3) if False == eval(t1) => eval(t3)
case Succ(t) if isNum(eval(t)) => Succ(eval(t))
case Pred(t) => eval(t) match {
case Zero => Zero
case Succ(v) if isNum(v) => v
case _ => throw TermIsStuck(Pred(eval(t)))
}
case IsZero(t) => eval(t) match {
case Zero => True
case Succ(v) if isNum(v) =>False
case _ => throw TermIsStuck(IsZero(eval(t)))
}
case _ => throw TermIsStuck(t)
}
For a reference you can see this repository. So I wrote the following test:
test("testEval") {
Arithmetic.term(new Arithmetic.lexical.Scanner("if iszero pred pred 2 then if iszero 0 then true else false else false")) match {
case Arithmetic.Success(res,next) => assert(eval(res) == True)
case Arithmetic.Failure(msg,next) => assert(false)
}
Arithmetic.term(new Arithmetic.lexical.Scanner("pred succ succ succ false")) match {
case Arithmetic.Success(res,next) => assert(Arithmetic.eval(res) == TermIsStuck(Succ(False)))
case Arithmetic.Failure(msg,next) => assert(false)
}
}
Getting the error:
Succ(False)
fos.Arithmetic$TermIsStuck: Succ(False) ...
How can I test this catching the error? Indeed this test should be passed...
You are in Scala you are not supposed to throw exception around, but in scalatest you can do something like this:
a[yourException.type] should be thrownBy yourFunction()
You can do a better version of that,
case class Error(exception: Exception){
throw exception
???
}
Or something like
case object Error
And then you check it like:
Error shouldBe yourFunction()
Looks like you're not using any widespread testing framework for scala (such as scalatest or specs2) - an obvious recommendation would be to use some.
However, if you'd like to stick with "no-framework" approach, one option would be to use good old try .. catch:
val term = Arithmetic.term(
new Arithmetic.lexical.Scanner("pred succ succ succ false")
)
try {
val evaluated = term match {
case Arithmetic.Success(res,next) => Arithmetic.eval(res)
case Arithmetic.Failure(msg,next) => assert(false)
}
} catch {
case TermIsStuck(Succ(False)) => // do nothing, or explicitly mark test as successful
case _ => assert(false) // any other exception should fail the test
}
However, I think the implementation is partially broken - it never returns Arithmetic.Failure, but throws exception. I think the intent of Arithmetic.Failure was exactly to capture such cases. If that's the case, then better implementation would be to replace throw TermIsStuck with Arithmetic.Failure(TermIsStuck(...)).

How to pass method of String as callable parameter?

Assume I have a function like:
private def throwUnlessValid[T](func: (String => T)): T = {
try {
func()
} catch {
case _: Throwable => throw new Error(s"""string: "${s}" is not a numeric""")
}
}
And I want to call it like:
implicit val intParser: TryParser[Int] = (s: String) => {
s.trim.isEmpty match {
case true => 0
case false => throwUnlessValid[Int](s.toInt)
}
}
This will fail as the result of s.toInt, which is the Int. Even though I want to pass in the callable function to be called in the other method.
How can I achieve this?
The syntax is _.toInt, which is a shorthand for s => s.toInt (or without type inference, (s: String) => s.toInt). Generally, search for "scala lambdas" for a lot more information on this.
But the problem is that you call func() without passing a string to it. There's also s in throw new Error(s"""string: "${s}" is not a numeric"""); if s is in your scope, you should have func(s) instead. Or maybe you want
private def throwUnlessValid[T](func: () => T): T = { ... }
throwUnlessValid(() => s.toInt)
Using by-name parameters, you can even write
private def throwUnlessValid[T](func: => T): T = { ... }
throwUnlessValid(s.toInt)
You can pass a callable function by using _.toInt, which is shorthand for the function x => x.toInt.
However this isn't going to help because your throwUnlessValid function doesn't work, for two reasons:
You don't give a String argument to func
You attempt to print s which is not a visible to this function
You have two choices:
Pass s to throwUnlessValid and give it to func
Make func into a => T parameter and remove the references to s from the Error string.
You need to fix your throwUnlessValid function.
Currently it accepts as argument a function which takes a string as argument:
throwUnlessValid[T](func: (String => T))
However then you are trying to call it without any argument: func() which obviously won't work. Besides that you also have reference to nonexisting variable s: ${s} which also should produce error.
In order to fix the throwUnlessValid function you can pass toInt as by-name argument:
private def throwUnlessValid[T](code: => T): T = {
try {
code
} catch {
case ex: Throwable => throw new Error(s"""string: "${ex.getMessage}" is not a numeric""")
}
}
In other words you don't need to change your existing intParser implementation (including throwUnlessValid[Int](s.toInt) call), you only need to change implementation of throwUnlessValid).
If i understant correctly, you are looking for this.
Actually the problem is that you are not passing string to your function (func) hence it's not working.
import scala.util.{Failure, Success, Try}
object Solution1 extends App {
private def throwUnlessValid[T](func: (String => T)): T = {
Try {
func("1")
} match {
case Success(_) => //do Something which return T
case Failure(ex) => throw new Error(s"""string: "${ex}" is not a numeric""")
}
}
implicit val intParser: String => Int = (s: String) => {
if (s.trim.isEmpty) {
0
} else {
throwUnlessValid[Int](_ => s.toInt)
}
}
}

Play Scala 2.4 linking asynchronous calls

I'm trying to figure out how to link multiple asynchronous calls and return a result. I am currently trying to asynchronously user data first, and update user data asynchronously and return result, but it seems like it is not working :(
i used map { result => Ok(result)}, but play still thinks that I am returning an object. any help?
def updateUserData() = Action.async { implicit request =>
updateUserForm.bindFromRequest.fold(
errors => Future.successful(BadRequest(views.html.authenticated.settings.settings_hero(errors, Option(""), Option("")))),
{
case (userData) =>
request.session.get("email") match {
case Some(email) =>
getUser(email, userData.curent_password) map { userCheck =>
if (userCheck) {
updateUserOnService(email, userData.f_name, userData.l_name, userData.new_password) map { result =>
Ok("please")
}
//val e = updateUserOnService(email, userData.f_name, userData.l_name, userData.new_password) map {result => Ok("")}
// user is valid now update the user data
// call removeAuth to log out
// redirect to home
///Ok (updateUserOnService(email, userData.f_name, userData.l_name, userData.new_password) map { result => result})
//Redirect(routes.settings.index()).addingToSession("email" -> email)
} else {
BadRequest(views.html.authenticated.settings.settings_hero(updateUserForm.bindFromRequest.withGlobalError(Messages("error.login", email)), Option(""), Option("")))
}
}
}
})
}
The main part that i am having issue is this part. I think it is matter of some syntax. Could someone help?
Thanks
updateUserOnService(email, userData.f_name, userData.l_name, userData.new_password) map { result =>
Ok("please")
}
The issue is with your types and that they don't match up with the required ones.
.fold has to result in Future[Result] in both branches (the error and the success ones).
In the successful form bind branch you have this:
case (userData) => ... // The ... must evaluate to Future[Result]
Looking at your first operation we see:
request.session.get("email") match {
case Some(email) => ...
}
One big issue here is that the None case is not handled! (but this is does not cause the types not matching up). Having something like the following will solve this: case None => Future.successful(BadRequest(...))
So moving on: in the Some you have the following:
getUser(email, userData.curent_password) map { userCheck =>
if (userCheck) {
updateUserOnService(email, userData.f_name, userData.l_name, userData.new_password) map { result =>
Ok("please")
}
} else {
BadRequest(views.html.authenticated.settings.settings_hero(updateUserForm.bindFromRequest.withGlobalError(Messages("error.login", email)), Option(""), Option("")))
}
}
This is where the issue is:
getUser will return with a Future[X] and when you map over it you will have Future[Y] where Y will be what userCheck => ... evaluates to.
In this case the types are totally mixed up, since when you do if(usercheck) on the true branch you have Future[Result] on the false branch you have Result. So the types don't align on both branches which is a big issue and the compiler will infer Any from this.
To fix this, in the false branch create a future: Future.successful(BadRequest(....))
Ok, now that we fixed the most inner type issues, let's start going backwards. Inside we have Future[Result], if we go back one level (before the getUser()) then we will have Future[Future[Result]]. Again this is not what we want, because we need Future[Result].
The solution to this is to flatMap instead of map, because with flatMap when you need to return with the same container type and it flattens it. A quick example to understand this:
Seq(1, 2, 3).flatMap(i => Seq(i, i))
// res0: Seq[Int] = List(1, 1, 2, 2, 3, 3)
In the case of Futures:
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
Future(1).flatMap(i => Future(i*2))
// res1: scala.concurrent.Future[Int] = [....]
So we see that we don't have double nesting, but just a single Future.
Going back to your example this would be my updated code that would work better:
def updateUserData() = Action.async { implicit request =>
updateUserForm.bindFromRequest.fold(
errors => Future.successful(BadRequest(views.html.authenticated.settings.settings_hero(errors, Option(""), Option("")))),
{
case (userData) =>
request.session.get("email") match {
case Some(email) =>
getUser(email, userData.curent_password).flatMap { userCheck =>
if (userCheck) {
updateUserOnService(email, userData.f_name, userData.l_name, userData.new_password) map { result =>
Ok("please")
}
} else {
Future.successful(BadRequest(views.html.authenticated.settings.settings_hero(updateUserForm.bindFromRequest.withGlobalError(Messages("error.login", email)), Option(""), Option(""))))
}
}
case None => Future.successful(BadRequest) // FIXME: Implement as you wish
}
})
}

return type for findAndModify with futures

Say I'm using an async library to read and write data. If a failure, I get an error message as a string, otherwise the data. Is there a better way to represent the return type than Future[Either[String, Future[Data]]]?
Possible implementation:
def createData(name: String): Future[Either[String, Future[Data]]] = {
dataDAO.findOneByName(name).flatMap {
case Some(data) =>
Future.successful(Left(s"Data with name already exists. $name"))
case None =>
val data = Data.createFromName(name)
dataDAO.save(data).map {
lastError =>
data
}.right.futureSuccessful
}
}
Your return type should be Future[Either[String, Data]]
To archive this you need to change your second case to:
case None =>
val data = Data.createFromName(name)
dataDAO.save(data).map {
_ => Right(data)
}
I even would improve your error type from String to e.g. CreateDataError, so your return type would be Future[Either[CreateDataError, Data]] and your first case would be
// Having somewhere decalred
trait CreateDataError
object DataAlreadyExistsError
//...
//...
case Some(data) =>
Future.successful(Left(DataAlreadyExistsError))