2

I want to implement a subclass that includes a type parameter, and write implementations of functions that behave differently depending on whether arguments conform to the parameter.

Consider:

import scala.reflect.runtime.universe;

trait Lover {
    def love( amour : Any ) : String
}

class TypedLover[MY_TYPE]( implicit val myTypeTag : universe.TypeTag[MY_TYPE] ) extends Lover {
    def love( amour : Any ) : String =
        if ( ??? ) "You are totally my type." else "Nope, sorry, not my type."
}

What would I use for the conditional here? [Update: The conditional should return true if the function argument amour conforms to MY_TYPE, false otherwise.]

Many thanks for any help.

Steve Waldman
  • 13,689
  • 1
  • 35
  • 45
  • OK. This topic looks like it may become the basis for an answer: http://stackoverflow.com/questions/11628379/how-to-know-if-an-object-is-an-instance-of-a-typetags-type – Steve Waldman Mar 12 '13 at 11:26
  • Nope -- won't work on this example as written, because http://stackoverflow.com/questions/11628379/how-to-know-if-an-object-is-an-instance-of-a-typetags-type captures compile-time type only. however, maybe we can modify to let the love method to be generic, so we can at least capture discriminate based on types known at compile time. – Steve Waldman Mar 26 '13 at 02:38

4 Answers4

3
class TypedLover[A](implicit tt: TypeTag[A]) extends Lover {
  def love(amour: Any) =
    if (tt.tpe =:= typeOf[String]) "You are totally my type."
    else "Nope, sorry, not my type."
}

scala> new TypedLover[String].love(null)
res2: String = You are totally my type.

scala> new TypedLover[Int].love(null)
res3: String = Nope, sorry, not my type.

For a detailed introduction in how a TypeTag works, see this question: Scala: What is a TypeTag and how do I use it?

Community
  • 1
  • 1
kiritsuku
  • 52,967
  • 18
  • 114
  • 136
  • Thanks. I had seen that post, and you'll note do pull in the TypeTag in my example. But I don't know where to go from there. This is closer, but I want to discriminate based in the runtime type of the argument, not based on something like typeOf[String] known at compile time. In other words, if and only if the value of amour conforms to type A, then "You are totally my type." I'd like to use something like "tt.tpe =:= amour.getClass", but that doesn't work, complains of a type mismatch. (i really want "amour.getClass <:< tt.tpe") – Steve Waldman Mar 07 '13 at 07:40
2

I think you want something like this:

trait Lover {
  def love( amour : Any ) : String
}

class TypedLover[A : ClassTag] extends Lover {
    def love(amour : Any) = {
        if (implicitly[ClassTag[A]].runtimeClass == amour.getClass) {
                "Totally my type."
            } else {
                "Sorry, you are not my type."
            }
    }
}

val tl = new TypedLover[String]
tl.love(1) // res0: String = Sorry, you are not my type.
tl.love("Hello") //res1: String = Totally my type.

Note that you can use methods such as isAssignableFrom in place of == if you want to capture subtyping.

Impredicative
  • 5,039
  • 1
  • 17
  • 43
  • Yes, that's pretty much the best you can do when the parameter is of type `Any`. Note that the comparison is subject to erasure so for example both `List[Int]` and `List[String]` will erase to `List` and compare equal. – Jesper Nordenberg Mar 07 '13 at 10:33
  • Impredicative -- Thank you. This is definitely the closest yet to what I want, but I'm surprised if the best I can do is normalize on Java reflective constructs. (I would use isAssignableFrom.) There's no way I can ask at whether a runtime Object conforms to the richer information in a Scala Type? – Steve Waldman Mar 07 '13 at 11:09
  • Jesper -- Why does use of the parameter type Any complicate things? Suppose it were something narrower, AnyRef or MyTrait or MyBaseClass. Obviously, the problem would be simpler if the base were something sealed, we could just exhaustively check the compile-time known possibilities, but that is too limiting a case. – Steve Waldman Mar 07 '13 at 11:14
  • Well, if the signature would be `def love[T: TypeTag](amour: T)` you could obviously do a more precise type check. – Jesper Nordenberg Mar 07 '13 at 11:30
0

Scala have a way of getting around with type erasure, using Manifest class. A link to my gist illustrating the usage - https://gist.github.com/anoopelias/4462155

anoopelias
  • 9,240
  • 7
  • 26
  • 39
  • hi. first, thanks for the reply! i have code (in the constructor of TypedLover) to capture a TypeTag for MY_TYPE. i think that TypeTag largely replaces Manifests in Scala 2.10 (but i could be mistaken!). but i don't know how to use the TypeTag to check whether a runtime parameter conforms to that type. – Steve Waldman Mar 07 '13 at 05:57
0

This is the best that I have been able to do. It doesn't capture true runtime type information, which is what I want, but it does at least capture the type information known at the point of use, at compile time:

import scala.reflect.runtime.universe._

trait Lover {
  def love[ T : TypeTag ]( amour : T ) : String;
}

class TypedLover[MY_TYPE : TypeTag] {
  def love[ T : TypeTag ]( amour : T ) : String =
    if ( implicitly[TypeTag[T]].tpe <:< implicitly[TypeTag[MY_TYPE]].tpe )
      "You are totally my type."
    else
      "Nope, sorry, not my type."
}

Here's how it works:

scala> val intLover = new TypedLover[Int]
intLover: TypedLover[Int] = TypedLover@2d4cadc4

scala> intLover.love(7)
res2: String = You are totally my type.

scala> intLover.love("Hello")
res3: String = Nope, sorry, not my type.

scala> val stringLover = new TypedLover[String]
stringLover: TypedLover[String] = TypedLover@1f7c9157

scala> stringLover.love(7)
res4: String = Nope, sorry, not my type.

scala> stringLover.love("Hello")
res5: String = You are totally my type.

But here's how it still fails to capture actual runtime type information:

scala> val stringInAnyRef : Any = "Hello"
stringInAnyRef: Any = Hello

scala> stringLover.love( stringInAnyRef )
res7: String = Nope, sorry, not my type.

It isn't perfect, but it's the best I've got so far.

Steve Waldman
  • 13,689
  • 1
  • 35
  • 45