Multiple pattern matches with same input variable - scala

I oftentimes face the following problem:
val b = a match {
case Some(a) => "1"
case None => "n"
}
val c = a match {
case Some(a) => "2"
case None => "n"
}
Obviously, the match is executed twice even though it is only necessary once. How can I make sure the compiler takes this into account?

I don't think there will be any performance gain but you can write your code like this:
val (b, c) = a match {
case Some(a) => ("1","2)
case None => ("n", "n")
}

Matches can be extremely fast--comparable to an if-statement. Don't worry about doing the work twice unless it's a difficult match.
It's slightly easier to match a default than another case, so if you really don't need the parameter your best bet is
val b = a match { case None => "n"; case _ => "1" }
val c = a match { case None => "n"; case _ => "2" }
This will often even outperform a mutable solution like
var b,c = "n"
a match { case Some(_) => b = "1"; c = "2"; case _ => }
and will certainly outperform any tuple-creation.
If the match is a time-consuming one then you have to store your results and return them in a tuple or other suitable data structure, as om-nom-nom has already demonstrated. This may also be more clear, depending on context.

Related

In Scala 3, is there a way to disable -language:strictEquality (multiversal equality) in a region?

The particular reason for wanting to do this is to still be able to use pattern matching against a value from a super-class. For instance, I'd like to be able to match with case None when looking at values of type Option[Throwable], but this doesn't seem to be possible since Throwable does not, and never will (I imagine) have a CanEqual instance.
Try limiting the scope of givens like so
val x: Option[Throwable] = None
{
given CanEqual[Option[Throwable], Option[Throwable]] = CanEqual.derived
x match {
case Some(v) => v
case None => new Throwable()
}
} // after this brace CanEqual given is out-of-scope
x match {
case Some(v) => v
case None => new Throwable()
} // compile-time error: Values of types object None and Option[Throwable] cannot be compared with == or !=

Best functional way of handling conditional flows

I have an Option of String which may be empty too. I have written following code to handle different branches of flow:
input match {
case Some(val) => {
val match {
case "sayHi" => "Hi"
case "sayHello" => "Hello"
case _ => extractFromAnotherInput
}
}
None => extractFromAnotherInput
}
private def extractFromAnotherInput = {
anotherInput match {
case a => ....
case b => ....
}
}
Is this a good way of handling code branches in a functional language or it can be done in a better way?
You don't have to nest the matches:
input match {
case Some("sayHi") => "Hi"
case Some("sayHello") => "Hello"
case _ => extractFromAnotherInput
}
You can also combine this input with "another", and "strip" he option before doing the match:
input.getOrElse(anotherInput) match {
case "sayHi" => "Hi"
case "sayHello" => "Hello"
case a => ...
case b => ...
}
I would go with Dima's solution. Or if you want to use some method on Option:
input.collect{
case "sayHi" => "Hi"
case "sayHello" => "Hello"
}
.getOrElse(extractAnotherInput)
Just because, I would like to propose an alternative to Levi's answer; just to remark this is more of a matter of opinion.
input.filter(_.nonEmpty).fold(ifEmpty = extractAnotherInput) {
case "sayHi" => "Hi"
case "sayHello" => "Hello"
}
This is a matter of style: I personally dislike pattern-matching on Option, but that's idiosyncratic (I also may be the only Scala programmer who generally dislikes the for notation, but that's neither here nor there).
I'd express that as
input.flatMap { v =>
v match {
case "sayHi" => Some("Hi")
case "sayHello" => Some("Hello")
case _ => None
}
}.getOrElse(extractAnotherInput)
If overfitting to this example (exploiting the fact that I can uniformly transform sayHi and sayHello)
input.filter(v => v == "sayHi" || v == "sayHello")
.map(_.drop(3))
.getOrElse(extractAnotherInput)

Pattern matching using string interpolation

In the following example using Scala 2.13.3 the 1st pattern matches, but the 2nd does not.
The 3rd pattern again matches, while the 4th does not (note that separator in the 4th match expression is enclosed in backticks, thus referencing the value defined before).
trait A
case object A extends A {
def unapply(a: String): Option[A] = if (a == "my_a") Some(A) else None
}
trait B
case object B extends B {
def unapply(b: String): Option[B] = if (b == "myB") Some(B) else None
}
val match1 = "myB_my_a" match {
case s"${B(b)}_${A(a)}" => Some((a,b))
case _ => None
} // Some((A,B))
val match2 = "my_a_myB" match {
case s"${A(a)}_${B(b)}" => Some((a,b))
case _ => None
} // None
val match3 = "my_a__myB" match {
case s"${A(a)}__${B(b)}" => Some((a,b))
case _ => None
} // Some((A,B))
val separator = "__"
val match4 = s"my_a${separator}myB" match {
case s"${A(a)}${`separator`}${B(b)}" => Some((a,b))
case _ => None
} // None
Why do only the 1st and the 3rd pattern match?
Is there a good matching alternative to the 2nd pattern that a) is using the unapply methods of A and B and where b) we don't know what strings these methods are accepting?
Edit 1: Added case object B and another matching example.
Edit 2: Another example to illustrate jwvh's answer:
val (a, b) = ("my_a", "myB")
val match5 = s"${a}_${b}" match {
case s"${`a`}_${`b`}" => Some((a, b)) // does not match
case s"${x}_${y}" => Some((x, y)) // matches: Some(("my", "a_myB"))
}
Edit 3: To illustrate how, unlike case class construction and extraction with apply and unapply, the construction and extraction of strings using similar string interpolation are not (and cannot be) inverse functions:
case class AB(a: String, b: String)
val id = (AB.apply _ tupled) andThen AB.unapply andThen (_.get)
val compare = id(("my_a", "myB")) == ("my_a", "myB") // true
val construct: (String, String) => String = (a,b) => s"${a}_${b}"
val extract: String => (String, String) = { case s"${a}_${b}" => (a,b) }
val id2 = (construct tupled) andThen extract
val compare2 = id2(("my_a","myB")) == ("my_a","myB") // false
As your own test (mentioned in the comments) demonstrates, the interpolator recognizes that the match pattern "${A(a)}_${B(b)}" is made up of 2 parts separated by an underscore _. So a best-guess effort is made to split the target string accordingly.
The 1st part, "my", is sent to the A.unapply() where it fails. The 2nd part, "a_myB", is not even attempted.
Something similar happens in match4. The pattern "${A(a)}${'separator'}${B(b)}" has 3 dollar signs and thus 3 parts. But, without any explicit characters to anchor the pattern, the target string is split into these 3 parts.
""
""
"my_a__myB"
Again, the 1st part fails the unapply() and the other parts are never attempted.
While your Edit 3 code is technically correct, I don't find it terribly convincing. You've simply demonstrated that (String,String)=>AB(String,String)=>(String,String) is (or can be) a lossless data transition. The same cannot be said of (String,String)=>String which introduces some ambiguity, i.e. the loss of information sufficient to guarantee restoration of the original data. That loss is inherent in the transformation itself, not the tools (interpolation) used to achieve it.
The fact that case class and String interpolation both use apply()/unapply() under the hood strikes me as inconsequential.

scala error illegal variable in pattern alternative on pattern matching case

I am new to scala. I was writing a pattern matching as below:
val capitals = Map("France" -> "Paris", "Japan" -> "Tokyo")
show(capitals.get("test"))
def show(x: Option[String]) = x match {
case Some(s) | None => s
}
I am getting error:
Error: illegal variable in pattern alternative
case Some(s) | None => s
^
I am trying to see how can I achieve or condition like I have in if statement in java
if (str == null || str.isEmpty())
Can you help rewrite the code or point out the mistake?
Question: How do I mention or condition in a case pattern matching?
This is how you pattern match on Options:
def show(x: Option[String]) = x match {
case Some(s) => s
case None => "N/A"
}
(by the way, you can also do something like this):
capitals.get("test").getOrElse("N/A")
Now, to add OR conditions to a pattern match case, you cannot use bound variables. This will work, however:
def show(x: Option[String]) = x match {
case Some(_) | None => "a"
}
Note that the only difference is in Some(_) as opposed to your Some(s). Using Some(s) wouldn't make much sense because you can't reuse that s anyway (what if None comes, what would s be in that case?)
I think this is what you are trying to achieve. If s has a value in the map return, s. If s has no value in the map, return a message indicating so.
val capitals = Map("France" -> "Paris", "Japan" -> "Tokyo")
def show(x: Option[String]) = x match {
case Some(s) => s
case None => "x has no value"
}
show(capitals.get("mani"))
A similar and more concise version of show is capitals.get("mani").getOrElse("No value found") which returns
No value found.
Further, you can use a guards to check various conditions on s such as if the first char is upper case. This first case will only match if s begins with an upper case character.
def show(x: Option[String]) = x match {
case Some(s) if(s.head.isUpper) => s
case None => "No value"
}
Matching Some(s) | None doesn't make sense, as if (true || false) (just no-op as it doesn't dispatch cases).
x match {
case Some(s) => println(s"Some($s)")
case _ => println("_None")
}
The most direct solution is using the method defined for maps, which takes the default as the second argument:
capitals.getOrElse("test","N/A")

More concise way to class match and access last of Option[List]

I have a function that as a parameter takes an object and if it is of the correct type I need to access the last element in an Option[List[Int]]. I have a working solution but it seems clumsy. In the case that there are not any items in obj.listOfThings I will need to have i have the value 0. Is there a better way to achieve this?
val i = foo match {
case obj: Bar =>
obj.listOfInts match {
case Some(ints) =>
ints.last
case _ =>
0
}
case _ =>
0
}
Technically it could return an Option[Int]. I'm still pretty new to Scala and would like to learn better approaches to this sort of problems.
In your case initially it seems that what Ende Neu suggested is the right way to go:
val i = foo match {
case obj: Bar =>
obj.listOfInts.map(_.last /* This throws an exception when the list is empty*/).getOrElse(0)
case _ =>
0
}
But if you look into it you'll see that you have a bug in your code, in the case that that obj.listOfInts is Some(Nil), because in that case you get a NoSuchElementException for trying to call last on an empty List.
Try this code with foo = Bar(Some(Nil)) and see for yourself.
When you use Option[List] think very carefully if this is what you want.
Usually after some thinking you will scrap the Option and just stay with a List because the Option serves no purpose.
I worked with many developers who misuse Option[List] because of not understanding the similarities between Nil and None and usually the 'None' case ends up playing the same role as Some(Nil)
So you end up having to do this:
optionalList match {
case None => // do something
case Some(list) =>
list match {
case Nil => // do the same thing
case head::tail => // do other stuff
}
}
As you can see the None case and the Some(Nil) case are basically the same.
To fix your bug you should do:
case class Bar(listOfInts: Option[List[Int]])
val i = foo match {
case Bar(Some(list)) if list != Nil => list.last
case _ => 0
}
You probably want to use flatMap and lastOption here:
obj.listOfInts.flatMap(_.lastOption)
In case listOfInts is None, or it is Some(Nil), this will return None. Otherwise it will return the last element. If you want to return 0 instead of None, just use getOrElse:
obj.listOfInts.flatMap(_.lastOption).getOrElse(0)
If you wanted to use a match, you could do:
obj.listOfInts match {
case Some(list#(hd::tl)) => list.last
case _ => 0
}
Here, the hd::tl guarantees that list is not empty. Another option is use a conditional match:
obj.listOfInts match {
case Some(list) if list.nonEmpty => list.last
case _ => 0
}
Or to match the None and Some(Nil) cases first:
obj.listOfInts match {
case None | Some(Nil) => 0
case Some(list) => list.last
}
As suggested in the comments, I think the best way to go is:
val i = foo match {
case obj: Bar => obj.listOfInts.map(_.last).getOrElse(0)
case _ => 0
}
More concise way including the instanceof:
scala> case class B(is: Option[List[Int]])
defined class B
scala> def f(x: Any) = Option(x) collect { case b: B => b.is flatMap (_.lastOption) } flatten
f: (x: Any)Option[Int]
scala> f(B(Option(5 to 7 toList)))
res0: Option[Int] = Some(7)
or
scala> import PartialFunction.{ condOpt => when }
import PartialFunction.{condOpt=>when}
scala> def g(x: Any) = when(x) { case b: B => b.is flatMap (_.lastOption) } flatten
g: (x: Any)Option[Int]
scala> g(B(Option(5 to 7 toList)))
res1: Option[Int] = Some(7)
It's probably worth asking why you lost static type info, that you need to pattern match.