Совпадение с тем, что результат выполнения вычисления является недопустимым для понимания - PullRequest
0 голосов
/ 28 февраля 2019

Я нахожусь внутри для понимания, где мы сочетаем несколько операций вместе.Когда каждый шаг завершен, мне нужно либо выдать ошибку, используя MonadError.raiseError, либо перейти к следующему шагу, если он действителен.

def myFunc[F[_]: Monad](input: Input)(implicit err: MonadError) = {
  for {
    stepAResult <- runStepA(input)
    if (stepAResult.isInstanceOf[Invalid[_]) {
      err.raiseError(new Throwable("error 1"))
    } else {
       stepBResult<- runStepB(stepAResult.toOption.get, input)
       if (stepBResult.isInstanceOf[Invalid[_]]) {
          err.raiseError(new Throwable("error 2"))
       } else {
        stepCResult <- runStepC(stepBResult.toOption.get)
        // check for invalid here again.
      }
    }
  }
}

Это не компилируется.Мне нужно понять, есть ли способ сделать эту работу.

Ответы [ 2 ]

0 голосов
/ 01 марта 2019

Для понимания просто синтаксический сахар для последовательности flatMap вызовов.Из-за этого не просто любой код Scala может войти внутрь вашего для понимания.Следующее, что вы пытаетесь сделать, не является допустимым Scala:

//This does not compile because normal if-else statements are not valid inside a for comprehension
object Example1 {
  def f(o: Option[Int]): Option[Int] = for {
    x <- o
    if (x < 0) "return some value"
    else { //attempting to continue the for comprehension
      y <- o
    }
  } yield ??? //what would we yield here?
}

Ключевое слово if для понимания используется для охранников :

object Example2 {
  def f(o: Option[Int]): Option[Int] = for {
    x <- o
    if x >= 0
  } yield x

  //f and f2 are equivalent functions
  def f2(l: Option[Int]): Option[Int] = l.filter(_ >= 0)
}

Но это не похоже на то, что вы хотите.Похоже, вы пытаетесь отслеживать исключения при выполнении каждого шага.Try Монада делает именно это и может быть использована для понимания.Обратите внимание на эквивалентный код Scala, который использует flatMap вызовы вместо для понимания.Я рекомендую написать функцию со всеми вложенными вызовами flatMap, прежде чем пытаться преобразовать ее в более симпатичный для синтаксиса понимания, если вы застряли.Проверьте этот ответ для некоторых примеров, как это сделать.

// myFunc1 is equiv to myFunc2 is equiv to myFunc3
// they only differ in syntax
object Example3 {
  import scala.util.Try

  def runStepA[A](in: A): Try[A] = ???
  def runStepB[A](in: A): Try[A] = ???
  def runStepC[A](in: A): Try[A] = ???

  def myFunc1[A](input: A): Try[A] = for {
    nonErrorResultA <- runStepA(input)
    nonErrorResultB <- runStepB(nonErrorResultA)
    nonErrorResultC <- runStepC(nonErrorResultB)
  } yield nonErrorResultC

  def myFunc2[A](input: A): Try[A] =
    runStepA(input).flatMap {
      nonErrorResultA => runStepA(nonErrorResultA).flatMap {
        nonErrorResultB => runStepB(nonErrorResultB).flatMap {
          nonErrorResultC => runStepC(nonErrorResultC)
        }
      }
    }

  def myFunc3[A](input: A): Try[A] =
    runStepA(input).flatMap {
      runStepA(_).flatMap {
        runStepB(_).flatMap {
          runStepC
        }
      }
    }
}
0 голосов
/ 01 марта 2019

Предполагая, что у вас есть контроль над функциями "runStep", я бы посоветовал вам изменить их так, чтобы они возвращали Either [Invalid, _].Таким образом, вы можете составить их в единый для понимания, как это (я использовал Throwable вместо Invalid, но идея та же самая):

def raiseError(e: Throwable): Unit = println(e.getMessage)

def firstMethod(v: Int): Either[Throwable, Int] = Right(v * 2)    
def secondMethod(v: Int) : Either[Throwable, Int] = Try(v / 0).toEither    
def thirdMethod(v: Int): Either[Throwable, Int] = Right(v * 2)

val x = for {
  first <- firstMethod(5)
  second <- secondMethod(first)
  third <- thirdMethod(second)
} yield third

x match {
  case Left(err) => raiseError(err)
  case Right(result) => println(result)
}

Здесь secondMethod выдает исключение, поэтому третийMethodникогда не выполняется, что вы пытаетесь сделать

...