2

Here is code example:

  type FailFast[A] = Either[List[String], A]
  import cats.instances.either._
  def f1:ReaderT[FailFast, Map[String,String], Boolean] = ReaderT(_ => Right(true))
  def f2:ReaderT[FailFast, Map[String,String], Boolean] = ReaderT(_ => Right(true))

  def fc:ReaderT[FailFast, Map[String,String], Boolean] =
    for {
      b1 <- f1
      if (b1)
      b2 <- f2
    } yield b2

The error is:

Error:(17, 13) value withFilter is not a member of cats.data.ReaderT[TestQ.this.FailFast,Map[String,String],Boolean] b1 <- f1

How can I compose f1 with f2. f2 must be applied only if f1 returns Right(true). I solved it via:

  def fc2:ReaderT[FailFast, Map[String,String], Boolean] =
    f1.flatMap( b1 => {
      if (b1)
        f2
      else ReaderT(_ => Right(true))
    })

But I hope there is a more elegant solution.

Alexandr
  • 9,213
  • 12
  • 62
  • 102
  • use `match/case`, on [Scala Either map Right or return Left](https://stackoverflow.com/questions/34545394/scala-either-map-right-or-return-left) thread (even though the question is different) there are details about how this could be done –  Mar 27 '19 at 14:08
  • @ValentinCarnu, can you please show how it might look? – Alexandr Mar 27 '19 at 14:53

1 Answers1

2
  1. The huge ReaderT[FailFast, Map[String, String], Boolean] type is annoying. I replaced it by ConfFF-shortcut ("map-configured fail-fast"); You probably can find a better name for that.
  2. You can still use the for-comprehension syntax, if you want.
  3. No need to write out all the _ => and Right(...) every time, just use appropriate pure from applicative.

Thus, your fc2 becomes:

  def fc3: ConfFF[Boolean] =
    for {
      b1 <- f1
      b2 <- if (b1) f2 else true.pure[ConfFF]
    } yield b2

Full code:

import scala.util.{Either, Left, Right}
import cats.instances.either._
import cats.data.ReaderT
import cats.syntax.applicative._

object ReaderTEitherListExample {

  type FailFast[A] = Either[List[String], A]
  /** Shortcut "configured fail-fast" */
  type ConfFF[A] = ReaderT[FailFast, Map[String, String], A]

  def f1: ConfFF[Boolean] = ReaderT(_ => Right(true))
  def f2: ConfFF[Boolean] = ReaderT(_ => Right(true))

  def fc3: ConfFF[Boolean] =
    for {
      b1 <- f1
      b2 <- if (b1) f2 else true.pure[ConfFF]
    } yield b2
}
Andrey Tyukin
  • 43,673
  • 4
  • 57
  • 93
  • definitely, it looks more elegant. My main mistake was that I did not include the `else` logic. Thank you for such clear solution. – Alexandr Mar 27 '19 at 16:52
  • 2
    @Alexandr No, your mistake was that you attempted to use a `for`-comprehension guard on a type that had no `withFilter`. The `if` that I used is an ordinary expression on the right hand side of a `<-`, which evaluates to a usual `for`-comprehension generator. These two `if`s are quite different. The `for`-comprehension guards *cannot* have an `else` part. The `if-else` expressions *must* have an `else` part. – Andrey Tyukin Mar 27 '19 at 16:54
  • 1
    may I ask you also to have a look at [https://stackoverflow.com/questions/55382975/how-to-inject-dependencies-through-scala-reader-from-java-code] "Вопрос жизни и смерти:)" – Alexandr Mar 27 '19 at 17:14