Home > OS >  Swift-like "guard-let" in Scala
Swift-like "guard-let" in Scala

Time:09-30

In Swift, one can do something like:

guard let x = z else {return}  // z is "String?"

In this simple case, if z (an optional) is empty, the function will exit.

I really like this structure and recently started developing in Scala, and I was wondering if there's an equivalent in Scala.

What I found is this:

val x = if (z.isEmpty) return else z.get  // z is "Option[String]"

It works - but I still wonder if there's a more "Scala-ish" way of doing it.

EDIT: the use case is that a configuration argument is allowed to be null in the app, but this specific function has nothing to do in that case. I'm looking for the correct way to avoid calling code that will not work.

CodePudding user response:

Scala is functional so using return to break out early is almost always a bad idea (partly because return doesn't work the way it might appear).

In this case there are a couple of choices.

Using map allows the value inside an Option to be processed if it is there:

val xOpt = z.map { x =>
      // process x
    }

xOpt is a new Option that contains the results of the processing, or None if z was already None.

The alternative is to use a default value if z is None and then use that.

val x = z.getOrElse(defaultX)
// process x

In this case x is a bare value not an Option.

CodePudding user response:

For comprehensions are similar:

def f(z: Option[String]) = for {
  x <- z  // x is String
  result = x   " okay"
} yield result

This is useful when you have several short-circuiting operations, but the downside (compared to Swift) is you can't use other control structures such as if without some difficulty.

However, for in Scala can be used for more than just Option, e.g. to process lists:

def products(xs: Vector[Int], ys: Vector[Int]) = for {
  x <- xs
  y <- ys
} yield x * y  // Result is a Vector of each x multiplied by each y

CodePudding user response:

Admittedly I'm not familiar with Swift, but since you're not returning a value, this is presumably in a function being called for side-effect.

In Scala, the natural equivalent for "perform this side-effect if and only if this Option is non-empty is foreach:

z.foreach { x =>
  // code that uses x
}

There is syntactic sugar around this, the above is equivalent to

for (
  x <- z
) {  // the absence of yield is significant here
  // code that uses x
}

If z is empty, the foreach does nothing, so the "code that uses x would be everything in the function after the guard-let.

It might be that there's an implicit null being returned in the early exit case (it's hard to think of anything else) if this is a function being called for value. In that case, the natural equivalent would be map:

z.map { x =>
  // code that computes a value from x
} // passes through None if z is empty

The for sugar would be

for (
  x <- z
) yield {
  // code that uses x
}

As above, "code that uses x" means everything after the guard-let.

If you happened to have two nullable arguments like

guard let x = z else { return }
guard let w = y else { return }

You could nest these or have multiple <-'s in your for expression; alternatively (and my personal preference), you can use zip:

z.zip(y).foreach {
  case (x, w) =>
    // code that uses x and w
}

// or in the called-for-value case

z.zip(y).map {
  case (x, w) =>
    // code that uses x and w
}.headOption  // zip creates an Iterable, so bring it back to Option
  • Related