How to refactor (if / elsif / elsif) a chain in Scala?

I have a chain if/ else ifwhich is not self-evident. I would like to extract each of them into my function with a clear explanatory name, and then connect these functions.

How can I stop the chain of calls in the middle in scala?

Here is a sample code:

// actual code 

for( klass <- program.classes ) {
    if ( complicated boolean ) { //checkVars
        error1
    } else if ( complicated boolean ) { //checkMethods
        error2
    } else if ( ... ) { //...
        error3
    } else {
        complicated good case code
    }
}

// wanted 

for( klass <- program.classes ) {
    (checkName 
     andThen checkVars
     andThen checkMethods
     andThen addToContext) (klass)
// where the chaining stops if a check fails
}
+4
source share
7 answers

You can use the type and methods Optionthat are returned Option[_]in understanding forfor checking circuits when retrieving partial results. Processing stops when the option returns None

for {
    klass <- program.classes
    name <- checkName // Option[String]
    vars <- checkVars // Option[SomeType]
    methods <- checkMethods // Option[SomeOtherT]
    ctx <- addToContext // Option[...]
} {
// do something with klass
// if you got here, all the previous Options returned Some(_)
}
+3
source

, , :

def checkName(klass: Klass): Option[Klass] = if (compBoolean) Some(klass) else None
def checkVars(klass: Klass): Option[Klass] = if (compBoolean) Some(klass) else None
def checkMethods(klass: Klass): Option[Klass] = if (compBoolean) Some(klass) else None
def finalOp(klass: Klass): OutputClass = //your final operation

// Use the above checks
program.classes.map(checkName(_).flatMap(checkVars).flatMap(checkMethods).map(finalOp).getOrElse(defaultResult))

/ , , flatMap:

program.classes.flatMap(checkName(_).flatMap(checkVars).flatMap(checkMethods).map(finalOp))
+2

, .

def checkName(klass: Klass): Boolean = ???
def checkVars(klass: Klass): Boolean = ???
def checkMethods(klass: Klass): Boolean = ???
def finalOp(klass: Klass): OutputClass = ???

Option(klass)
  .filter(checkName)
  .filter(checkVars)
  .filter(checkMethods)
  .map(finalOp)

Some(), , None, - .

+2
program.classes foreach {
  case klass if checkName(klass) => error1
  case klass if checkVars(klass) => error2
  case klass if checkMethods(klass) => error3
  case klass => addToContext(klass)
}
+2

, ( ), PartialFunction. Try . Try , . (Option, , , . , - .)

:

import scala.util.{Try, Success, Failure}

val check1:PartialFunction[Int, Try[String]] = {case x if x==1 => Failure(new Exception("error1"))}
val check2:PartialFunction[Int, Try[String]] = {case x if x==2 => Failure(new Exception("error2"))}
val check3:PartialFunction[Int, Try[String]] = {case x if x==3 => Failure(new Exception("error3"))}
val process: PartialFunction[Int, Try[String]] = {case x => Success(s"[$x] processed OK")}

val checks = check1 orElse check2 orElse check3 orElse process

for (i <- 1 to 4) yield (checks(i))
//  scala.collection.immutable.IndexedSeq[scala.util.Try[String]] = Vector(
//    Failure(java.lang.Exception: error1),
//    Failure(java.lang.Exception: error2),
//    Failure(java.lang.Exception: error3),
//    Success([4] processed OK)
//)
+2

fold

Option fold.

fold

final def fold[B](ifEmpty: => B)(f: Int => B): B

. , , - - . , - None . , Some None .

def f1: Option[_] = ???
def f2: Option[_] = ???
def f3: Option[_] = ???

f1.fold[Option[Unit]](None)(_ => f2).fold[Option[Unit]](None)(_ => f3)

Scala REPL

scala> Option(1).fold[Option[Unit]](None)(_ => Some(println("hello"))).fold[Option[Unit]](None)(_ => Some(println("scala")))
hello
scala
res59: Option[Unit] = Some(())

scala> None.fold[Option[Unit]](None)(_ => Some(println("hello"))).fold[Option[Unit]](None)(_ => Some(println("scala")))
res60: Option[Unit] = None

scala> Option(1).fold[Option[Unit]](None)(_ => None).fold[Option[Unit]](None)(_ => Some(println("scala")))
res61: Option[Unit] = None
0

, C, Scala. . , && thenIfSuccess - ( ).

, && .


def checkName(klass: Klass): Boolean = ???
def checkVars(klass: Klass): Boolean = ???
def checkMethods(klass: Klass): Boolean = ???
def finalOp(klass: Klass): Boolean = ???

// just chain the method calls :
checkName(cls) && checkVars(cls) && checkMethods(cls) && finalOp(cls)


// this will call each method in order and stop if one fails.

If you think about it, it is very easy to read, much more than other answers using fold, filteror pattern matching. The expression is foralso very easy to read imho, but it makes you return Option[_], which is not very natural.

0
source

Source: https://habr.com/ru/post/1660844/


All Articles