I´m learning the monad IO of scalaZ and I cannot understand how catchAll and catchSome operators works.
I was expecting so see a behave like the onError or onErrorrResumeNext of RxJava, but instead is not catching the throwable, and it´s just breaking the test and throwing the NullPointerException..
Here my two examples
#Test
def catchAllOperator(): Unit = {
val errorSentence: IO[Throwable, String] =
IO.point[Throwable, String](null)
.map(value => value.toUpperCase())
.catchAll(error => IO.fail(error))
println(unsafePerformIO(errorSentence))
}
And catchSome example
#Test
def catchSomeOperator(): Unit = {
val errorFunction = new PartialFunction[Throwable /*Entry type*/ , IO[Throwable, String] /*Output type*/ ] {
override def isDefinedAt(x: Throwable): Boolean = x.isInstanceOf[NullPointerException]
override def apply(v1: Throwable): IO[Throwable, String] = IO.point("Default value")
}
val errorSentence = IO.point[Throwable, String](null)
.map(value => value.toUpperCase())
.catchSome(errorFunction)
println(unsafePerformIO(errorSentence))
}
Any idea what I´m doing wrong?.
Regards
Related
Just trying to propagate my tracing context inside Kleisli as it was done originally in the next tutorial.
object TraceLogger {
def log(msg: String): Kleisli[IO, UUID, Unit] = Kleisli { traceId => IO(println(s"[$traceId] $msg")) }
}
trait ServiceStub {
def request(arg: String): Kleisli[IO, UUID, _]
}
trait ClientStub {
def get(arg: String): Kleisli[IO, UUID, _]
}
case class FirstServiceExample(clientStub: ClientStub) extends ServiceStub {
override def request(arg: String): Kleisli[IO, UUID, _] = Kleisli { (context: UUID) =>
val requestComputation = clientStub.get("calling second service!")
TraceLogger.log(arg)
requestComputation(context)
}
}
case class FirstClientExample(service: FirstServiceExample) {
def request(): IO[_] = {
val traceId = UUID.randomUUID()
service.request("root!").run(traceId)
}
}
And now I need to run execution:
val exampleClientStub = new ClientStub() {
override def get(arg: String): Kleisli[IO, UUID, _] = Kleisli.ask
}
val exampleClientService = FirstServiceExample(exampleClientStub)
FirstClientExample(exampleClientService).request().unsafeRunSync()
But, unfortunately, I don't see any logs here. Would you kindly help me to find an issue?
TraceLogger.log(arg) This returns an IO which is just a description of computation; it is doing nothing.
And since you just leave that value alone it is equivalent to just having a 1 in the middle of your code, it is simply discarded.
You need to chain your IOs together to create new IOs that represent "do this and then do that", that is basically what the flatMap method does.
Kleisli { (context: UUID) =>
val requestComputation = clientStub.get("calling second service!")
TraceLogger.log(arg)(context) >> // >> is equivalent to flatMap(_ => )
requestComputation(context)
}
(There is probably a better way to write this, I am not used to Kliesli)
Fabio's series on "Programas as Values" may be very useful: https://systemfw.org/archive.html
I have this repository :
trait TrainRepository {
def get(trainId: TrainId): IO[Option[Train]]
def getAll: IO[List[Train]]
def save(train: Train): IO[Train]
}
I would like to provide an in-memory implementation using a State Monad.
But I am stuck :
if I extend the trait, I will be stuck with the types IO[...] so I believe that I would not be able to use a State Monad.
do I need to use a natural transformation?
do I need to use a Free Monad? (I would rather not)
How would you do that?
EDIT to give a little bit more context:
trait TrainRepository[F[_]] {
def get(trainId: TrainId): F[Option[Train]]
def save(train: Train): F[Train]
}
class TrainService[F[_]](repository: TrainRepository[F])(implicit monad: Monad[F]) {
def reservation(id: TrainId): F[Train] =
for{
train <- repository.get(id)
updatedTrain <- train match {
case None => monad.pure("test") // return error here
case Some(train) => monad.pure(train.bookSeat)
}
_ <- repository.save(updatedTrain)
} yield updatedTrain
}
type TrainStateRepository[A] = State[Map[TrainId, Train], A]
val inMemoryTrainRepository = new TrainRepository[TrainStateRepository] {
override def get(trainId: TrainId): TrainStateRepository[Option[Train]] = ???
override def save(train: Train): TrainStateRepository[Train] = ???
}
val postgresTrainRepository = new TrainRepository[IO] {
override def get(trainId: TrainId): IO[Option[Train]] = ???
override def save(train: Train): IO[Train] = ???
}
val testTrainService = new TrainService[IO](inMemoryTrainRepository)
// The error is here ^^^^
// I cannot mix IO and State
val prodTrainService = new TrainService[IO](postgresTrainRepository)
You can introduce a type parameter in order to abstract over your monad:
trait TrainRepository[F[_]] {
def get(trainId: TrainId): F[Option[Train]]
def getAll: F[List[Train]]
def save(train: Train): F[Train]
}
Then your implementation with state monad can look like
type TrainsState[A] = State[Map[TrainId, Train], A]
class StateTrainRepository extends TrainRepository[TrainsState] {
override def get(trainId: TrainId): TrainsState[Option[Train]] = State.inspect(_.get(trainId))
override def getAll: TrainsState[List[Train]] = State.inspect(_.values.toList)
override def save(train: Train): TrainsState[Train] =
State.modify[Map[TrainId, Train]](m => m + (train.id -> train)) *> State.pure(train)
}
My old code looks something like below, where all db calls blocking.
I need help converting this over to using Futures.
def getUserPoints(username: String): Option[Long]
db.getUserPoints(username) match {
case Some(userPoints) => Some(userPoints.total)
case None => {
if (db.getSomething("abc").isEmpty) {
db.somethingElse("asdf") match {
case Some(pointId) => {
db.setPoints(pointId, username)
db.findPointsForUser(username)
}
case _ => None
}
} else {
db.findPointsForUser(username)
}
}
}
}
My new API is below where I am returning Futures.
db.getUserPoints(username: String): Future[Option[UserPoints]]
db.getSomething(s: String): Future[Option[Long]]
db.setPoints(pointId, username): Future[Unit]
db.findPointsForUser(username): Future[Option[Long]]
How can I go about converting the above to use my new API that uses futures.
I tried using a for-compr but started to get wierd errors like Future[Nothing].
var userPointsFut: Future[Long] = for {
userPointsOpt <- db.getUserPoints(username)
userPoints <- userPointsOpt
} yield userPoints.total
But it gets a bit tricky with all the branching and if clauses and trying to convert it over to futures.
I would argue that the first issue with this design is that the port of the blocking call to a Future should not wrap the Option type:
The blocking call:
def giveMeSomethingBlocking(for:Id): Option[T]
Should become:
def giveMeSomethingBlocking(for:Id): Future[T]
And not:
def giveMeSomethingBlocking(for:Id): Future[Option[T]]
The blocking call give either a value Some(value) or None, the non-blocking Future version gives either a Success(value) or Failure(exception) which fully preserves the Option semantics in a non-blocking fashion.
With that in mind, we can model the process in question using combinators on Future. Let's see how:
First, lets refactor the API to something we can work with:
type UserPoints = Long
object db {
def getUserPoints(username: String): Future[UserPoints] = ???
def getSomething(s: String): Future[UserPoints] = ???
def setPoints(pointId:UserPoints, username: String): Future[Unit] = ???
def findPointsForUser(username: String): Future[UserPoints] = ???
}
class PointsNotFound extends Exception("bonk")
class StuffNotFound extends Exception("sthing not found")
Then, the process would look like:
def getUserPoints(username:String): Future[UserPoints] = {
db.getUserPoints(username)
.map(userPoints => userPoints /*.total*/)
.recoverWith{
case ex:PointsNotFound =>
(for {
sthingElse <- db.getSomething("abc")
_ <- db.setPoints(sthingElse, username)
points <- db.findPointsForUser(username)
} yield (points))
.recoverWith{
case ex: StuffNotFound => db.findPointsForUser(username)
}
}
}
Which type-checks correctly.
Edit
Given that the API is set in stone, a way to deal with nested monadic types is to define a MonadTransformer. In simple words, let's make Future[Option[T]] a new monad, let's call it FutureO that can be composed with other of its kind. [1]
case class FutureO[+A](future: Future[Option[A]]) {
def flatMap[B](f: A => FutureO[B])(implicit ec: ExecutionContext): FutureO[B] = {
val newFuture = future.flatMap{
case Some(a) => f(a).future
case None => Future.successful(None)
}
FutureO(newFuture)
}
def map[B](f: A => B)(implicit ec: ExecutionContext): FutureO[B] = {
FutureO(future.map(option => option map f))
}
def recoverWith[U >: A](pf: PartialFunction[Throwable, FutureO[U]])(implicit executor: ExecutionContext): FutureO[U] = {
val futOtoFut: FutureO[U] => Future[Option[U]] = _.future
FutureO(future.recoverWith(pf andThen futOtoFut))
}
def orElse[U >: A](other: => FutureO[U])(implicit executor: ExecutionContext): FutureO[U] = {
FutureO(future.flatMap{
case None => other.future
case _ => this.future
})
}
}
And now we can re-write our process preserving the same structure as the future-based composition.
type UserPoints = Long
object db {
def getUserPoints(username: String): Future[Option[UserPoints]] = ???
def getSomething(s: String): Future[Option[Long]] = ???
def setPoints(pointId: UserPoints, username:String): Future[Unit] = ???
def findPointsForUser(username: String): Future[Option[Long]] = ???
}
class PointsNotFound extends Exception("bonk")
class StuffNotFound extends Exception("sthing not found")
def getUserPoints2(username:String): Future[Option[UserPoints]] = {
val futureOpt = FutureO(db.getUserPoints(username))
.map(userPoints => userPoints /*.total*/)
.orElse{
(for {
sthingElse <- FutureO(db.getSomething("abc"))
_ <- FutureO(db.setPoints(sthingElse, username).map(_ => Some(())))
points <- FutureO(db.findPointsForUser(username))
} yield (points))
.orElse{
FutureO(db.findPointsForUser(username))
}
}
futureOpt.future
}
[1] with acknowledgements to http://loicdescotte.github.io/posts/scala-compose-option-future/
Can't I use a generic on the unapply method of an extractor along with an implicit "converter" to support a pattern match specific to the parameterised type?
I'd like to do this (Note the use of [T] on the unapply line),
trait StringDecoder[A] {
def fromString(string: String): Option[A]
}
object ExampleExtractor {
def unapply[T](a: String)(implicit evidence: StringDecoder[T]): Option[T] = {
evidence.fromString(a)
}
}
object Example extends App {
implicit val stringDecoder = new StringDecoder[String] {
def fromString(string: String): Option[String] = Some(string)
}
implicit val intDecoder = new StringDecoder[Int] {
def fromString(string: String): Option[Int] = Some(string.charAt(0).toInt)
}
val result = "hello" match {
case ExampleExtractor[String](x) => x // <- type hint barfs
}
println(result)
}
But I get the following compilation error
Error: (25, 10) not found: type ExampleExtractor
case ExampleExtractor[String] (x) => x
^
It works fine if I have only one implicit val in scope and drop the type hint (see below), but that defeats the object.
object Example extends App {
implicit val intDecoder = new StringDecoder[Int] {
def fromString(string: String): Option[Int] = Some(string.charAt(0).toInt)
}
val result = "hello" match {
case ExampleExtractor(x) => x
}
println(result)
}
A variant of your typed string decoder looks promising:
trait StringDecoder[A] {
def fromString(s: String): Option[A]
}
class ExampleExtractor[T](ev: StringDecoder[T]) {
def unapply(s: String) = ev.fromString(s)
}
object ExampleExtractor {
def apply[A](implicit ev: StringDecoder[A]) = new ExampleExtractor(ev)
}
then
implicit val intDecoder = new StringDecoder[Int] {
def fromString(s: String) = scala.util.Try {
Integer.parseInt(s)
}.toOption
}
val asInt = ExampleExtractor[Int]
val asInt(Nb) = "1111"
seems to produce what you're asking for. One problem remains: it seems that trying to
val ExampleExtractor[Int](nB) = "1111"
results in a compiler crash (at least inside my 2.10.3 SBT Scala console).
I have been looking at this How are Scala Futures chained together with flatMap? and the corresponding article as well on translating for comprehension. I am slowly adding stuff to my for comprehension and am stuck as I guess the code I thought would translate to is not correct.
Here I have a runProgram and runProgram2 which I thought would be equivalent and are not because runProgram2 does not compile. Can someone explain the equiavalent of this for comprehension...
NOTE: yes I know that future.flatMap is typically for collapsing Future[Future[String]] but this is a trimmed down version of my file(perhaps I trimmed it down too far).
def main(args: Array[String]) = {
val future1: Future[String] = runMyProgram()
//val future2: Future[String] = runMyProgram2()
}
def runMyProgram() : Future[String] = {
val future = serviceCall()
future.flatMap(processAllReturnCodes)
}
// def runMyProgram2() : Future[String] = {
// val future = serviceCall()
// for {
// result <- future
// } yield processAllReturnCodes(result)
// }
def processAllReturnCodes(count: Int) : Future[String] = {
val promise = Promise.successful("done")
promise.future
}
def serviceCall() : Future[Int] = {
val promise = Promise.successful(5)
promise.future
}
def serviceCall2() : Future[String] = {
val promise = Promise.successful("hithere")
promise.future
}
This for comprehension:
for {
result <- future
} yield processAllReturnCodes(result)
Is being translated to this:
val t: Future[Future[String]] = future.map(result => processAllReturnCodes(result))
Comprehension is really only syntactic sugar for map and flatMap, with flatMap you can flatten the future nesting:
val u: Future[String] = future.flatMap(result => processAllReturnCodes(result))
The difference lies in the signatures:
def map[S](f: T => S)(implicit executor: ExecutionContext): Future[S]
So map takes a function form T to S and wraps the S in a future, the problem is that here your S is a Future[String] which is wrapped in another future giving Future[Future[String]], flatMap instead:
def flatMap[S](f: T => Future[S])(implicit executor: ExecutionContext): Future[S]
Takes a function from T to Future[S] and returns that future, in your case your method already returns a future and it's a valid parameter for flatMap.