Is it possible to match the result of Symbol.typeSignature in a case class

423 views
Skip to first unread message

berryware

unread,
Mar 30, 2014, 9:29:27 AM3/30/14
to scala...@googlegroups.com
I am trying to get the type of a field in the class. All I know is the string name for the field and want to identify its type so that I can take appropriate action. Here is my code. I am using the universe definitions for intrinsic types(Boolean, Int, Long, Double) but am not sure how to get the types for String and DateTime. 

import scala.reflect.runtime.{universe => ru}
import scala.reflect.runtime.universe.definitions._

  private def processField(name:String)(implicit tag : ru.TypeTag[T] ) = {
    tag.tpe.member(ru.newTermName(key)) match {
      case ru.NoSymbol => // do something if the field does not exist
      case s:Symbol => s.typeSignature. match {
          case BooleanTpe => // do Boolean logic
          case IntTpe => // do Int Logic
          case LongTpe => // do Long Logic
          case DoubleTpe => // do Double Logic
          case DateTime => // do DateTime Logic
          case String => // do String logic
        }
    }
  }

Eugene Burmako

unread,
Mar 30, 2014, 9:31:04 AM3/30/14
to berryware, scala-user
I think you're looking for ru.typeOf[String] and ru.typeOf[DateTime].


--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

berryware

unread,
Mar 30, 2014, 9:58:17 AM3/30/14
to scala...@googlegroups.com, berryware
I tried that already. Compile throws this message when I do that:

type typeOf is not a member of scala.reflect.api.JavaUniverse 

Eugene Burmako

unread,
Mar 30, 2014, 9:59:38 AM3/30/14
to scala...@googlegroups.com, berryware
Could you show the code that the compiler complains about?

berryware

unread,
Mar 30, 2014, 10:14:05 AM3/30/14
to scala...@googlegroups.com, berryware

File attached to post. error below.

$ scala TypeTest.scala

/project/scala/reflection/TypeTest.scala:13: error: type typeOf is not a member of scala.reflect.api.JavaUniverse

    case ru.typeOf[String] => // do String logic

TypeTest.scala

Eugene Burmako

unread,
Mar 30, 2014, 10:17:05 AM3/30/14
to berryware, scala-user
I see. Using typeOf as a pattern directly wouldn't work. You'll need something like `case tpe if tpe =:= typeOf[String]`.

Also, note the =:= part. Depending on your classloader configuration, == (or eq used for matches like `case ByteTpe`) might or might not work. =:= is the only reliable way to compare types.


--

berryware

unread,
Mar 30, 2014, 10:37:42 AM3/30/14
to scala...@googlegroups.com, berryware
Thanks. Compiling now. 

berryware

unread,
Mar 30, 2014, 11:04:17 AM3/30/14
to scala...@googlegroups.com, berryware
Side note for anyone else reading this and having the same problem and using IntelliJ. When I added org.joda.time.DateTime into the pattern match I got the following error.

not found: type TypeCreator

[error]           case tpe if tpe =:= typeOf[org.joda.time.DateTime] => // do DateTime logic

This is fixed by a simple import of 

import scala.reflect.api.TypeCreator

Because there is no reference to TypeCreator in the code, IntelliJ thinks that this is an unused import. I normally have "optimize imports" checked for commits. Running this manually removes this import and breaks the build. Make sure you turn organize imports off.

Eugene Burmako

unread,
Mar 30, 2014, 11:05:47 AM3/30/14
to berryware, scala-user
Could you share your sbt project? This should not happen.

berryware

unread,
Mar 30, 2014, 2:37:36 PM3/30/14
to scala...@googlegroups.com, berryware
Sorry it took so long. It appears the message is a phantom. It is getting generated when another error happens. If I fix the other error both errors go away. I had fixed this one first last time and thought that it was its own error. 

I have included my play project with the error occurring.  It will show two errors. When you comment out the method def and uncomment the working one, both errors disappear.

Dave

playReflectTest.tgz

berryware

unread,
Mar 31, 2014, 8:07:39 AM3/31/14
to scala...@googlegroups.com, berryware
Eugene,

I have another question for you. In my first test of this code, the Symbol.toString that is returned is "value duration". duration is the correct attribute of the class. The Type.toString that is returned is "=> Int". This Type is not matching typeOf[Int] or weakTypeOf[Int]. How do I dereference "=> Int" to "Int"?

Thanks,

Dave 

Eugene Burmako

unread,
Mar 31, 2014, 10:41:52 AM3/31/14
to berryware, scala-user
If it's 2.10.x, you have to pattern match against NullaryMethodType(res), and res will be the normal Int, not => Int. If it's 2.11.0, then instead of pattern matching you can use the newly introduced Type.resultType.

berryware

unread,
Mar 31, 2014, 4:56:13 PM3/31/14
to scala...@googlegroups.com, berryware
For all those following along at home, here is what I got to work:

  def processField(name:String, sval:String)(implicit tag : TypeTag[T] ) : (String, BSONValue) = tag.tpe.member(newTermName(name)) match {
        case s:SymbolApi => 
          val actualType = s.typeSignature.baseType(s.typeSignature.typeSymbol)
          name -> (s.typeSignature.toString match {
            case tpe if typeOf[Boolean] =:= actualType          => BSONBoolean(sval.toBoolean)
            case tpe if typeOf[Option[Boolean]] =:= actualType  => BSONBoolean(sval.toBoolean)
            case tpe if typeOf[Int] =:= actualType              => BSONInteger(sval.toInt)
            case tpe if typeOf[Option[Int]] =:= actualType      => BSONInteger(sval.toInt)
            case tpe if typeOf[Long] =:= actualType             => BSONLong(sval.toLong)
            case tpe if typeOf[Option[Long]] =:= actualType     => BSONLong(sval.toLong)
            case tpe if typeOf[Double] =:= actualType           => BSONDouble(sval.toDouble)
            case tpe if typeOf[Option[Double]] =:= actualType   => BSONDouble(sval.toDouble)
            case tpe if typeOf[DateTime] =:= actualType         => BSONDateTime(sval.toLong)
            case tpe if typeOf[Option[DateTime]] =:= actualType => BSONDateTime(sval.toLong)
            case _ => BSONRegex(sval.replaceAll("\\*", ".*"),"i")  // default is a String
          })
        case _ => name -> BSONString(sval)
      }

This code is part of a rest api that queries a MongoDB via reactiveMongo. When query parameters come that filter the search, the query parameters are turned into real BSONValues for a BSONDocument used to query MongoDB. The parameter name is the same as the attribute name on the class so reflection is used to identify the type of the attribute for the given name and then convert it to the corresponding BSONValue. The way I was able to get the actual type was with the line:

 val actualType = s.typeSignature.baseType(s.typeSignature.typeSymbol)

Once I was able to get the actual type the pattern matching started working. I was not able to get the NullaryMethodType working but this works so I am good for now. 

Thank you Eugene for all your help. 


Kevin Wright

unread,
Mar 31, 2014, 5:02:36 PM3/31/14
to berryware, scala-user
Some patterns never die... Here's how it was done back in the days of Manifests:

Eugene Burmako

unread,
Mar 31, 2014, 5:08:27 PM3/31/14
to berryware, scala-user
Why the s: SymbolApi match? Type.member always returns a Symbol.

Also, I'm not quite getting the "s.typeSignature.toString" part. That expression is never used, right? So why not write the pattern match as a series of ifs?


berryware

unread,
Mar 31, 2014, 5:31:06 PM3/31/14
to scala...@googlegroups.com, berryware
I was trying so many things to get this to work that the code is still a bit of a mess. The toString is because I originally got it working matching on the "=> Int" type signature. It was ugly but it was working. SymbolApi I can't actually remember why it is there but that I was orginally matching on NoSymbol in the event that the name does not correspond to a valid attribute.

Here is another go at it:

def processField(name:String, sval:String)(implicit tag : TypeTag[T] ) : (String, BSONValue)  = tag.tpe.member(newTermName(name)) match {
        case NoSymbol => name -> BSONString(sval)
        case s => name -> (s.typeSignature.baseType(s.typeSignature.typeSymbol) match {
            case tpe if typeOf[Boolean] =:= tpe          => BSONBoolean(sval.toBoolean)
            case tpe if typeOf[Option[Boolean]] =:= tpe  => BSONBoolean(sval.toBoolean)
            case tpe if typeOf[Int] =:= tpe              => BSONInteger(sval.toInt)
            case tpe if typeOf[Option[Int]] =:= tpe      => BSONInteger(sval.toInt)
            case tpe if typeOf[Long] =:= tpe             => BSONLong(sval.toLong)
            case tpe if typeOf[Option[Long]] =:= tpe     => BSONLong(sval.toLong)
            case tpe if typeOf[Double] =:= tpe           => BSONDouble(sval.toDouble)
            case tpe if typeOf[Option[Double]] =:= tpe   => BSONDouble(sval.toDouble)
            case tpe if typeOf[DateTime] =:= tpe         => BSONDateTime(sval.toLong)
            case tpe if typeOf[Option[DateTime]] =:= tpe => BSONDateTime(sval.toLong)

Eugene Burmako

unread,
Mar 31, 2014, 5:35:25 PM3/31/14
to berryware, scala-user
Would it work if you write:

s.typeSignature match { case NullaryMethodType(res) if typeOf[X] =:= res => ... }

instead of:

s.typeSignature.baseType(s.typeSignature.typeSymbol) match { case tpe if typeOf[X] =:= tpe => ... }

berryware

unread,
Mar 31, 2014, 6:08:43 PM3/31/14
to scala...@googlegroups.com, berryware
That worked, thanks.  I misread read your original post on the NullaryMethodType
Reply all
Reply to author
Forward
0 new messages