Why does chaining match expressions does not compile? [duplicate] - scala

This question already has answers here:
Pattern Match "return" value
(2 answers)
Closed 9 years ago.
Chaining match expressions does not compile.
val x = Array("abc", "pqr")
x match {
case Array("abc", _*) => Some("abc is first")
case Array("xyz", _*) => Some("xyz is first")
case _ => None
} match {
case Some(x) => x
case _ => "Either empty or incorrect first entry"
}
While the following compiles fine:
(x match {
case Array("abc", _*) => Some("abc is first")
case Array("xyz", _*) => Some("xyz is first")
case _ => None
}) match {
case Some(x) => x
case _ => "Either empty or incorrect first entry"
}
Why does the later version (where first match expression is in paranthesis) compile fine while earlier one does not?

If it were allowed, you couldn't do:
scala> List(1,2,3) last match { case 3 => true }
warning: there were 1 feature warning(s); re-run with -feature for details
res6: Boolean = true
That is, if it were infix notation, then the thing to the left could not be postfix.
Disallowing infix match permits a postfix scrutinee.
That expression is parsed the natural way
(List(1,2,3) last) match { case 3 => true }
that is, if postfix notation is natural and not unholy.
The feature warning is for import language.postfixOps. Perhaps with that feature turned off, the Defenders of the Good would be willing to entertain import language.infixMatch.
Consider constructs that are syntactic siblings to match, that are not infixable without parens:
scala> if (true) 1 else 2 match { case 1 => false }
res4: AnyVal = 1 // not false
scala> (if (true) 1 else 2) match { case 1 => false }
res1: Boolean = false
or
scala> throw new IllegalStateException match { case e => "ok" }
<console>:11: error: type mismatch; // not "ok", or rather, Nothing
found : String("ok")
required: Throwable
throw new IllegalStateException match { case e => "ok" }
^
scala> (throw new IllegalStateException) match { case e => "ok" }
java.lang.IllegalStateException

Related

Scala: How to simplify nested pattern matching statements

I am writing a Hive UDF in Scala (because I want to learn scala). To do this, I have to override three functions: evaluate, initialize and getDisplayString.
In the initialize function I have to:
Receive an array of ObjectInspector and return an ObjectInspector
Check if the array is null
Check if the array has the correct size
Check if the array contains the object of the correct type
To do this, I am using pattern matching and came up with the following function:
override def initialize(genericInspectors: Array[ObjectInspector]): ObjectInspector = genericInspectors match {
case null => throw new UDFArgumentException(functionNameString + ": ObjectInspector is null!")
case _ if genericInspectors.length != 1 => throw new UDFArgumentException(functionNameString + ": requires exactly one argument.")
case _ => {
listInspector = genericInspectors(0) match {
case concreteInspector: ListObjectInspector => concreteInspector
case _ => throw new UDFArgumentException(functionNameString + ": requires an input array.")
}
PrimitiveObjectInspectorFactory.getPrimitiveWritableObjectInspector(listInspector.getListElementObjectInspector.asInstanceOf[PrimitiveObjectInspector].getPrimitiveCategory)
}
}
Nevertheless, I have the impression that the function could be made more legible and, in general, prettier since I don't like to have code with too many levels of indentation.
Is there an idiomatic Scala way to improve the code above?
It's typical for patterns to include other patterns. The type of x here is String.
scala> val xs: Array[Any] = Array("x")
xs: Array[Any] = Array(x)
scala> xs match {
| case null => ???
| case Array(x: String) => x
| case _ => ???
| }
res0: String = x
The idiom for "any number of args" is "sequence pattern", which matches arbitrary args:
scala> val xs: Array[Any] = Array("x")
xs: Array[Any] = Array(x)
scala> xs match { case Array(x: String) => x case Array(_*) => ??? }
res2: String = x
scala> val xs: Array[Any] = Array(42)
xs: Array[Any] = Array(42)
scala> xs match { case Array(x: String) => x case Array(_*) => ??? }
scala.NotImplementedError: an implementation is missing
at scala.Predef$.$qmark$qmark$qmark(Predef.scala:230)
... 32 elided
scala> Array("x","y") match { case Array(x: String) => x case Array(_*) => ??? }
scala.NotImplementedError: an implementation is missing
at scala.Predef$.$qmark$qmark$qmark(Predef.scala:230)
... 32 elided
This answer should not be construed as advocating matching your way back to type safety.

pattern matching with case match in scala

I have a match statement like this:
val x = y match {
case array: Array[Float] => call z
case array: Array[Double] => call z
case array: Array[BigDecimal] => call z
case array: Array[_] => show error
}
How do I simplify this to use only two case statements, since first three case statements do same thing, instead of four.
Type erasure does not really gives you opportunity to understand how array was typed. What you should do instead is to extract head ( first element) of array and check it's type. For example following code works for me:
List(1,2,3) match {
case (a:Int) :: tail => println("yep")
}
This work, although not very nice:
def x(y: Array[_]) = y match {
case a if a.isInstanceOf[Array[Double]] ||
a.isInstanceOf[Array[Float]] ||
a.isInstanceOf[Array[BigDecimal]] => "call z"
case _ => "show error"
}
Would have thought that pattern matching with "|" as below would do the trick. However, this gives pattern type is incompatible with expected type on Array[Float] and Array[BigDecimal]. It might be that matching of generic on this single case where it could work has not been given so much attention:
def x(y: Array[_ <: Any]) = y match {
case a # (_:Array[Double] | _:Array[Float] | _:Array[BigDecimal]) => "call z"
case a: Array[_] => "show error"
}
May be it helps a bit:
import reflect.runtime.universe._
object Tester {
def test[T: TypeTag](y: Array[T]) = y match {
case c: Array[_] if typeOf[T] <:< typeOf[AnyVal] => "hi"
case c: Array[_] => "oh"
}
}
scala> Tester.test(Array(1,2,3))
res0: String = hi
scala> Tester.test(Array(1.0,2.0,3.0))
res1: String = hi
scala> Tester.test(Array("a", "b", "c"))
res2: String = oh
You can obtain the class of array elements as follows (it will be null for non-array types): c.getClass.getComponentType. So you can write:
if (Set(classOf[Float], classOf[Double], classOf[BigDecimal]).contains(c.getClass.getComponentType)) {
// call z
} else {
// show error
}
Not particularly Scala'ish, though; I think #thoredge's answer is the best for that.
You could also check whether the Array is empty first and then if not, just pattern match on Array.head...something like:
def x(y: Array[_]) = {
y.isEmpty match {
case true => "error"
case false => y.head match {
case a:Double | a:BigInt => do whatever
case _ => "error"
}
}
}

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.

Pattern matching with more than one match

Consider the following Scala code.
val a = "both"
a match {
case "both" | "foo" => println ("foo") // case 1
case "both" | "bar" => println ("bar") // case 2
}
I would like match to work so that if a == "both", Scala will execute both cases. Is this possible or are there any alternatives to achieve what I want?
Standard pattern-matching will always match on only exactly one case. You can get close to what you want by using the fact that patterns can be treated as partial functions (see the Language Specification, Section 8.5, Pattern Matching Anonymous Functions) and by defining your own matching operator, though:
class MatchAll[S](scrutinee : =>S) {
def matchAll[R](patterns : PartialFunction[S,R]*) : Seq[R] = {
val evald : S = scrutinee
patterns.flatMap(_.lift(evald))
}
}
implicit def anyToMatchAll[S](scrut : =>S) : MatchAll[S] = new MatchAll[S](scrut)
def testAll(x : Int) : Seq[String] = x matchAll (
{ case 2 => "two" },
{ case x if x % 2 == 0 => "even" },
{ case x if x % 2 == 1 => "neither" }
)
println(testAll(42).mkString(",")) // prints 'even'
println(testAll(2).mkString(",")) // prints 'two,even'
println(testAll(1).mkString(",")) // prints 'neither'
The syntax is slightly off the usual, but to me such a construction is still a witness to the power of Scala.
Your example is now written as:
// prints both 'foo' and 'bar'
"both" matchAll (
{ case "both" | "foo" => println("foo") },
{ case "both" | "bar" => println("bar") }
)
(Edit huynhjl pointed out that he gave a frighteningly similar answer to this question.)
At risk of being Captain Obvious, in a case like this it would be simplest just to forget pattern matching and use if.
if (a == "both" || a == "foo") println("foo")
if (a == "both" || a == "bar") println("bar")
If the repetition of a == worries you, you could instead write
if (Set("both", "foo")(a)) println("foo")
if (Set("both", "bar")(a)) println("bar")
using the fact that the apply method on Set does the same as contains, and is a bit shorter.
match executes one, and only one, of the cases, so you can't do this as an or in the match. You can, however, use a list and map/foreach:
val a = "both"
(a match {
case "both" => List("foo", "bar")
case x => List(x)
}) foreach(_ match {
case "foo" => println("foo")
case "bar" => println("bar")
})
And you're not duplicating any of the important code (in this case the printlns).
Just match twice:
val a = "both"
a match {
case "both" | "foo" => println ("foo") // Case 1
}
a match {
case "both" | "bar" => println ("bar") // Case 2
}
One possible way could be:
val a = "both"
a match {
case "foo" => println ("foo") // Case 1
case "bar" => println ("bar") // Case 2
case "both" => println ("foo"); println ("bar")
}

Why does the empty string not match as Seq.empty?

EDIT: This was an old bug long since fixed in Scala 2.8 and later
During some experimentation around question Pattern matching a String as Seq[Char], I ran across another weird matching phenomenon. Consider the following code that treats a string as a sequence of characters:
def %%&#(input: String) : String = {
val uha : Seq[Char] = input
uha match {
case Seq() => "Empty"
case Seq(first # _, 'o', 'o') => "Bar"
case _ => "Oh"
}
}
Calling input on the empty String "" correctly yields "Empty".
However, if I rewrite the first match clause as
case Seq.empty => "Empty"
the matching of "" fails and matches the default clause instead.
Walking through the Scala library source code (which you shouldn't have to do in an ideal world :-) ) I believe that both Seq() and Seq.empty will result in RandomAccessSeq.empty. Apparently, this does not concur with the phenomenon described above because only Seq() matches the empty String.
UPDATE: Upon some further experimentation this question can be narrowed down to the following:
val list = List()
>>> list2: List[Nothing] = List()
val emptySeq = Seq.empty
list == emptySeq
>>> res1: Boolean = false
This basically means that an empty Seq does not automatically equal Seq.empty .
So when matching against a constant (as opposed to using an extractor as suggested by starblue) this unequality leads to the failing match.
The same is true when interpreting the empty String as a sequence.
This appears to be a bug in the library. Do you want to file the bug or shall I?
scala> Seq.empty match {case Seq() => "yup"; case _ => "nope"}
res0: java.lang.String = yup
scala> Seq() match {case Seq.empty => "yup"; case _ => "nope"}
res1: java.lang.String = yup
scala> ("" : Seq[Char]) match {case Seq() => "yup"; case _ => "nope"}
res2: java.lang.String = yup
scala> ("" : Seq[Char]) match {case Seq.empty => "yup"; case _ => "nope"}
res3: java.lang.String = nope
In matching the unapply or unapplySeq functions are used, not apply as you seem to believe.