It seems that nested matching doesn't work, which is a strange limitation.
An example of the behaviour follows:
Some(Some(1),2) match {
| case Some(Some(a),b) => a
| case e => e
| }
<console>:9: error: wrong number of arguments for <none>: (x: (Some[Int], Int))Some[(Some[Int], Int)]
case Some(Some(a),b) => a
^
<console>:9: error: not found: value a
case Some(Some(a),b) => a
^
This works:
Some(Some(1),2) match {
case Some(a) => a match {
case (Some(a),b) => "yay"
case e => "nay"
}
}
Now, am I just being a twit or is there a better way to achieve this?
What is Some (Some(1),2)? An Option of Tuple of (Option (of Int) and Int)? This works:
scala> Some ((Some (1), 2)) match {
| case Some ((Some (a), b)) => a
| case e => e }
res13: Any = 1
Note the additional parenthesis around the tuple - it's a common mistake to have too few of them.
Related
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.
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"
}
}
}
What's wrong with the following piece of code ? I'm trying to use a tuple (String, Int) as the type of input to the function find_host. The complier doesn't give me any errors but when I run the program I get one. What am I missing here?
def find_host ( f : (String, Int) ) = {
case ("localhost", 80 ) => println( "Got localhost")
case _ => println ("something else")
}
val hostport = ("localhost", 80)
find_host(hostport)
missing parameter type for expanded function
The argument types of an anonymous function must be fully known. (SLS 8.5)
Expected type was: ?
def find_host ( f : (String, Int) ) = {
^
To do a pattern match (your case statements here), you need to tell the compiler what to match on:
def find_host ( f : (String, Int) ) = f match {
... ^^^^^^^
This code does fail compilation. IntelliJ's Scala support is not perfect; you can't count on it to find all compile errors.
This is what you get if you try it in the REPL:
scala> def find_host ( f : (String, Int) ) = {
| case ("localhost", 80 ) => println( "Got localhost")
| case _ => println ("something else")
| }
<console>:7: error: missing parameter type for expanded function
The argument types of an anonymous function must be fully known. (SLS 8.5)
Expected type was: ?
def find_host ( f : (String, Int) ) = {
^
Like Shadowlands's answer says, you're missing f match before the partial function.
But also, since this method returns Unit, don't define it with the equals symbol.
def find_host(f: (String, Int)) {
f match {
case ("localhost", 80) => println("Got localhost")
case _ => println("something else")
}
}
Here is another solution:
Note: here you don't need to tell the compiler what to match.
scala> def find_host: PartialFunction[(String, Int), Unit] = {
| case ("localhost", 80) => print("Got localhost")
| case _ => print("Something else")
| }
find_host: PartialFunction[(String, Int),Unit]
scala> find_host(("localhost", 80))
Got localhost
Or this one:
scala> def find_host: ((String, Int)) => Unit = {
| case ("localhost", 80) => print("Got localhost")
| case _ => print("Something else")
| }
find_host: ((String, Int)) => Unit
scala> find_host(("localhost", 80))
Got localhost
If you have a pattern matching (case) in Scala, for example:
foo match {
case a: String => doSomething(a)
case f: Float => doSomethingElse(f)
case _ => ? // How does one determine what this was?
}
Is there a way to determine what type was actually caught in the catch-all?
case x => println(x.getClass)
Too easy :-)
Basically, you just need to bind the value in your catch-all statement to a name (x in this case), then you can use the standard getClass method to determine the type.
If you're trying to perform specific logic based on the type, you're probably doing it wrong. You could compose your match statements as partial functions if you need some 'default' cases that you don't want to define inline there. For instance:
scala> val defaultHandler: PartialFunction[Any, Unit] = {
| case x: String => println("String: " + x)
| }
defaultHandler: PartialFunction[Any,Unit] = <function1>
scala> val customHandler: PartialFunction[Any, Unit] = {
| case x: Int => println("Int: " + x)
| }
customHandler: PartialFunction[Any,Unit] = <function1>
scala> (customHandler orElse defaultHandler)("hey there")
String: hey there
foo match {
case a: String => doSomething(a)
case f: Float => doSomethingElse(f)
case x => println(x.getClass)
}
what is wrong in this piece of code?
(Left("aoeu")) match{case Right(x) => ; case Left(x) => }
<console>:6: error: constructor cannot be instantiated to expected type;
found : Right[A,B]
required: Left[java.lang.String,Nothing]
why the pattern matcher just doesn't skip the Right and examine Left?
Implicit typing is inferring that Left("aoeu") is a Left[String,Nothing]. You need to explicitly type it.
(Left("aoeu"): Either[String,String]) match{case Right(x) => ; case Left(x) => }
It seems that pattern matching candidates must always be of a type matching the value being matched.
scala> case class X(a: String)
defined class X
scala> case class Y(a: String)
defined class Y
scala> X("hi") match {
| case Y("hi") => ;
| case X("hi") => ;
| }
<console>:11: error: constructor cannot be instantiated to expected type;
found : Y
required: X
case Y("hi") => ;
^
Why does it behave like this? I suspect there is no good reason to attempt to match on an incompatible type. Attempting to do so is a sign that the developer is not writing what they really intend to. The compiler error helps to prevent bugs.
scala> val left: Either[String, String] = Left("foo")
left: Either[String,String] = Left(foo)
scala> left match {
| case Right(x) => "right " + x
| case Left(x) => "left " + x }
res3: java.lang.String = left foo