Showing posts with label manifest. Show all posts
Showing posts with label manifest. Show all posts
Friday, January 29, 2010
Overcoming Type Erasure in Matching 2 (Variance)
A commenter (Brian Howard) on the post Overcoming Type Erasure in Matching 1 made a very good point:
There is a way to do this, however the information is not captured in the Manifest. A manifest is not designed to do full reflection it is by design very light and has little impact on performance. So to provide the functionality requested by Brian one needs to add that information to the Extractor Definition. Have have a possible solution below.
Is there a way to deal with some type arguments being contravariant? Try the following:
class A
class B extends A
val AAFunction = new Def[Function1[A,A]]
((a:A) => a) match {case AAFunction(f) => Some(f(new A)); case _ => None} // this is OK
((a:A) => new B) match {case AAFunction(f) => Some(f(new A)); case _ => None} // this is OK
((b:B) => b) match {case AAFunction(f) => Some(f(new A)); case _ => None} // gives a ClassCastException, since new A is not a B
There is a way to do this, however the information is not captured in the Manifest. A manifest is not designed to do full reflection it is by design very light and has little impact on performance. So to provide the functionality requested by Brian one needs to add that information to the Extractor Definition. Have have a possible solution below.
- scala> class A
- defined class A
- scala> class B extends A
- defined class B
- scala> object Variance extends Enumeration {
- | val Co, Contra, No = Value
- | }
- defined module Variance
- scala> import Variance._
- import Variance._
- scala> class Def[C](variance:Variance.Value*)(implicit desired : Manifest[C]) {
- | def unapply[X](c : X)(implicit m : Manifest[X]) : Option[C] = {
- | val typeArgsTriplet = desired.typeArguments.zip(m.typeArguments).
- | zipWithIndex
- | def sameArgs = typeArgsTriplet forall {
- | case ((desired,actual),index) if(getVariance(index) == Contra) =>
- | desired <:< actual
- | case ((desired,actual),index) if(getVariance(index) == No) =>
- | desired == actual
- | case ((desired,actual),index) =>
- | desired >:> actual
- | }
- |
- | val isAssignable = desired.erasure.isAssignableFrom(m.erasure) || (desired >:> m)
- | if (isAssignable && sameArgs) Some(c.asInstanceOf[C])
- | else None
- | }
- | def getVariance(i:Int) = if(variance.length > i) variance(i) else No
- | }
- defined class Def
- scala> val AAFunction = new Def[Function1[A,A]]
- AAFunction: Def[(A) => A] = Def@64a65760
- scala> ((a:A) => a) match {case AAFunction(f) => Some(f(new A)); case _ => None}
- res0: Option[A] = Some(A@1bd4f279)
- scala> ((a:A) => new B) match {case AAFunction(f) => Some(f(new A)); case _ => None}
- res1: Option[A] = None
- scala> ((b:B) => b) match {case AAFunction(f) => Some(f(new A)); case _ => None}
- res2: Option[A] = None
- scala> val BAFunction = new Def[Function1[B,A]](Contra,Co)
- BAFunction: Def[(B) => A] = Def@26114629
- scala> ((b:A) => b) match {case BAFunction(f) => Some(f(new B)); case _ => None}
- res3: Option[A] = Some(B@15dcc3ca)
Labels:
Advanced,
contravariance,
covariance,
Enumeration,
manifest,
match,
matching,
Scala,
variance
Thursday, January 28, 2010
Overcoming Type Erasure in matching 1
Since Scala runs on the JVM (it also runs on .NET but we this tip is aimed at Scala on the JVM) much of the type information that is available at compile-time is lost during runtime. This means certain types of matching are not possible. For example it would be nice to be able to do the following:
If you run this code the list matches the list of ints which is incorrect (I have ran the following with -unchecked so it will print the warning about erasure):
Another example is trying to match on structural types. Wouldn't it be nice to be able to do the following (We will solve this in a future post):
So lets see what we can do about this. My proposed solution is to create an class that can be used as an extractor when instantiated to do the check.
This is a fairly advanced tip so make sure you have read up on Matchers and Manifests:
The key parts of the next examples are the Def class and the function 'func'. 'func' is defined in the comments in the code block.
The Def class is the definition of what we want to match. Once we have the definition we can use it as an Extractor to match List[Int] instances.
The Def solution is quite generic so it will satisfy may cases where you might want to do matching but erasure is getting in the way.
The secret is to use manifests:
It is critical to notice the use of the typeArguments of the manifest. This returns a list of the manifests of each typeArgument. You cannot simply compare desired == m because manifest comparisons are not deep. There is a weakness in this code in that it only handles generics that are 1 level deep. For example:
List[Int] can be matched with the following code but List[List[Int]] will match anything that is List[List[_]]. Making the method more generic is an exercise left to the reader.
- scala> val x : List[Any] = List(1.0,2.0,3.0)
- x: List[Any] = List(1, 2, 3)
- scala> x match { case l : List[Boolean] => l(0) }
If you run this code the list matches the list of ints which is incorrect (I have ran the following with -unchecked so it will print the warning about erasure):
- scala> val x : List[Any] = List(1.0,2.0,3.0)
- x: List[Any] = List(1, 2, 3)
- scala> x match { case l : List[Boolean] => l(0) }
- < console>:6: warning: non variable type-argument Boolean in type pattern List[Boolean] is unchecked since it is eliminated by erasure
- x match { case l : List[Boolean] => l(0) }
- ^
- java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.Boolean
- at scala.runtime.BoxesRunTime.unboxToBoolean(Unknown Source)
- at .< init>(< console>:6)
Another example is trying to match on structural types. Wouldn't it be nice to be able to do the following (We will solve this in a future post):
- scala> "a string" match {
- | case l : {def length : Int} => l.length
- | }
- < console>:6: warning: refinement AnyRef{def length: Int} in type pattern AnyRef{def length: Int} is unchecked since it is eliminated by erasure
- case l : {def length : Int} => l.length
- ^
- res5: Int = 8
So lets see what we can do about this. My proposed solution is to create an class that can be used as an extractor when instantiated to do the check.
This is a fairly advanced tip so make sure you have read up on Matchers and Manifests:
The key parts of the next examples are the Def class and the function 'func'. 'func' is defined in the comments in the code block.
The Def class is the definition of what we want to match. Once we have the definition we can use it as an Extractor to match List[Int] instances.
The Def solution is quite generic so it will satisfy may cases where you might want to do matching but erasure is getting in the way.
The secret is to use manifests:
- When the class is created the manifest for the class we want is captured.
- Each time a match is attempted the manifest of the class being matched is captured
- In the unapply method, the two manifests are compared and when they match we can return the matched element but cast to the desired type for the compiler
It is critical to notice the use of the typeArguments of the manifest. This returns a list of the manifests of each typeArgument. You cannot simply compare desired == m because manifest comparisons are not deep. There is a weakness in this code in that it only handles generics that are 1 level deep. For example:
List[Int] can be matched with the following code but List[List[Int]] will match anything that is List[List[_]]. Making the method more generic is an exercise left to the reader.
- scala> import reflect._
- import reflect._
- /*
- This is the key class
- */
- scala> class Def[C](implicit desired : Manifest[C]) {
- | def unapply[X](c : X)(implicit m : Manifest[X]) : Option[C] = {
- | def sameArgs = desired.typeArguments.zip(m.typeArguments).forall {case (desired,actual) => desired >:> actual}
- | if (desired >:> m && sameArgs) Some(c.asInstanceOf[C])
- | else None
- | }
- | }
- defined class Def
- // first define what we want to match
- scala> val IntList = new Def[List[Int]]
- IntList: Def[List[Int]] = Def@6997f7f4
- /*
- Now use the object as an extractor. the variable l will be a typesafe List[Int]
- */
- scala> List(1,2,3) match { case IntList(l) => l(1) : Int ; case _ => -1 }
- res36: Int = 2
- scala> List(1.0,2,3) match { case IntList(l) => l(1) : Int ; case _ => -1 }
- res37: Int = -1
- // 32 is not a list so it will fail to match
- scala> 32 match { case IntList(l) => l(1) : Int ; case _ => -1 }
- res2: Int = -1
- scala> Map(1 -> 3) match { case IntList(l) => l(1) : Int ; case _ => -1 }
- res3: Int = -1
- /*
- The Def class can be used with any Generic type it is not restricted to collections
- */
- scala> val IntIntFunction = new Def[Function1[Int,Int]]
- IntIntFunction: Def[(Int) => Int] = Def@5675e2b4
- // this will match because it is a function
- scala> ((i:Int) => 10) match { case IntIntFunction(f) => f(3); case _ => -1}
- res38: Int = 10
- // no match because 32 is not a function
- scala> 32 match { case IntIntFunction(f) => f(3); case _ => -1}
- res39: Int = -1
- // Cool Map is a function so it will match
- scala> Map(3 -> 100) match { case IntIntFunction(f) => f(3); case _ => -1}
- res6: Int = 100
- /*
- Now we see both the power and the limitations of this solution.
- One might expect that:
- def func(a:Any) = {...}
- would work.
- However, if the function is defined with 'Any' the compiler does not pass in the required information so the manifest that will be created will be a Any manifest object. Because of this the more convoluted method declaration must be used so the type information is passed in.
- I will discuss implicit parameters in some other post
- */
- scala> def func[A](a:A)(implicit m:Manifest[A]) = {
- | a match {
- | case IntList(l) => l.head
- | case IntIntFunction(f) => f(32)
- | case i:Int => i
- | case _ => -1
- | }
- | }
- func: [A](a: A)(implicit m: Manifest[A])Int
- scala> func(List(1,2,3))
- res16: Int = 1
- scala> func('i')
- res17: Int = -1
- scala> func(4)
- res18: Int = 4
- scala> func((i:Int) => i+2)
- res19: Int = 34
- scala> func(Map(32 -> 2))
- res20: Int = 2
Wednesday, January 13, 2010
Comparing Manifests
Manifests are Scala's solution to Java's type erasure. It is not a complete solution as of yet but it does have several useful applications. Manifests were originally added to Scala 2.7 in an extremely limited form but have been improved a great deal for Scala 2.8. Now more powerful comparisons of manifests are possible. For another introduction to Manifests (2.7 manifests) see Manifests.
This post looks at a few ways to create manifests as well as how to compare manifests. The goal is to create a method that can be used in testing to require that a certain exception is thrown during a code block:
The code snippet above is a failure because println does not throw an exception. In addition to requiring manifests this code snippet also is a custom control structure, for more information on those see Control Structure Tag
But before writing the intercepts methods a short inspection of the new manifest comparison operators:
Now the implementation of intercepts:
This post looks at a few ways to create manifests as well as how to compare manifests. The goal is to create a method that can be used in testing to require that a certain exception is thrown during a code block:
- scala> intercepts[Exception] {println("hi :)")}
- hi :)
- Expected java.lang.Exception but instead no exception was raised
The code snippet above is a failure because println does not throw an exception. In addition to requiring manifests this code snippet also is a custom control structure, for more information on those see Control Structure Tag
But before writing the intercepts methods a short inspection of the new manifest comparison operators:
- scala> import scala.reflect.{
- | Manifest, ClassManifest
- | }
- import scala.reflect.{Manifest, ClassManifest}
- // from class creates a manifest object given a class object
- scala> import ClassManifest.fromClass
- import ClassManifest.fromClass
- // several comparisons using <:<
- scala> fromClass(classOf[Exception]) <:< fromClass(classOf[Exception])
- res4: Boolean = true
- scala> fromClass(classOf[Exception]) <:< fromClass(classOf[RuntimeException])
- res5: Boolean = false
- scala> fromClass(classOf[Exception]) <:< fromClass(classOf[AssertionError])
- res6: Boolean = false
- // now the opposite operator >:>
- scala> fromClass(classOf[Exception]) >:> fromClass(classOf[AssertionError])
- res7: Boolean = false
- scala> fromClass(classOf[Exception]) >:> fromClass(classOf[RuntimeException])
- res8: Boolean = true
- scala> fromClass(classOf[Exception]) >:> fromClass(classOf[Error])
- res9: Boolean = false
- scala> fromClass(classOf[Exception]) >:> fromClass(classOf[Throwable])
- res10: Boolean = false
- // the method singleType creates a manifest given an object
- scala> ClassManifest.singleType(new RuntimeException())
- res12: scala.reflect.Manifest[Nothing] = java.lang.RuntimeException.type
- scala> ClassManifest.singleType(new RuntimeException()) <:< fromClass(classOf[Throwable])
- res13: Boolean = true
- scala> fromClass(classOf[Exception]) <:< fromClass(classOf[Throwable])
- res14: Boolean = true
Now the implementation of intercepts:
- scala> import scala.reflect.{
- | Manifest, ClassManifest
- | }
- import scala.reflect.{Manifest, ClassManifest}
- scala>
- scala> import ClassManifest.singleType
- import ClassManifest.singleType
- scala> def intercepts[E <: Throwable](test : => Unit)(implicit m:Manifest[E]) : Unit = {
- | import Predef.{println => fail}
- | try {
- | test
- | // this is a failure because test is expected to throw an exception
- | fail("Expected "+m.toString+" but instead no exception was raised")
- | }catch{
- | // this checks that the expected type (m) is a superclass of the class of e
- | case e if (m >:> singleType(e)) => ()
- | // any other error is handled here
- | case e => fail("Expected "+m.toString+" but instead got "+e.getClass)
- | }
- | }
- intercepts: [E <: Throwable](test: => Unit)(implicit m: scala.reflect.Manifest[E])Unit
- scala> intercepts[Exception] {println("hi :)")}
- hi :)
- Expected java.lang.Exception but instead no exception was raised
- scala> intercepts[Exception] { throw new IllegalArgumentException("why not throw this :)")}
- scala> intercepts[Exception] { throw new AssertionError("The method should complain")}
- Expected java.lang.Exception but instead got class java.lang.AssertionError
Labels:
Advanced,
control structure,
manifest,
Scala
Wednesday, November 18, 2009
Manifests
Added in Scala 2.7 as an experimental feature is the manifest class it is a handy class allowing for very limited type reification. Take the following example:
If you want to know they type of T in Java you are out of luck because that type information is gone. However, Scala offers help through manifests.
This code snippet will print out the string representation of type T.
More Examples:
def inspect[T](l:List[T])
If you want to know they type of T in Java you are out of luck because that type information is gone. However, Scala offers help through manifests.
- def inspect[T](l:List[T])(implicit manifest : scala.reflect.Manifest[T]) = println(manifest.toString)
This code snippet will print out the string representation of type T.
More Examples:
- scala> def inspect[T](l:List[T])(implicit manifest : scala.reflect.Manifest[T]) = println(manifest.toString)
- inspect: [T](List[T])(implicit scala.reflect.Manifest[T])Unit
- scala> inspect(List(1,2,3,4))
- int
- scala> inspect(List(List(1,2),List(3,4)))
- scala.List[int]
- scala> inspect(List(List(List(1),List(2)),List(List(3),List(4))))
- scala.List[scala.List[int]]
- scala> val l:List[Iterable[Int]] = List(List(1,2))
- l: List[Iterable[Int]] = List(List(1, 2))
- scala> inspect(l)
- scala.collection.Iterable[Int]
- scala> class MV[T](val v:T)(implicit m:scala.reflect.Manifest[T]) { println(m.toString) }
- defined class MV
- scala> new MV(1)
- Int
- res1: MV[Int] = MV@180e6899
- scala> class MV2[T](val v:T)(implicit m:scala.reflect.Manifest[T]) {
- | def isA[A](implicit testManifest:scala.reflect.Manifest[A]) = m.toString == testManifest.toString
- | }
- defined class MV2
- scala> val x = new MV2(19)
- x: MV2[Int] = MV2@41ff8506
- scala> x.isA[String]
- res2: Boolean = false
- scala> x.isA[Int]
- res3: Boolean = true
- scala> def isA[A](o:Object)(implicit m:Manifest[A]) = {
- | val `class` = Class.forName(m.toString)
- | `class`.isAssignableFrom(o.getClass)
- | }
- isA: [A](o: java.lang.Object)(implicit m: scala.reflect.Manifest[A])Boolean
- scala> isA[java.lang.Integer](java.lang.Integer.valueOf(19))
- res6: Boolean = true
Labels:
implicit,
manifest,
reflection,
Scala
Subscribe to:
Comments (Atom)