How to make TypeTag work in a pattern?

1,305 views
Skip to first unread message

Dave

unread,
Jun 22, 2012, 4:10:15 PM6/22/12
to scala-user
The TypeTag for type parameter A doesn't seem to work inside a
pattern.
Is this a bug? What is the syntax to make this work?

reifiedpattern.scala
==============
package reifiedpattern
import reflect.TypeTag
class Test[A:TypeTag]{
def m(x: Any) = x match { case y: A => println("Found match " + y)
case _ => println("No match")
}

}
object Main {
val test = new Test[Int]
test.m(1)
test.m(2.0)
test.m("test")

val test2 = new Test[String]
test2.m(1)
test2.m(2.0)
test2.m("test")
}


C:\scala-2.10.0-M4\myexamples2>scalac -unchecked reifiedpattern.scala
reifiedpattern.scala:4: warning: abstract type A in type pattern A is
unchecked
since it is eliminated by erasure
def m(x: Any) = x match { case y: A => println("Found match" + y)
^
one warning found

C:\scala-2.10.0-M4\myexamples2>scala reifiedpattern.Main
java.lang.NoSuchMethodException:
reifiedpattern.Main.main([Ljava.lang.String;)
at java.lang.Class.getMethod(Class.java:1605)
at scala.tools.nsc.util.ScalaClassLoader
$class.run(ScalaClassLoader.scal
a:67)
at scala.tools.nsc.util.ScalaClassLoader
$URLClassLoader.run(ScalaClassLo
ader.scala:139)
at scala.tools.nsc.CommonRunner$class.run(ObjectRunner.scala:
28)
at scala.tools.nsc.ObjectRunner$.run(ObjectRunner.scala:45)
at scala.tools.nsc.CommonRunner
$class.runAndCatch(ObjectRunner.scala:35)

at scala.tools.nsc.ObjectRunner
$.runAndCatch(ObjectRunner.scala:45)
at scala.tools.nsc.MainGenericRunner.runTarget
$1(MainGenericRunner.scala
:70)
at
scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:92)

at scala.tools.nsc.MainGenericRunner
$.main(MainGenericRunner.scala:101)
at
scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)

Daniel Sobral

unread,
Jun 22, 2012, 7:15:00 PM6/22/12
to Dave, scala-user
object Main doesn't have a "main" method, nor does it extend App.
--
Daniel C. Sobral

I travel to the future all the time.

Daniel Sobral

unread,
Jun 22, 2012, 7:19:16 PM6/22/12
to Dave, scala-user, Eugene Burmako, adriaa...@epfl.ch
This is about https://github.com/scala/scala/commit/6b3ef4f1676adcbe6dbdbf59a3bd359f339b0626,
right? I also wondered why TypeTag wasn't made available as well,
though I did see something in another commit about having a clear
split between TypeTag and ClassTag. Still, I wonder...

Dave

unread,
Jun 23, 2012, 5:52:07 AM6/23/12
to scala-user
Thanks,
I forgot "extends App" which I write normally automatically after
'object Main'

But the problem is known

Just for completeness I submit here the code and the output:

reifiedpattern.scala
====================
package reifiedpattern
import reflect.TypeTag
class Test[A:TypeTag]{

def m(x: Any) = x match { case y: A => println("Found match " + y)
case _ => println("No match")
}

}
object Main extends App {
val test = new Test[Int]
test.m(1)
test.m(2.0)
test.m("test")

val test2 = new Test[Double]
test2.m(1)
test2.m(2.0)
test2.m("test")

val test3 = new Test[String]
test3.m(1)
test3.m(2.0)
test3.m("test")
}


C:\scala-2.10.0-M4\myexamples2>scalac -unchecked reifiedpattern.scala
reifiedpattern.scala:5: warning: abstract type A in type pattern A is
unchecked
since it is eliminated by erasure
def m(x: Any) = x match { case y: A => println("Found match " + y)
^
one warning found

C:\scala-2.10.0-M4\myexamples2>scala reifiedpattern.Main
Found match 1
Found match 2.0
Found match test
Found match 1
Found match 2.0
Found match test
Found match 1
Found match 2.0
Found match test


Expected output:
Found match 1
No match
No match
No match
Found match 2.0
No match
No match
No match
Found match test











On 23 jun, 01:19, Daniel Sobral <dcsob...@gmail.com> wrote:
> This is abouthttps://github.com/scala/scala/commit/6b3ef4f1676adcbe6dbdbf59a3bd359...,
> right? I also wondered why TypeTag wasn't made available as well,
> though I did see something in another commit about having a clear
> split between TypeTag and ClassTag. Still, I wonder...
>
>
>
>
>
> On Fri, Jun 22, 2012 at 8:15 PM, Daniel Sobral <dcsob...@gmail.com> wrote:
> > object Main doesn't have a "main" method, nor does it extend App.
>
> I travel to the future all the time.- Tekst uit oorspronkelijk bericht niet weergeven -
>
> - Tekst uit oorspronkelijk bericht weergeven -

Eugene Burmako

unread,
Jun 23, 2012, 11:09:58 AM6/23/12
to Daniel Sobral, Dave, scala-user, adriaa...@epfl.ch
We definitely discussed using type tags to assist pattern matching, but, I think, Adriaan has more important things to do right now.

Daniel Sobral

unread,
Jun 23, 2012, 12:25:54 PM6/23/12
to Eugene Burmako, Dave, scala-user, adriaa...@epfl.ch
His solution for ClassTag was extractors. It looked relatively simple,
and, given that ClassTag extractors are implemented, perhaps expanding
it to cover TypeTag wouldn't be much difficult.

I'm just not sure how this goes at runtime. A TypeTag is related to
the stuff stored in an annotation, isn't it? I imagine that erasure
would have to apply anyway, since annotations are stored on the class,
which is itself generic. Would that require scala-reflect.jar to work?

I'm still feeling my way on what requires scala-reflect.jar and what
does not, and REPL seems to load it automatically, so I don't stumble
upon the limits automatically as I do my exploring.

Daniel Sobral

unread,
Jun 23, 2012, 12:27:16 PM6/23/12
to Eugene Burmako, Dave, scala-user, adriaa...@epfl.ch
Or... if one could derive a ClassTag from a TypeTag, it would be just
a matter of making an implicit inside the body of the function. But
that's not possible, right?

Eugene Burmako

unread,
Jun 23, 2012, 12:38:04 PM6/23/12
to Daniel Sobral, Dave, scala-user, adriaa...@epfl.ch
Deriving a class tag from a type tag requires scala-reflect.jar.

Dave

unread,
Jun 23, 2012, 6:29:25 PM6/23/12
to scala-user
With ClassTag

package reifiedpattern
import reflect.ClassTag
class Test[A:ClassTag]{

def m(x: Any) = x match { case y: A => println("Found match " + y)
case _ => println("No match")
}

}
object Main extends App {
val test = new Test[Int]
test.m(1)
test.m(2.0)
test.m("test")

val test2 = new Test[Double]
test2.m(1)
test2.m(2.0)
test2.m("test")

val test3 = new Test[String]
test3.m(1)
test3.m(2.0)
test3.m("test")
}

I get:

C:\scala-2.10.0-M4\myexamples2>scala reifiedpattern.Main
No match
No match
No match
No match
No match
No match
No match
No match
Found match test

So it seems to work for String only and only with ClassTag


On 23 jun, 18:38, Eugene Burmako <eugene.burm...@epfl.ch> wrote:
> Deriving a class tag from a type tag requires scala-reflect.jar.
>
> On 23 June 2012 18:27, Daniel Sobral <dcsob...@gmail.com> wrote:
>
>
>
> > Or... if one could derive a ClassTag from a TypeTag, it would be just
> > a matter of making an implicit inside the body of the function. But
> > that's not possible, right?
>
> > On Sat, Jun 23, 2012 at 1:25 PM, Daniel Sobral <dcsob...@gmail.com> wrote:
> > > His solution for ClassTag was extractors. It looked relatively simple,
> > > and, given that ClassTag extractors are implemented, perhaps expanding
> > > it to cover TypeTag wouldn't be much difficult.
>
> > > I'm just not sure how this goes at runtime. A TypeTag is related to
> > > the stuff stored in an annotation, isn't it? I imagine that erasure
> > > would have to apply anyway, since annotations are stored on the class,
> > > which is itself generic. Would that require scala-reflect.jar to work?
>
> > > I'm still feeling my way on what requires scala-reflect.jar and what
> > > does not, and REPL seems to load it automatically, so I don't stumble
> > > upon the limits automatically as I do my exploring.
>
> > > On Sat, Jun 23, 2012 at 12:09 PM, Eugene Burmako <eugene.burm...@epfl.ch>
> > wrote:
> > >> We definitely discussed using type tags to assist pattern matching,
> > but, I
> > >> think, Adriaan has more important things to do right now.
>
> > >> On 23 June 2012 01:19, Daniel Sobral <dcsob...@gmail.com> wrote:
>
> > >>> This is about
>
> >https://github.com/scala/scala/commit/6b3ef4f1676adcbe6dbdbf59a3bd359...
> > ,
> > >>> right? I also wondered why TypeTag wasn't made available as well,
> > >>> though I did see something in another commit about having a clear
> > >>> split between TypeTag and ClassTag. Still, I wonder...
>
> > >>> On Fri, Jun 22, 2012 at 8:15 PM, Daniel Sobral <dcsob...@gmail.com>
> > wrote:
> > >>> > object Main doesn't have a "main" method, nor does it extend App.
>
> > >>> > On Fri, Jun 22, 2012 at 5:10 PM, Dave <dave.mahabiers...@hotmail.com

Daniel Sobral

unread,
Jun 23, 2012, 8:05:53 PM6/23/12
to Dave, scala-user

Yes. Int, like all primitives, erases to Object.

Daniel Sobral

unread,
Jun 24, 2012, 12:24:56 AM6/24/12
to Dave, scala-user
I've been corrected off-thread -- Int does not erase to Object. So, to
avoid introduce further confusion, please see expected behavior around
this whole issue at:

https://github.com/scala/scala/commit/6b3ef4f1676adcbe6dbdbf59a3bd359f339b0626

I was thinking about a different situation, which can be seen in the
links below:

https://issues.scala-lang.org/browse/SI-4214
https://github.com/scala/scala/commit/e42733e9fe1f3af591976fbb48b66035253d85b9

Dave

unread,
Jun 24, 2012, 5:07:05 AM6/24/12
to scala-user
Indeed,
Int and Double are erased to the unboxed java types int and double
@specialized(Int,Double) doesn't seem to work


package reifiedpattern
import reflect._
class Test[A:ClassTag]{

def m(x: Any) = x match { case (y:A) => println("Found match " + y)
case _@(y:Any) => println("No match: " +
y.getClass.getName + ", " + classTag[A])
}

}
object Main extends App {
val test = new Test[Int]
test.m(1)
test.m(2.0)
test.m("test")

val test2 = new Test[Double]
test2.m(1)
test2.m(2.0)
test2.m("test")

val test3 = new Test[String]
test3.m(1)
test3.m(2.0)
test3.m("test")
}

C:\scala-2.10.0-M4\myexamples2>scala reifiedpattern.Main
No match: java.lang.Integer, ClassTag[int]
No match: java.lang.Double, ClassTag[int]
No match: java.lang.String, ClassTag[int]
No match: java.lang.Integer, ClassTag[double]
No match: java.lang.Double, ClassTag[double]
No match: java.lang.String, ClassTag[double]
No match: java.lang.Integer, ClassTag[class java.lang.String]
No match: java.lang.Double, ClassTag[class java.lang.String]
Found match test

Eugene Burmako

unread,
Jun 24, 2012, 6:28:57 AM6/24/12
to Daniel Sobral, Dave, scala-user, adriaa...@epfl.ch
In the example above, TypeTag, as a regular context bound, will be stored in a synthetic implicit evidence. The value that will be passed into that implicit parameter will be a factory that is capable of creating the exact type that was there during compilation.

We do need scala-reflect.jar to convert types to erasures, but the `x: A` pattern match can be translated into something that doesn't use erasures, for example, to `implicitly[TypeTag[<typeof x>]].tpe <:< typeOf[A]`. Oh... The subtyping test also needs scala-reflect.jar!

Adriaan, could you, please, comment on how you planned to utilize type tags for pattern matching?

Dave

unread,
Jun 24, 2012, 7:11:12 AM6/24/12
to scala-user


On 24 jun, 12:28, Eugene Burmako <eugene.burm...@epfl.ch> wrote:
> In the example above, TypeTag, as a regular context bound, will be stored
> in a synthetic implicit evidence. The value that will be passed into that
> implicit parameter will be a factory that is capable of creating the exact
> type that was there during compilation.
>

Yes indeed, and ErasureTag and ConcreteTypeTag give at least a compile
time error about it, but TypeTag not.
Probably the implicit evidences are created for TypeTag(Int, Double,
String) and for ClassTag(Int, Double) but they aren't working
and for ClassTag the @specialized is not working to work around box/
unboxed jvm types.

package reifiedpattern
import reflect._
class TestClassTag[@specialized(Int,Double) A:ClassTag]{

def m(x: Any) = x match { case _:A => println("Found match " + x +
"(" + x.getClass.getName + ", " + classTag[A] + ")")
case _ => println("No match: " +
x.getClass.getName + ", " + classTag[A])
}

}
class TestErasureTag[@specialized(Int,Double) A:ErasureTag]{

def m(x: Any) = x match { case _:A => println("Found match " + x +
"(" + x.getClass.getName)
case _ => println("No match: " +
x.getClass.getName)
}

}

class TestTypeTag[@specialized(Int,Double) A:TypeTag]{

def m(x: Any) = x match { case _:A => println("Found match " + x +
"(" + x.getClass.getName)
case _ => println("No match: " +
x.getClass.getName)
}

}

class TestConcreteTypeTag[@specialized(Int,Double) A:ConcreteTypeTag]{

def m(x: Any) = x match { case _:A => println("Found match " + x +
"(" + x.getClass.getName)
case _ => println("No match: " +
x.getClass.getName)
}

}
object Main extends App {
println("Testing ClassTag")

val testClassTag = new TestClassTag[Int]
testClassTag.m(1)
testClassTag.m(2.0)
testClassTag.m("test")

val testClassTag2 = new TestClassTag[Double]
testClassTag2.m(1)
testClassTag2.m(2.0)
testClassTag2.m("test")

val testClassTag3 = new TestClassTag[String]
testClassTag3.m(1)
testClassTag3.m(2.0)
testClassTag3.m("test")

println("Testing ErasureTag")

val testErasureTag = new TestErasureTag[Int]
testErasureTag.m(1)
testErasureTag.m(2.0)
testErasureTag.m("test")

val testErasureTag2 = new TestErasureTag[Double]
testErasureTag2.m(1)
testErasureTag2.m(2.0)
testErasureTag2.m("test")

val testErasureTag3 = new TestErasureTag[String]
testErasureTag3.m(1)
testErasureTag3.m(2.0)
testErasureTag3.m("test")

println("Testing TypeTag")

val testTypeTag = new TestTypeTag[Int]
testTypeTag.m(1)
testTypeTag.m(2.0)
testTypeTag.m("test")

val testTypeTag2 = new TestTypeTag[Double]
testTypeTag2.m(1)
testTypeTag2.m(2.0)
testTypeTag2.m("test")

val testTypeTag3 = new TestTypeTag[String]
testTypeTag3.m(1)
testTypeTag3.m(2.0)
testTypeTag3.m("test")


println("Testing ConcreteTypeTag")

val testConcreteTypeTag = new TestConcreteTypeTag[Int]
testConcreteTypeTag.m(1)
testConcreteTypeTag.m(2.0)
testConcreteTypeTag.m("test")

val testConcreteTypeTag2 = new TestConcreteTypeTag[Double]
testConcreteTypeTag2.m(1)
testConcreteTypeTag2.m(2.0)
testConcreteTypeTag2.m("test")

val testConcreteTypeTag3 = new TestConcreteTypeTag[String]
testConcreteTypeTag3.m(1)
testConcreteTypeTag3.m(2.0)
testConcreteTypeTag3.m("test")
}
/*
C:\scala-2.10.0-M4\myexamples2>scalac -unchecked reifiedpattern.scala
reifiedpattern.scala:12: warning: abstract type A in type pattern A is
unchecked
since it is eliminated by erasure
def m(x: Any) = x match { case _:A => println("Found match " + x +
"(" + x.get
Class.getName)
^
reifiedpattern.scala:20: warning: abstract type A in type pattern A is
unchecked
since it is eliminated by erasure
def m(x: Any) = x match { case _:A => println("Found match " + x +
"(" + x.get
Class.getName)
^
reifiedpattern.scala:28: warning: abstract type A in type pattern A is
unchecked
since it is eliminated by erasure
def m(x: Any) = x match { case _:A => println("Found match " + x +
"(" + x.get
Class.getName)
^
reifiedpattern.scala:53: error: could not find implicit value for
evidence param
eter of type scala.reflect.ErasureTag[Int]
val testErasureTag = new TestErasureTag[Int]
^
reifiedpattern.scala:58: error: could not find implicit value for
evidence param
eter of type scala.reflect.ErasureTag[Double]
val testErasureTag2 = new TestErasureTag[Double]
^
reifiedpattern.scala:63: error: could not find implicit value for
evidence param
eter of type scala.reflect.ErasureTag[String]
val testErasureTag3 = new TestErasureTag[String]
^
reifiedpattern.scala:88: error: could not find implicit value for
evidence param
eter of type scala.reflect.ConcreteTypeTag[Int]
val testConcreteTypeTag = new TestConcreteTypeTag[Int]
^
reifiedpattern.scala:93: error: could not find implicit value for
evidence param
eter of type scala.reflect.ConcreteTypeTag[Double]
val testConcreteTypeTag2 = new TestConcreteTypeTag[Double]
^
reifiedpattern.scala:98: error: could not find implicit value for
evidence param
eter of type scala.reflect.ConcreteTypeTag[String]
val testConcreteTypeTag3 = new TestConcreteTypeTag[String]
^
three warnings found
6 errors found
*/
Reply all
Reply to author
Forward
0 new messages