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:
  1. scala> intercepts[Exception] {println("hi :)")}                                            
  2. hi :)
  3. 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:
  1. scala> import scala.reflect.{
  2.      |   Manifest, ClassManifest
  3.      | }
  4. import scala.reflect.{Manifest, ClassManifest}
  5. // from class creates a manifest object given a class object
  6. scala> import ClassManifest.fromClass
  7. import ClassManifest.fromClass
  8. // several comparisons using <:<
  9. scala> fromClass(classOf[Exception]) <:< fromClass(classOf[Exception])
  10. res4: Boolean = true
  11. scala> fromClass(classOf[Exception]) <:< fromClass(classOf[RuntimeException])
  12. res5: Boolean = false
  13. scala> fromClass(classOf[Exception]) <:< fromClass(classOf[AssertionError])         
  14. res6: Boolean = false
  15. // now the opposite operator >:>
  16. scala> fromClass(classOf[Exception]) >:> fromClass(classOf[AssertionError])
  17. res7: Boolean = false
  18. scala> fromClass(classOf[Exception]) >:> fromClass(classOf[RuntimeException])
  19. res8: Boolean = true
  20. scala> fromClass(classOf[Exception]) >:> fromClass(classOf[Error])           
  21. res9: Boolean = false
  22. scala> fromClass(classOf[Exception]) >:> fromClass(classOf[Throwable])
  23. res10: Boolean = false
  24. // the method singleType creates a manifest given an object
  25. scala> ClassManifest.singleType(new RuntimeException())
  26. res12: scala.reflect.Manifest[Nothing] = java.lang.RuntimeException.type
  27. scala> ClassManifest.singleType(new RuntimeException()) <:< fromClass(classOf[Throwable])
  28. res13: Boolean = true
  29. scala> fromClass(classOf[Exception]) <:< fromClass(classOf[Throwable])                   
  30. res14: Boolean = true

Now the implementation of intercepts:
  1. scala> import scala.reflect.{
  2.      |   Manifest, ClassManifest
  3.      | }
  4. import scala.reflect.{Manifest, ClassManifest}
  5. scala> 
  6. scala> import ClassManifest.singleType
  7. import ClassManifest.singleType
  8. scala>  def intercepts[E <: Throwable](test : => Unit)(implicit m:Manifest[E]) : Unit = {
  9.      |     import Predef.{println => fail}
  10.      |     try {
  11.      |       test
  12.      |       // this is a failure because test is expected to throw an exception
  13.      |       fail("Expected "+m.toString+" but instead no exception was raised")
  14.      |     }catch{
  15.      |       // this checks that the expected type (m) is a superclass of the class of e
  16.      |       case e if (m >:> singleType(e)) => ()
  17.      |       // any other error is handled here
  18.      |       case e => fail("Expected "+m.toString+" but instead got "+e.getClass)
  19.      |     }
  20.      |   }
  21. intercepts: [E <: Throwable](test: => Unit)(implicit m: scala.reflect.Manifest[E])Unit
  22. scala> intercepts[Exception] {println("hi :)")}                                            
  23. hi :)
  24. Expected java.lang.Exception but instead no exception was raised
  25. scala> intercepts[Exception] { throw new IllegalArgumentException("why not throw this :)")}
  26. scala> intercepts[Exception] { throw new AssertionError("The method should complain")}     
  27. Expected java.lang.Exception but instead got class java.lang.AssertionError

5 comments:

  1. Hi Jesse,

    Thanks again for that post, I didn't have time to check out the comparison operators on Manifests.

    BTW there are 2 typos:

    "Manifests were originally added to Java 2.7"
    It's Scala 2.7.

    And

    "the method singeType creates" instead of
    "the method singleType creates"

    E.

    ReplyDelete
  2. Do you have a link to docs for the <:< and >:> operators? I've never seen them before and don't know where they are.

    ReplyDelete
  3. Thanks Eric. I have fixed those errors

    ReplyDelete
  4. Hi I don't know of Scala 2.8 docs that are online. These methods are only in Scala 2.8

    ReplyDelete
  5. Scaladoc of Scala 2.8 nightly builds:

    http://www.scala-lang.org/archives/downloads/distrib/files/nightly/docs/library/index.html

    ReplyDelete