Matching Type symbols in Scala - scala

I've asked a couple of questions regarding reflection in Scala the last few days, because it still seems new to me.
The new one is actually two questions that are related :
How would you create a function that returns a different result based on the input type?
Can you do the same with the TypeSymbol and Type objects returned by the reflection API?
Example (does not compile, but that's roughly how I want it to work) :
def result[T <: String] = "STRING"
def result[T <: Int] = "INT"
result[String] // Returns "STRING"
result[Int] // Returns "INT"
val type_symbol: Type = ... // Get a Type object from a reflection
type_symbol match {
case Int => "INT"
case String => "STRING"
case _ => "OTHER"
}

Maybe it's something like this you're looking for?
import scala.reflect.runtime.universe._
def someStringBasedOnAType[T: TypeTag] = typeOf[T] match {
case t if t =:= typeOf[Int] => "INT"
case t if t =:= typeOf[String] => "STRING"
case _ => "OTHER"
}
Results:
scala> someStringBasedOnAType[String]
res11: String = STRING
scala> someStringBasedOnAType[Float]
res12: String = OTHER
scala> someStringBasedOnAType[Int]
res13: String = INT
Be aware, however, that using TypeTags is thread-unsafe a the moment. As far as I know this should change in a few months, probably in Scala 2.10.2 (see SI-6240)

Related

How to find class parameter datatype at runtime in scala

import scala.reflect.runtime.universe
import scala.reflect.runtime.universe._
def getType[T: TypeTag](obj: T) = typeOf[T]
case class Thing(
val id: Int,
var name: String
)
val thing = Thing(1, "Apple")
val dataType = getType(thing).decl(TermName("id")).asTerm.typeSignature
dataType match {
case t if t =:= typeOf[Int] => println("I am Int")
case t if t =:= typeOf[String] => println("String, Do some stuff")
case _ => println("Absurd")
}
Not able to digest why result is Absurd instead of I am int?
My aim is to know data-type of class parameter at runtime and match it to predefined types.
Both current dataType and typeOf[Int] are printed as Int but if you do showRaw you'll see why they don't match
showRaw(dataType) // NullaryMethodType(TypeRef(ThisType(scala), scala.Int, List()))
showRaw(typeOf[Int]) // TypeRef(ThisType(scala), scala.Int, List())
The thing is that just the type Int and the type of nullary method returning Int are different types.
Try to add .resultType
val dataType = getType(thing).decl(TermName("id")).asTerm.typeSignature.resultType
dataType match {
case t if t =:= typeOf[Int] => println("I am Int")
case t if t =:= typeOf[String] => println("String, Do some stuff")
case _ => println("Absurd")
} // I am Int
It's also worth mentioning that .decl(TermName("id")) returns getter symbol, it's .decl(TermName("id ")) (with a blank space) that returns field symbol. So alternatively you can do with a blank space in the symbol name and without .resultType
val dataType = getType(thing).decl(TermName("id ")).asTerm.typeSignature
I'll add to #TomerShetah's answer that if the goal is "pattern matching" all fields of a case class then this can be done also at compile time (mostly) with Shapeless:
import shapeless.Poly1
import shapeless.syntax.std.product._
object printTypes extends Poly1 {
implicit val int: Case.Aux[Int, Unit] = at(t => println(s"I am Int: $t"))
implicit val string: Case.Aux[String, Unit] = at(t => println(s"String, Do some stuff: $t"))
implicit def default[V]: Case.Aux[V, Unit] = at(t => println(s"Absurd: $t"))
}
thing.toHList.map(printTypes)
// I am Int: 1
// String, Do some stuff: Apple
https://scastie.scala-lang.org/DmytroMitin/N4Idk4KcRumQJZE2CHC0yQ
#Dmytrio answer is a great explanation why the reflection didn't work as you expected.
I can understand from your question, that what you are trying to do, is actually pattern match all variables you have in a case class. Please consider doing it in the following way:
case class Thing(id: Int, name: String)
val thing = Thing(1, "Apple")
thing.productIterator.foreach {
case t: Int => println(s"I am Int: $t")
case t: String => println(s"String, Do some stuff: $t")
case t => println(s"Absurd: $t")
}
Code run at Scastie.

it is possible to cast to wrong types without exception in scala

I wrote a code the other day to filter out mixing behavior form a list.
Her is an example code which should describe the problem I ran into.
def myFilter[A](toFilter : Any) : Option[A] = toFilter match {
case keep : A => Some(keep)
case _ => None
}
// what happens
myFilter[Int]("hallo") // => Option[Int] = Some(hallo)
// what I expect
myFilter[Int]("hallo") // => Option[Int] = None
myFilter[Int](1) // => Option[Int] = Some(1)
Maybe I'm doing something completely wrong, but it created a lot of problems on my side, I have to create a lot of code now, which I was hoping to make more readable by this function.
Just provide a ClassTag:
scala> import scala.reflect.ClassTag
import scala.reflect.ClassTag
scala> def myFilter[A: ClassTag](toFilter : Any) : Option[A] = toFilter match {
| case keep : A => Some(keep)
| case _ => None
| }
myFilter: [A](toFilter: Any)(implicit evidence$1: scala.reflect.ClassTag[A])Option[A]
scala> myFilter[Int]("hallo")
res2: Option[Int] = None
scala> myFilter[String]("hallo")
res3: Option[String] = Some(hallo)
The type went away due Type Erasure. You can however provide the type, try something like
def myFilter[A](toFilter : Any)(implicit classTag: ClassTag[A]) : Option[A] = toFilter match {
case keep : A => Some(keep)
case _ => None
}

pattern matching function signatures with ClassTags/TypeTags

I have a method that explores different parameter values for different classes of functions. Previously, I did some un-typesafe runtime checks, where, post-erasure, all that mattered was that I was using a Function2 versus a Function3. I've taking a stab at using ClassTags/TypeTags to be a bit safer, but I'm still struggling for a good solution. My original reference point was
tksfz's answer to:
How do I get around type erasure on Scala? Or, why can't I get the type parameter of my collections?
import reflect.ClassTag
def matchList[A: ClassTag](list: List[A]) = list match {
case strlist: List[String] => println("A list of strings!")
case intlist: List[Int] => println("A list of ints!")
case _ => println("Ok")
}
However, I found that the snippet as given didn't work, and both cases match a list of strings -- it may be that the answer concerns a different version of scala (I'm using 2.10.3).
I found another example, in which someone gave a snippet using TypeTags (and I'm still a bit hazy on what the difference is between the type tag types; I'd be happy for an RTFM response on that point, provided it's accompanied by a link to a good M to FR).
EDIT: My earlier example didn't apply the functions, so the parts of the matching that produced warnings were pointless (as the first answerer pointed out). I've updated the example so that it's more representative of my actual problem.
package misc
import reflect.runtime.universe._
object CTDemo {
def main(args: Array[String]) = {
val f0v: Function2[Int, Double, String] = f0(_, _)
val f1v: Function2[Int, (Double, Double), String] = f1(_, _)
println(matchFunc(f0v))
println(matchFunc(f1v))
}
def f0(i: Int, p: Double) = {
s"output on $p doesn't matter"
}
def f1(i: Int, p: (Double, Double)) = {
s"output on $p doesn't matter"
}
def matchFunc[I: TypeTag, A: TypeTag, O: TypeTag](predFunc: Function2[I, A, O]) = {
predFunc match {
case fs: Function2[Int, Double, String] if(typeOf[A] <:< typeOf[Double]) => {
"Single-arg, result is: " + fs(1,2.0)
}
case ds: Function2[Int, (Double, Double), String] if(typeOf[A] <:< typeOf[(Double,Double)]) => {
"Double-arg, result is: " + ds(1,(2.0,3.0))
}
}
}
}
This case works, but it still throws compiler warnings. Is there a succinct, "asInstanceOf"-free, warning-free, and thread-safe way to check the type signature of functions? I'm willing to upgrade to scala 2.11 if that would help.
Your matches aren't doing any work, so just
def matchFunc[I: TypeTag, A: TypeTag, O: TypeTag](predFunc: Function2[I, A, O]) = {
if (typeOf[A] <:< typeOf[Double]) {
"Single-arg"
} else if (typeOf[A] <:< typeOf[(Double,Double)]) {
"Double-arg"
}
}
The other idiom works thisaway:
scala> def g[A: reflect.ClassTag](as: List[A]) = as match { case List(_: Int) => "I" }
g: [A](as: List[A])(implicit evidence$1: scala.reflect.ClassTag[A])String
scala> g(List(3))
res4: String = I
scala> g(List(3.0))
scala.MatchError: List(3.0) (of class scala.collection.immutable.$colon$colon)
at .g(<console>:7)
... 33 elided
That is, as a glorified (albeit convenient) isInstanceOf.
In Scala, it's RTFS, where S = spec or SIP.

Using typetags to match for java overloaded vararg method, mapping over Try

I have an overloaded java method call that can take either String or InetAddress vararg parameters. To reduce complexity I wanted to convert a sequence of strings to a sequence of InetAddresses before calling the java method. In converting a String to an InetAddress I only want to allow valid values
Having see this on typetags and this on Try I came up with:
import scala.reflect.runtime.universe._
import java.net.InetAddress
import scala.util.Try
type InetAddr = java.net.InetAddress
def tryMap[A](xs: Seq[Try[A]]) =
Try(Some(xs.map(_.get))).
getOrElse(None)
def toInet(address: String): Try[InetAddr] = {
Try(InetAddress.getByName(address))
}
def stringToInet[A: TypeTag](xs: Seq[A]): Option[Seq[InetAddr]] = typeOf[A] match {
case t if t =:= typeOf[String] => tryMap(xs.map(toInet)).getOrElse(None)
case t if t =:= typeOf[InetAddr] => Some(xs)
case _ => None
}
But I get the error
<console>:18: error: type mismatch;
found : String => scala.util.Try[InetAddr]
required: A => ?
case t if t =:= typeOf[String] => tryMap(xs.map(toInet)).getOrElse(None)
I am not sure if I am close to getting something workable or wildly off track and would appreciate some guidance in finding a solution.
The problem is that even if you know the type tag is equal to the type you want, that doesn't change the type of xs, so you would need to do something like this:
import scala.reflect.runtime.universe._
import java.net.InetAddress
import scala.util.Try
def tryMap[A](xs: Seq[Try[A]]) =
Try(Some(xs.map(_.get))).
getOrElse(None)
def toInet(address: String): Try[InetAddress] = {
Try(InetAddress.getByName(address))
}
def stringToInet[A: TypeTag](xs: Seq[A]): Option[Seq[InetAddress]] = typeOf[A] match {
case t if t =:= typeOf[String] =>
tryMap(xs.asInstanceOf[Seq[String]].map(toInet)) // don't need the getOrElse, it's already an option
case t if t =:= typeOf[InetAddress] =>
Some(xs.asInstanceOf[Seq[InetAddress]])
case _ => None
}
The asInstanceOf is kind of ugly, but necessary to do what you want.

Why no partial function type literal?

I wonder why there doesn't exist a literal for partial function types. I have to write
val pf: PartialFunction[Int, String] = {
case 5 => "five"
}
where an literal like :=> would be shorter:
val pf: Int :=> String = {
case 5 => "five"
}
Partial functions are often used and in Scala already some "special" feature, so why no special syntax for it?
Probably in part because you don't need a literal: you can always write your own :=> as a type infix operator if you want more concise syntax:
scala> type :=>[A, B] = PartialFunction[A, B]
defined type alias $colon$eq$greater
scala> val pf: Int :=> String = { case 5 => "five" }
pf: :=>[Int,String] = <function1>
scala> pf.isDefinedAt(0)
res0: Boolean = false
scala> pf.isDefinedAt(5)
res1: Boolean = true
I'm not one of the designers of the Scala language, though, so this is more or less a guess about the "why?". You might get better answers over at the scala-debate list, which is a more appropriate venue for language design questions.