Composing PartialFunctions with orElse when there is a wildcard case - scala

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

Related

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

Pattern Matching/Checking Arity of Function1

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"

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]

Generic unapply method for different types of List

Is there a way to generalize this code with generics?
object ListInt {
def unapply(o: Any): Option[List[Int]] = o match {
case lst: List[_] if(lst.forall(_.isInstanceOf[Int])) =>
Some(lst.asInstanceOf[List[Int]])
case _ => None
}
}
object ListDouble {
def unapply(o: Any): Option[List[Double]] = o match {
case lst: List[_] if(lst.forall(_.isInstanceOf[Double])) =>
Some(lst.asInstanceOf[List[Double]])
case _ => None
}
}
object ListString {
def unapply(o: Any): Option[List[String]] = o match {
case lst: List[_] if(lst.forall(_.isInstanceOf[String])) =>
Some(lst.asInstanceOf[List[String]])
case _ => None
}
}
val o: Any = List("a", "b", "c")
o match {
case ListInt(lst) => println(lst.sum)
case ListDouble(lst) => println(lst.product)
case ListString(lst) => println(lst.mkString("(", ", ", ")"))
case _ => println("no match")
}
abstract class ListExtractor[A](implicit ct: reflect.ClassTag[A]) {
def unapply(o: Any): Option[List[A]] = o match {
case lst: List[_] if (lst.forall(ct.unapply(_).isDefined)) =>
Some(lst.asInstanceOf[List[A]])
case _ => None
}
}
object ListInt extends ListExtractor[Int ]
object ListString extends ListExtractor[String]
val o: Any = List("a", "b", "c")
o match {
case ListInt (lst) => println(lst.sum)
case ListString(lst) => println(lst.mkString("(", ", ", ")"))
case _ => println("no match")
}
It seems TypeTag is the way to go:
import scala.reflect.runtime.universe._
def foo[A: TypeTag](lst: A) = typeOf[A] match {
case t if t =:= typeOf[List[Int]] => lst.asInstanceOf[List[Int]].sum
case t if t =:= typeOf[List[Double]] => lst.asInstanceOf[List[Double]].product
case t if t =:= typeOf[List[String]] => lst.asInstanceOf[List[String]].mkString("(", ", ", ")")
}
println(foo(List("a", "b", "c")))
Check this excellent post for detailed explanation:
Scala: What is a TypeTag and how do I use it?

How to avoid syntax overhead for def definition with pattern matching in Scala?

How to avoid to wrap args when implementing a def with pattern matching ?
Examples :
def myDef(a: A, b:B, c: C): D = (a,c,d) match {
case ('qsdqsd, _ , _ ) => ???
case _ => ???
}
You can take the tuple as the function argument instead:
def myDef(abc: (A,B,C)): D = abc match {
case ('qxpf, _, _) => ???
case _ => ???
}
Users will have their non-tuple argument lists automatically promoted into tuples. Observe:
scala> def q(ab: (Int,String)) = ab.swap
q: (ab: (Int, String))(String, Int)
scala> q(5,"Fish")
res1: (String, Int) = (Fish,5)
You could declare it as a PartialFunction so that you could use the case-block directly. This works because a block of cases is a PartialFunction in Scala.
val myDef: PartialFunction[(A, B, C), D] = {
case ("qsdqsd", b, c) => b + c
case _ => "no match"
}
println(myDef("qsdqsd", "b", "c"))
println(myDef("a", "b", "c"))