Pattern matching on a Try lazy val in Scala? - scala

Is it possible to pattern match on a lazy val, declared as a Try, like this?
lazy val kafkaProducer: Try[producer.KafkaProducer[Array[Byte], String]] = Try(kafkaProducerSettings.createKafkaProducer())
...
kafkaProducer.get match {
case Success(_) => Source.single(producerRecord()).runWith(Producer.plainSink(kafkaProducerSettings, kafkaProducer.get))
case Failure(x) => Future.failed(x)
}
I'm getting this error:
constructor cannot be instantiated to expected type;
[error] found : akka.actor.Status.Success
[error] required: org.apache.kafka.clients.producer.KafkaProducer[Array[Byte],String]
[error] case Success(_) => Source.single(producerRecord()).runWith(Producer.plainSink(kafkaProducerSettings, kafkaProducer.get))
Note, this alternative code works, but I'm not sure it's the "Scala way":
lazy val kafkaProducer: producer.KafkaProducer[Array[Byte], String] = kafkaProducerSettings.createKafkaProducer()
...
val tryAccessLazyKafkaProducer = Try(kafkaProducer)
if (tryAccessLazyKafkaProducer.isSuccess) {
Source.single(producerRecord()).runWith(Producer.plainSink(kafkaProducerSettings, kafkaProducer))
} else {
Future.failed(tryAccessLazyKafkaProducer.failed.get)
}

It's definitely possible, you just have the wrong Success type imported:
found : akka.actor.Status.Success
You need scala.util.Success instead
One thing you mustn't do is call Try.get, which will explode if the returned type is a Failure. Instead, do:
import scala.util.Success
import scala.util.Failure
kafkaProducer match {
case Success(producer) => Source.single(producerRecord()).runWith(Producer.plainSink(kafkaProducerSettings, producer))
case failure: Failure => failure
}
lazy is just a language construct which makes sure the value is only ever evaluated once. The underlying type, whether lazy or not, is still a Try which you can do what you do with it.

Related

How to narrow down ZIO Schedule environment?

There is an example of a simple API that uses ZIO effect to return None or Option[String]. I use ZIO Schedule to run the effect as long the None is returned, but limited to a certain number of times. The example is based on the code from ZIO usecases_scheduling:
import zio._
import zio.random._
import zio.duration._
import zio.console.{Console, putStrLn}
import zio.Schedule
import scala.util.{Random => ScalaUtilRandom}
object RecordAPI {
def randomId(length: Int): String =
LazyList.continually(ScalaUtilRandom.nextPrintableChar).filter(_.isLetterOrDigit).take(length).mkString
def getRecordId: Task[Option[String]] = Task.effect(
if (ScalaUtilRandom.nextInt(10) >= 7) Some(randomId(16)) else None
)
}
object ScheduleUtil {
def schedule[A]: Schedule[Random, Option[String], Option[String]] =
(Schedule.exponential(10.milliseconds) && Schedule.recurs(10)) *> Schedule.recurWhile(_.isEmpty)
}
object RandomScheduler extends scala.App {
implicit val rt: Runtime[zio.ZEnv] = Runtime.default
rt.unsafeRun {
RecordAPI.getRecordId
.repeat(ScheduleUtil.schedule)
.foldM(
ex => putStrLn(s"failed with ${ex.getMessage}"),
success => putStrLn(s"Succeeded with $success")
)
}
}
This effect below has the type ZIO[Random with clock.Clock, Throwable, Option[String]]:
RecordAPI.getRecordId.repeat(ScheduleUtil.schedule)
I would like to remove the ScheduleUtil.schedule dependency on Random by providing the Random env and to receive the effect ZIO[Any with clock.Clock, Throwable, Option[String]]:
RecordAPI.getRecordId.repeat(ScheduleUtil.schedule.provide(Random))
but I get compilation error:
[error] found : zio.random.Random.type
[error] required: zio.random.Random
[error] (which expands to) zio.Has[zio.random.Random.Service]
[error] .repeat(ScheduleUtil.schedule.provide(Random))
[error] ^
[error] one error found
What parameter should be provided to the .provide method?
Error message talks you that you tries to pass to function provide Random.type
in the line:
RecordAPI.getRecordId.repeat(ScheduleUtil.schedule.provide(Random))
Random is passed as type but provide expects instance of Random. So you can make your code compilable just replacing Random type to some it's instance:
val hasRandomService: Random = Has.apply(Random.Service.live)
val randomIdZIO: ZIO[Random, Throwable, Option[String]] =
RecordAPI.getRecordId.repeat(ScheduleUtil.schedule.provide(hasRandomService))
but if you want to get rid of ScheduleUtil.schedule maybe it's better to use Schedule.fromFunction function:
val randomIdZIOFromFunction: ZIO[Random, Throwable, Option[String]] =
RecordAPI.getRecordId.repeat(
Schedule.fromFunction(_ => if (ScalaUtilRandom.nextInt(10) >= 7) Some(randomId(16)) else None)
)

Scala tests with Either and Pattern Matching best practice

I want to test my method which returns an Either. This is how i do this:
#Test def `Test empty name is not valid`: Unit = {
val book = createBook()
val result = insertEntryToBook(book, "", "12345")
result match {
case Left(InvalidNameFormat) => true
case _ => fail()
}
}
Should i do some assert() call instead of fail() to make the test's fail message more explicit (e.g. for assertion side-to-side view)?
There is no reason to pattern match here. You can just assert the result equals the expected value wrapped in a Left().
assertEquals(Left(InvalidNameFormat), result)
In case your test fails, you get a precise error message.
ScalaTest can be used with JUnit, so consider mixing in EitherValues which should give more informative messages
import org.junit.Test
import org.scalatest.{EitherValues, Matchers}
class ExampleSuite extends Matchers with EitherValues {
#Test def matchEithers(): Unit = {
val result: Either[String, Int] = Right(42)
result.left.value should be ("Boom")
}
}
which gives
org.scalatest.exceptions.TestFailedException: The Either on which left.value was invoked was not defined as a Left.
at org.scalatest.EitherValues$LeftValuable.value(EitherValues.scala:124)
at example.ExampleSuite.matchEithers(ExampleSuite.scala:9)
...

Type mismatch in Play controller action when recovering a future

I'm having a problem to return the correct type in a scala play controller method can someone give me a hint here? I'm using for comprehantion to deal with two service methods that returns a Future, and I would like to handle elegantly the result and the errors.
What is the best practice to do this?
def registerUser = Action { implicit request =>
Logger.info("Start play actoin")
RegisterForm.form.bindFromRequest.fold(
formWithErrors => {
BadRequest(views.html.register(formWithErrors))
},
formData => {
val registerResult = for {
reCaptchaOk <- registerUserService.checkRecaptcha(formData.gRecaptchaResponse)
userId <- registerUserService.registerUser(formData) if reCaptchaOk
} yield userId
registerResult.map(
result => Redirect(routes.DashboardController.dashboard).withSession("USER_ID" -> result.toString))
.recover{
e => handleRegisterError(e)
}
})
}
def handleRegisterError(cause: Throwable)(implicit request: Request[_]) : Result = {
val form = RegisterForm.form.bindFromRequest
cause match {
case dae: DataAccessException =>
val globalError = dae.getCause.asInstanceOf[PSQLException].getSQLState match {
case "23505" => GlobalMessages(Seq(GlobalMessage(Messages("errors.db.userAlreadyExists") ,ERROR)))
case _ => GlobalMessages(Seq(GlobalMessage(Messages("errors.system.error"),ERROR)))
}
BadRequest(views.html.register(form,globalError))
case _ =>
BadRequest(views.html.register(form))
}
the error:
[error] (compile:compileIncremental) Compilation failed
[info] Compiling 1 Scala source to C:\repos\scala\SocerGladiatorWeb\target\scala-2.11\classes...
[error] C:\repos\scala\SocerGladiatorWeb\app\controllers\RegisterController.scala:56: type mismatch;
[error] found : Throwable => play.api.mvc.Result
[error] required: PartialFunction[Throwable,?]
[error] e => handleRegisterError(e)
[error] ^
[error] one error found
[error] (compile:compileIncremental) Compilation failed
Short answer
You need a partial function to recover future failures:
def handleRegisterError(implicit request: Request[_]): PartialFunction[Throwable, Result] = {
case dae: DataAccessException =>
val form = RegisterForm.form.bindFromRequest
val globalError = dae.getCause.asInstanceOf[PSQLException].getSQLState match {
case "23505" => GlobalMessages(Seq(GlobalMessage(Messages("errors.db.userAlreadyExists"), ERROR)))
case _ => GlobalMessages(Seq(GlobalMessage(Messages("errors.system.error"), ERROR)))
}
BadRequest(views.html.register(form, globalError))
case _ =>
val form = RegisterForm.form.bindFromRequest
BadRequest(views.html.register(form))
}
then change the controller code to
registerResult
.map { result =>
Redirect(routes.DashboardController.dashboard).withSession("USER_ID" -> result.toString)
}
.recover {
handleRegisterError
}
Also note that you need an async action, i.e.
def registerUser = Action.async { implicit request =>
...
}
because you are not returning a Result but a Future[Result]. You can find more about actions in Play docs.
Details
If you look at the docs of the recover method of Future (see here) you'll see that it needs a pf: PartialFunction[Throwable, U].
Partial functions are just like normal functions but they might reject some values (for instance here, the recover method does not accept all exceptions, but only those specified in the body).
Defining a partial function needs a special syntax. It's very much like pattern matching but with no match expression.
Future(someAsyncWork).recover {
case my: MyException => ....
case _ => ....
}
Here we are using a partial recover function inline, so the type will be inferred automatically but if you want to define the recover as a separate function you need to explicitly state its type.
Advanced
The partial function syntax (pattern matching with no match keyword) is very concise and handy in most situations, but sometimes you need more than that.
For instance, note that using this syntax, we had to duplicate parts of the code (val form = RegisterForm.form.bindFromRequest) in the recover function.
Although in your case there might be better solutions but you can always convert a normal function to a partial function. First you need to define a function of the type Throwable => Option[Result] and then you can use Function#unlift to convert it to the desired partial function.
Also you can directly inherit from a PartialFunction and implement its two methods (apply and isDefinedAt).

Scala/Akka Future onComplete Success compiler error

I have an actor that waits for the results of a future. Calling onComplete of the future causes a compiler error:
error: constructor cannot be instantiated to expected type
[scalac] found : akka.actor.Status.Success
[scalac] required: scala.util.Try[Iterable[Any]]
[scalac] case Success(result: List[PCBInstanceStats]) => {
[scalac] ^
Actor's receive:
case "pcbStatus" => {
val future = Future.traverse(context.children)(x => {
(x ? "reportStatus")(5 seconds)
})
future.onComplete {
case Success(result: List[PCBInstanceStats]) => {
self ! result
}
}
Not sure how to provide the right type of parameter for this.
[scalac] found : akka.actor.Status.Success
That means the compiler sees your Success and thinks it's an akka.actor.Status.Success, when really you mean a scala.util.Success. You probably have an import somewhere that is importing the akka Success class.
Either remove the import for akka.actor.Status.Success, or resolve the ambiguity by either fully-qualifying the class, or using an import alias, e.g.
import scala.util.{Success => ScalaSuccess}
future.onComplete {
case ScalaSuccess(result) => ...
// or
case scala.util.Success(result) => ...
}

Interpreter result type not found despite importing it

I use Scala 2.9.1. I have a simple scala "interpreter":
import scala.tools.nsc.interpreter.IMain
import scala.tools.nsc.interpreter.Results.Result
import scala.tools.nsc.interpreter.Results.Success
object App {
def main(args: Array[String]) {
val interpreter = new IMain
val result:Result = interpreter.interpret(args(0))
result.toString() match {
case "Success" =>
{
var success = result.asInstanceOf[Success]
println(success.productElement(0))
};
case _ => println("very bad result");
}
}
}
When i try to compile it (maven) i get:
[ERROR] /home/koziolek/workspace/dsi/src/main/scala/pl/koziolekweb/scala/dsi/App.scala:15: error: not found: type Success
[INFO] var success = result.asInstanceOf[Success]
As you can see, the compiler said that there is no type Success, although I imported it.
Success is an object, not a class, you would need to cast it to its singleton type result.asInstanceOf[Success.type]. Obviously you are trying to work around not knowing how to do the pattern match. That would allow you to get the right result without casting:
import tools.nsc.interpreter.Results._
result match {
case Success => "yes!"
case Error => "no..."
case Incomplete => "you missed something"
}
If you want to get the resulting value of the interpreted expression in the case of success, see my reply in this post for more details.