As in most languages, Scala has an if statement. However, using pattern matching I can achieve the exact (?) same result using code of the form:
(condition) match {
case true => { /* handle the true case */ }
case false => { /* handle the false case */ }
}
This feels like an abuse of the mechanism, but I find it hard to explain why. Can wiser heads help me understand the position?
I wouldn't normally use it, although yes, it's a matter of taste. But there are cases where I might resort to this construction. For example when I need to send as argument a higher order function that reveives a Boolean value (using Scala's PartialFunction syntax):
future.onSuccess {
case true => ???
case false => ???
}
Or when there are extra conditions, something along these lines:
value match {
case true if condition1 => ???
case true if condition2 => ???
case true if condition3 => ???
case false => ???
}
The match compiles to the equivalent of
val scrutinee = condition
if (scrutinee == true) /* handle success case */
else if (scrutinee == false) /* handle failure case */
else throw new MatchException()
so it's semantically identical. But why would you? It's more verbose, more syntax-heavy, and less clear than the if expression.
Related
Say I have a set of rules that have a validation function that returns IO[Boolean] at runtime.
case class Rule1() {
def validate(): IO[Boolean] = IO.pure(false)
}
case class Rule2() {
def validate(): IO[Boolean] = IO.pure(false)
}
case class Rule3() {
def validate(): IO[Boolean] = IO.pure(true)
}
val rules = List(Rule1(), Rule2(), Rule3())
Now I have to iterate through these rules and see "if any of these rules" hold valid and if not then throw exception!
for {
i <- rules.map(_.validate()).sequence
_ <- if (i.contains(true)) IO.unit else IO.raiseError(new RuntimeException("Failed"))
} yield ()
The problem with the code snippet above is that it is trying to evaluate all the rules! What I really want is to exit at the encounter of the first true validation.
Not sure how to achieve this using cats effects in Scala.
I claim that existsM is the most direct way to achieve what you want. It behaves pretty much the same as exists, but for monadic predicates:
for {
t <- rules.existsM(_.validate())
_ <- IO.raiseUnless(t)(new RuntimeException("Failed"))
} yield ()
It also stops the search as soon as it finds the first true.
The raiseUnless is just some syntactic sugar that's equivalent to the if-else from your question.
If you take a look at list of available extension methods in your IDE, you can find findM:
for {
opt <- rules.findM(_.validate())
_ <- opt match {
case Some(_) => IO.unit
case None => IO.raiseError(new RuntimeException("Failed")
}
} yield ()
Doing it manually could be done with foldLeft and flatMap:
rules.foldLeft(IO.pure(false)) { (valueSoFar, nextValue) =>
valueSoFar.flatMap {
case true => IO.pure(true) // can skip evaluating nextValue
case false => nextValue.validate() // need to find the first true IO yet
}
}.flatMap {
case true => IO.unit
case false => IO.raiseError(new RuntimeException("Failed")
}
The former should have the additional advantage that it doesn't have to iterate over whole collection when it finds the first match, while the latter will still go through all items, even if will start discarding them at some point. findM solves that by using tailRecM internally to terminate the iteration on first met condition.
You can try recursive
def firstTrue(rules: List[{def validate(): IO[Boolean]}]): IO[Unit] = rules match {
case r :: rs => for {
b <- r.validate()
res <- if (b) IO.unit else firstTrue(rs)
} yield res
case _ => IO.raiseError(new RuntimeException("Failed"))
}
Another approach is not using booleans at all, but the monad capabilities of IO
def validateRules(rules: List[Rule]): IO[Unit] =
rules.traverse_ { rule =>
rule.validate().flatMap { flag =>
IO.raiseUnless(flag)(new RuntimeException("Failed"))
}
}
I find using match more readable than if. If I have a boolean value, can I use it with match?
I usually do
if(!authorised) {...} else {..}
But I am unable to do
authorised match {
case ??? //what here??
}
You just need to use boolean literals:
authorised match {
case true => /*true case*/
case false => /*false case*/
}
Alternatively to pattern match, consider mouse which provides extension methods for booleans, for example, consider fold
authorised.fold("It is true", "It is false")
You can do this using
authorised match {
case true => ...
case false => ...
}
Note that pattern matching on boolean values is not very idiomatic in Scala, and you'd probably be better off using the standard if/else expression. The compiler will actually not manage to generate the same efficient code for the pattern match, as discussed in this answer. It's very common for Scala beginners to get over-enthusiastic with pattern matching and start using it everywhere, but in the case of plain booleans, it really makes sense to stick with if/else.
Side-note: Other answers have mentioned using a default clause like
authorised match {
case true => /*true case*/
case false => /*false case*/
case _ => /* default case */
}
This is unnecessary, as Boolean can only ever be true or false. Like all primitive types, they cannot be assigned null so the additional clause is useless and the compiler will justifiably warn you about it.
warning: unreachable code
case _ => /* default case */
You can simply write:
authorised match {
case true => ...
case false => ...
}
Although intellij suggests refactoring to if statement.
You can do like this:
authorised match {
case true => // true case
case false => // false case
case _ => // other case for exception or null
}
I have two vals, a condition and an option. Note that condition is a simple boolean, not depending on the option's value.
If condition holds true, I would like to map over the option to convert it to a result value. In all other cases, I would like to return a defaultResult.
This works and is quite readable, but I dislike the duplication of defaultResult:
val result = if (condition) {
option.map(valueToResult).getOrElse(defaultResult)
} else {
defaultResult
}
My second approach does not have duplications, but I dislike the fact that filter is abused for something that is not actually dependent on the option's value:
val result = option.filter(_ => condition).map(valueToResult).getOrElse(defaultResult)
What's a more idiomatic or otherwise better approach in Scala?
You can use Option.collect:
Returns a scala.Some containing the result of applying pf to this
scala.Option's contained value, if this option is nonempty and pf is
defined for that value.
val result = option.collect {
case x if condition => valueToResult(x)
}.getOrElse(defaultResult)
val result = option match {
case Some(value) if condition => valueToResult(value)
case _ => defaultResult
}
val result = (for (v<-option if condition) yield valueToResult(v)).getOrElse(defaultResult)
option.foldLeft(defaultResult)((d,x) => if (condition) valueToResult(x) else d)
val result = (condition match {
case true => option.map(valueToResult)
case false => None
}).getOrElse(defaultResult)
I am looking for a way to do pattern matching based on the result of a function evaluation rather than the type of the val. For example,
def f1(x:String):Boolean = if (x contains ("Helllo")) true else false
val caller="Hello"
caller match
{
case f1(caller) => println ("caller said hello")
case _ => println ("caller did not say hello")
}
any idea ?
You want to use pattern guards:
caller match
{
case x if f1(x) => println ("caller said hello")
case _ => println ("caller did not say hello")
}
I would prefer to do it without guard, that would a bit faster and cleaner:
f1(caller) match {
case true => ....
case false => ....
}
but for Boolean better to use if/else expression, that would be cleaner in byte code and a bit faster
Suppose I have a Scala match expression
foo match {
case Bar(Some(x)) => /* do something */
case Bar(None) => /* do something else */
}
How much optimization does the compiler do when it compiles the expression? In particular, does it emit multiple calls to Bar.unapply, or does it make a single call and match multiple times on the result?
You can check this yourself with a little code:
object Bar {
def unapply(x: Int): Option[Option[Int]] = {
println("Checking: " + x)
Some(None)
}
}
1 match {
case Bar(Some(x)) => println("do something")
case Bar(None) => println("do something else")
}
When you run it, you get:
Checking: 1
do something else
So it looks like Scala is not doing multiple calls to Bar.unapply even though the code looks like there would be multiple calls. This is good from the standpoint of efficiency, but you should probably avoid side-effects in your unapply methods that rely on them getting called multiple times per match-statement.
If you were worried about when the optimization happens and making it clear to the reader that the unapply is only being called once, you could split up the matches quite easily:
1 match {
case Bar(y) => y match {
case Some(x) => println("do something")
case None => println("do something else")
}
}