Pattern Matching/Checking Arity of Function1 - scala

Given the following method:
scala> def f: List[Any] => Any = xs => 1234 // this output does not matter
f: List[Any] => Any
Is it possible to pattern match on List[Any] => Any? I don't see an unapply method on Function1, so I believe the answer is no.
Here's what I'm trying to do:
Example:
def foo(x: Any) = x match {
case ... // to handle the case of List[Any] => Any]?
case ...
}
Perhaps I can figure out the arity of x: Any to differentiate between List[Any] => Any versus everything else (_)?
EDIT:
I hope that I do not have to rely on f.toString == <function1>.

No, you can't match exactly on List[Any] => Any due to type erasure, but you can match on Function1 itself:
def foo(x: Any): String = x match {
case _: Function1[_, _] => "some function1"
case _ => "other"
}
Any other matching, like case _: (List[Any] => Any) => "function from list to any" will act the same as case _: Function1[_, _] => "some function":
scala> def foo(x: Any): String = x match {
| case _: (List[Any] => Any) => "function from list to any"
| case _ => "other"
| }
<console>:8: warning: non-variable type argument List[Any] in type pattern List[Any] => Any is unchecked since it is eliminated by erasure
case _: (List[Any] => Any) => "function from list to any"
^
foo: (x: Any)String
scala> def aaa(l: Any): Any = null //it's `Any => Any` - not `List[Any] => Any`!!
aaa: (l: Any)Any
scala> foo(aaa _)
res11: String = function from list to any
scala> foo(1)
res12: String = other

You can match purely on type, even without an unapply method:
def foo(x: Any): String = x match {
case _: (Any => Any) => "some function1"
case _ => "other"
}
And then you can check:
foo(f) //"some function1"
foo(1) //"other"

Related

Composing PartialFunctions with orElse when there is a wildcard case

Is it possible to orElse compose two PartialFunctions when the first function has case _ => wildcard pattern that matches anything thus in effect being a total function.
For example, given
val pf1: PartialFunction[Int, String] = {
case 1 => "foo"
case 2 => "bar"
case _ => "wildcard"
}
val pf2: PartialFunction[Int, String] = {
case 3 => "baz"
case _ => "wildcard"
}
then, out-of-the-box
(pf1 orElse pf2)(3)
outputs wildcard. However, assuming we cannot modify pf1, can we compose with pf2 using some compfn such that we get in effect
{
case 1 => "foo"
case 2 => "bar"
case 3 => "baz"
case _ => "wildcard"
}
where (pf1 compfn pf2)(3) would output baz?
You can turn pf1 into a true partial function by doing a second match to convert the "wildcard" result to a failed match.
val pf3: PartialFunction[Int, String] = (i: Int) => pf1(i) match {
case s if s != "wildcard" => s
}
And then
(pf3 orElse pf2)(3)
If you want the precise syntax you showed then you need to use an implicit class:
implicit class addCompfn(f1: PartialFunction[Int, String]) {
def compfn(f2: PartialFunction[Int, String]) = (i: Int) => f1(i) match {
case s if s != "wildcard" => s
case s => f2(i)
}
}
And then
(pf1 compfn pf2)(3)
Why not opting for something like this:
val pf1: PartialFunction[Int, String] = {
case 1 => "foo"
case 2 => "bar"
}
val pf2: PartialFunction[Int, String] = {
case 3 => "baz"
}
def composeOrElseWildcard(input: Int) = (pf1 orElse pf2).applyOrElse(input,(_: Int) => "wildcard")
Otherwise, to me, there are no solution to get rid of wildcard pattern effect in pf1.
( composeOrElseWildcard(3) gives "baz" and composeOrElseWildcard(4) gives "wildcard" )

Scala cast Any to Option[T]

Something like:
def cast[T](o: Any): Option[T] = o match {
case v: T => Some(v)
case _ => None
}
or:
def cast[T](c: Class[T], o: Any): Option[T] = o match {
case v: T => Some(v)
case _ => None
}
Is this a good idea? Is there a standard library equivalent?
Why do I get and how do I resolve the following Scala compiler warning:
Warning:(7, 13) abstract type pattern T is unchecked since it is eliminated by erasure
case v: T => Some(v)
Use class tag. Type information gets lost during runtime.So, you need provide type information which can be done using class tag.
import scala.reflect.ClassTag
def cast[T: ClassTag](o: Any): Option[T] = o match {
case v: T => Some(v)
case _ => None
}
Scala REPL
scala> cast[String]("hello")
res2: Option[String] = Some(hello)
scala> cast[Int]("scala")
res3: Option[Int] = None

Scala type erasure in pattern matching Map[String, Int]

In Scala 2.10, the compiler warns the given code:
private def getStrFromOpt[T](opt: Option[T]): String = opt match {
case Some(s: String) => s
case Some(i: Int) => i.toString()
case Some(l: Long) => l.toString()
case Some(m: Map[String, Int]) => m map ({ case (k, v) =>
"(" + k + ", " + v + ")" }) mkString ("(", ", ", ")")
case _ => ""
}
with the message non-variable type argument String in type pattern Map[String,Int] is unchecked since it is eliminated by erasure: case Some(m: Map[String, Int]) ....
How can I get rid of this warning? What if I will have a Map[String, MyObj] which I want to include as a case in this matching - how can I distinguish the two cases with parameterised Maps?
you can use Scala annotation #unchecked to suppress the warning,
for your second question, I suggest you use Scala reflection -- TypeTag. I am using Scala 2.11.4, here is the sample code, FYI.
import scala.reflect.runtime.{ universe => ru }
import scala.reflect.runtime.universe.{ typeTag, TypeTag }
object MapType extends App {
def getTypeTag[T: TypeTag](t: T) = typeTag[T].tpe
def getTypeTag[T: TypeTag] = ru.typeOf[T]
// context bound T: ru.TypeTag cause typeOf
// requires implicit parameter
def getStrFromOpt[T: TypeTag](opt: Option[T]): String = {
opt match {
case Some(s: String) => s
case Some(i: Int) => i.toString()
case Some(l: Long) => l.toString()
case Some(m: Map[String, Int] # unchecked)
if getTypeTag[T] =:= getTypeTag[Map[String, Int]] => "Int Map"
case Some(m: Map[String, String] # unchecked)
if getTypeTag[T] =:= getTypeTag[Map[String, String]] => "String Map"
case _ => ""
}
}
// "Int Map"
println(getStrFromOpt(Some(Map("a" -> 2, "b" -> 3))))
// "String Map"
println(getStrFromOpt(Some(Map("a" -> "2", "b" -> "3"))))
}
Actually, Scala uses the erasure model of generics just like Java does. So no information about type arguments is maintained at runtime.
def isIntMap(x: Any) = x match {
case m: Map[Int, Int] => true
case _ => false
}
For the code above, Scala compiler can not decide whether m is Map[Int, Int] or not. Therefore, isIntMap(Map("a"-> "b")) return true, which seems to be non-intuitive.
To alert you the runtime behavior, Scala compiler emitted that un-checked message.
However, Array is an Exception, its element type is stored with element value.
def isIntArray(x: Any) = x match {
case a: Array[String] => "yes"
case _ => "no"
}
scala> isIntArray(Array(3))
res1: String = no
scala> isIntArray(Array("1"))
res2: String = yes

Scala: Using a TypeTag to match on a Some's type

See the following code:
def createOption[T: TypeTag](referentialData: Any) : Option[T] = {
Option(referentialData) match {
case Some(camelMessage: CamelMessage) => {
Option(camelMessage.body) match {
case Some(option: T) => Some(option)
case _ => None
}
}
case _ => None
}
}
Basically I am looking to return an Option[T] if camelMessage.body is non-null and of type T.
The uses of Option(referentialData) is effectively referentialData != null
Likewise for Option(camelMessage.body)
How do I use the TypeTag to determine if camelMessage.body is of type T.
(I know this can be re-written to not use TypeTags and Options but I want to learn how to use TypeTags so please no suggestions to re-write, thanks!)
Edit
I tried a new approach as could not find a solution for the above, but could not get this one to work either:
def createOption[T](referentialData: Any) : Option[T] = {
Option(referentialData) match {
case Some(option) => Try(option.asInstanceOf[T]).toOption
case _ => None
}
}
When I invoke this using createOption[Long]("test") I was presuming to get a None back, but instead I got a Some(String)
Where am I going wrong here?
This is a duplicate of this one.
But you want to try it with ClassTag to show the limitation:
scala> def f[A: ClassTag](x: Any): Option[A] = x match {
| case y: A => println("OK"); Some(y) ; case _ => println("Nope"); None }
f: [A](x: Any)(implicit evidence$1: scala.reflect.ClassTag[A])Option[A]
scala> f[String]("foo")
OK
res0: Option[String] = Some(foo)
scala> f[Long](2L)
Nope
res1: Option[Long] = None
scala> f[java.lang.Long](new java.lang.Long(2L))
OK
res2: Option[Long] = Some(2)
scala> def f[A: TypeTag](x: Any): Option[A] = Option(x) match {
| case Some(y: A) => println("OK"); Some(y) ; case _ => println("Nope"); None }
<console>:51: warning: abstract type pattern A is unchecked since it is eliminated by erasure
case Some(y: A) => println("OK"); Some(y) ; case _ => println("Nope"); None }
^
f: [A](x: Any)(implicit evidence$1: reflect.runtime.universe.TypeTag[A])Option[A]

How to get around this scala compiler erasure warning?

I am trying to compile this scala code and getting the following compiler warnings.
scala> val props: Map[String, _] =
| x match {
| case t: Tuple2[String, _] => {
| val prop =
| t._2 match {
| case f: Function[_, _] => "hello"
| case s:Some[Function[_, _]] => "world"
| case _ => t._2
| }
| Map(t._1 -> prop)
| }
| case _ => null
| }
<console>:10: warning: non-variable type argument String in type pattern (String, _) is unchecked since it is eliminated by erasure
case t: Tuple2[String, _] => {
^
<console>:14: warning: non-variable type argument _ => _ in type pattern Some[_ => _] is unchecked since it is eliminated by erasure
case s:Some[Function[_, _]] => "world"
^
The answers given on How do I get around type erasure on Scala? Or, why can't I get the type parameter of my collections? seems to point to this same issue. But I couldn't infer a solution in this particular context.
Use case t # (_: String, _) instead of case t: Tuple2[String, _] and case s # Some(_: Function[_, _]) instead of case s:Some[Function[_, _]].
Scala cannot match on type parameters.
I would rewrite it like this:
x match {
case (name, method) => {
val prop =
method match {
case f: Function[_, _] => "hello"
case Some(f: Function[_, _]) => "world"
case other => other
}
Map(name -> prop)
}
case _ => null
}