scala error illegal variable in pattern alternative on pattern matching case - scala

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")

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 !=

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.

What's the most idiomatic way in Scala to pattern match on a Seq holding enum values converted to strings?

I'm trying to match an enum value converted to a string held in a collection. Here's the code:
object Foo extends Enumeration {
val ONE = Value("ONE")
val TWO = Value("TWO")
}
def check(seq: Seq[String]): Unit = seq match {
case Seq(Foo.ONE.toString) => println("match")
case _ => println("no match")
}
This results in a compilation error:
error: stable identifier required, but Foo.ONE.toString found.
case Seq(Foo.ONE.toString) => println("match")
What is the proper way to use my Foo enumerated values as elements of my pattern matching case statements?
Map it back to the enum first:
import scala.util.Try
val enumSeq = seq map (x => Try(Foo.withName(x)))
Then you can either filter out the Failures or match on Seq(Success(ONE)), Seq(Success(ONE)), ..., Seq(Failure), etc.
def check(seq: Seq[String]): Unit = seq match {
case Seq(s # _) if s == Foo.ONE.toString => println("match")
case _ => println("no match")
}
I like the response from #cchantep, which was to avoid calling .toString inside the pattern match and implement the check method like so:
def check(seq: Seq[Foo.Value]): Unit = seq match {
case Seq(Foo.ONE) => println("match")
case _ => println("no match")
}

Scala : Pattern matching with Option[Foo] and parameter of Foo

How can rewrite the following to make it more 'Scala way' or use just one match?
case class Foo(bar: Any)
val fooOpt = Some(Foo("bar as String"))
def isValid(p: Any) = p match {
case _ # (_: String | _: Int) => true
case _ => false
}
//Is it possible to check for the type of bar directly in this if statement?
fooOpt match {
case Some(f) if isValid(f.bar) => doSomething
case _ => doSomethingElse
}
One alternative would be using the isInstanceOf.
fooOpt match {
case Some(f) if f.bar.isInstanceOf[String] => doSomething
case Some(f) if f.bar.isInstanceOf[Int] => doSomething //could also rewrite to use just one case
case _ => doSomethingElse
}
Is there other way?
This can all be done in one big pattern match:
fooOpt match {
case Some(Foo(_: Int | _: String)) => doSomething
case _ => doSomethingElse
}
If you want to get the Int or String out, just split that case:
fooOpt match {
case Some(Foo(i: Int)) => doSomething
case Some(Foo(s: String)) => doSomething
case _ => doSomethingElse
}
Is there other way?
Although the solution with one big patten match works(and can be used if you really can't change bar to anything more specific than Any), it is not a proper 'Scala way' of dealing with this situations in general if you have control over Foo.
A better way would be to make Foo generic:
case class Foo[T](bar: T)
And have either a generic doSomething, if it can work with any particular T:
def doSomething[T](foo: Foo[T]): SomeType = ???
or to have different versions of it for different possible T's you have, if it should react on them differently:
def doSomethingWithString(foo: Foo[String]): SomeType = ???
def doSomethingWithInt(foo: Foo[Int]): SomeType = ???
Then you can use it just like this:
val fooOpt = Some(Foo("bar as String"))
fooOpt.map(doSomething).orElse(doSomethingElse)
or like this:
val fooOptString = Some(Foo("bar as String"))
fooOptString.map(doSomethingWithString).orElse(doSomethingElse)
val fooOptInt = Some(Foo(1))
fooOptInt.map(doSomethingWithInt).orElse(doSomethingElse)
So, in this case compiler checks types for you, answering to:
Is it possible to check for the type of bar directly?
And in many situations you can avoid using pattern match at all, using methods like map, orElse, etc. with proper typing. This might be an answer for this:
could also rewrite to use just one case

can we use case match to pattern again Option and normal value?

I want to print the value if the it's not an option, and print the value inside the option if it's option. How to get that? The following doesn't work
val a="test"
def b= a match {
case i:Some[_] => i.getOrElse("1")
case _#x=>x
}
Something like this I think:
val a: Any = "test"
def b[T] = a match {
case i: Option[T] => i.getOrElse("1")
case _#x=>x
}
First a must be some supertype you can match on, if you had a: String matching on options would not be possible because you would already know that it's a string, note also that you have to pass a type parameter for option.
val a:Any="test"
def b= a match {
case Some(i) => i
case None => "1"
case x=>x
}