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