Scala: понимание с охраной внутри читателя

Вот пример кода:

  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

Ошибка:

Ошибка: (17, 13) значение withFilter не является членом cats.data.ReaderT [TestQ.this.FailFast, Map [String, String], Boolean] b1 ‹- f1

Как я могу составить f1 с f2. f2 должен применяться только в том случае, если f1 возвращает Right (true). Я решил это с помощью:

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

Но я надеюсь, что есть более элегантное решение.


person Alexandr    schedule 27.03.2019    source источник
comment
используйте match/case в потоке Scala либо map Right, либо return Left (даже хотя вопрос в другом) есть подробности о том, как это можно было сделать   -  person    schedule 27.03.2019
comment
@ValentinCarnu, не могли бы вы показать, как это может выглядеть?   -  person Alexandr    schedule 27.03.2019


Ответы (1)


  1. Огромный шрифт ReaderT[FailFast, Map[String, String], Boolean] раздражает. Я заменил его на ConfFF-shortcut («быстро настроить карту»); Вы, наверное, сможете найти для этого более подходящее название.
  2. Вы по-прежнему можете использовать синтаксис for-computing, если хотите.
  3. Не нужно каждый раз выписывать все _ => и Right(...), просто используйте соответствующий pure из applicative.

Таким образом, ваш fc2 становится:

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

Полный код:

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
}
person Andrey Tyukin    schedule 27.03.2019
comment
определенно, выглядит более элегантно. Моя главная ошибка заключалась в том, что я не включил логику else. Спасибо за такое четкое решение. - person Alexandr; 27.03.2019
comment
@Alexandr Нет, ваша ошибка заключалась в том, что вы пытались использовать for-защиту понимания для типа, у которого не было withFilter. if, которое я использовал, является обычным выражением в правой части <-, которое оценивается как обычный генератор понимания for. Эти два if совершенно разные. Стражи for понимания не могут иметь else часть. if-else выражения должны иметь else часть. - person Andrey Tyukin; 27.03.2019
comment
Могу я попросить вас также взглянуть на заголовок [stackoverflow.com/questions/55382975/ Вопрос жизни и смерти :) - person Alexandr; 27.03.2019