Scala: better nested checks of several conditions

Recently, I often write code:

def doSomethingWithLotsOfConditions(arg1, arg2, arg3...) { arg1.get(arg2) match { case Some(value1) => arg3.get(value1) match { case Some(value2) => arg4.get(arg5, value2) match { case Some(value3) => finallyDoSomethingInside(value3) case None => log("Some excuse for being unable to work with arg4/arg5...") } case None => log("Some excuse for being unable to work with arg3") } case None => log("Some excuse for being unable to work with arg1/arg2") } } 

A somewhat related question , apparently, largely advocates such use of nested match , although, from my point of view, it hardly seems readable, concise or easy to understand: (1) it kind of breaks the check itself and its consequences, (2) makes the code unmanaged nested without any real justification for nesting. In these specific cases, I would be happy to structure the code in lines:

 def doSomethingWithLotsOfConditions(arg1, arg2, arg3...) { // Step 1 val value1Opt = arg1.get(arg2) if (value1Opt.isEmpty) { log("Some excuse for being unable to work with arg1/arg2") return } val value1 = value1Opt.get // Step 2 val value2Opt = arg3.get(value1) if (value2Opt.isEmpty) { log("Some excuse for being unable to work with arg3") return } val value2 = value2Opt.get // Step 3 val value3Opt = arg4.get(arg5, value2) if (value3Opt.isEmpty) { log("Some excuse for being unable to work with arg4/arg5...") return } val value3 = value3Opt.get // All checked - we're free to act! finallyDoSomethingInside(value3) } 

However, this template (i.e. valueXOpt = (...).get => check isEmpty => value = valueXOpt.get ) looks really ugly and also definitely too detailed. Damn, even a Java version would have looked more concise:

 Value1Type value1 = arg1.get(arg2); if (value1 != null) { log("Some excuse for being unable to work with arg1/arg2"); return; } 

Is there a better, cleaner alternative, that is, to get the value and indicate an alternative short escape route (that is log line + return ) without entering into the nest with matches?

+5
source share
3 answers

How about this?

 object Options{ implicit class OptionLog[T](val option:Option[T]) extends AnyVal{ def ifNone(body: =>Unit):Option[T] = option.orElse { body option } } } import Options._ def something(arg1:Option[Int], arg2:Option[String], arg3:Option[Long], arg4:Option[Any]){ for{ val1 <- arg1 ifNone(println("arg1 was none")) val2 <- arg2 ifNone(println("arg2 was none")) val3 <- arg3 ifNone(println("arg3 was none")) }{ println(s"doing something with $val1, $val2, $val3") } } 

Then...

 scala> something(Some(3), Some("hello"), None, Some("blah")) arg3 was none scala> something(Some(3), Some("hello"), Some(10l), Some("blah")) doing something with 3, hello, 10 
+6
source

Perhaps you mean for condition x :

 scala> def f(x: Option[Int]): Int = x orElse { println("nope"); return -1 } map (_ + 1) getOrElse -2 f: (x: Option[Int])Int scala> f(Some(5)) res3: Int = 6 scala> f(None) nope res4: Int = -1 

or even

 scala> def f(x: Option[Int], y: Option[Int]): Int = (for (i <- x orElse { println("nope"); return -1 }; j <- y orElse { println("gah!"); return -2 }) yield i + j) getOrElse -3 f: (x: Option[Int], y: Option[Int])Int scala> f(Some(5), None) gah! res5: Int = -2 

Sorry if I simplify.

+1
source

You do not want to use the map method?

 def doSomethingWithLotsOfConditions(arg1, arg2, arg3...) = arg1.get(arg2).map(value1 => arg3.get(value1).map(value2 => arg4.get(arg5, value2).map(value3 => finallyDoSomethingInside(value3)). getOrElse(log("Some excuse for being unable to work with arg4/arg5"))). getOrElse(log("Some excuse for being unable to work with arg3"))). getOrElse(log("Some excuse for being unable to work with arg1/arg2")) 

It is still nested, but at least it looks more elegant than the previous template.

Or you can implement your own Functor for this:

 trait Foo[+A] { def fmap[B](f: A => B): Foo[B] } case class Bar[A](value: A) { def fmap[B](f: A => B): Foo[B] = Bar(f(value)) } case object Error[Nothing](message: String) { def fmap[B](f: Nothing => B) = Error(message) } def doSomethingWithLotsOfConditions(arg1, arg2, arg3, arg4, arg5) = arg1.get(arg2).fmap(value1 => arg3.get(value1).fmap(value2 => arg4.get(arg5, value2).fmap(value3 => finallyDoSomethingInside)) def processResult = doSomethingWithLotsOfConditions(...) match { case Bar(a) => doSomething case Error(message) => log(message) } 

This code assumes arg.get returns Foo.

+1
source

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


All Articles