Avoiding Nothing

Sometimes Scala's compiler will infer the type Nothing for a generic type parameter which may cause an error during runtime. I show here a simple way to avoid that.

 

The method follows the strategy in http://github.com/harrah/up/blob/master/Contains.scala (which presents a more generalized way of declaring type parameters which are required or excluded). The strategy is to cause an ambiguity for the compiler when Nothing is used which will cause it to error.

 

First, a use case: Say I want to make working with Maps that contain multiple types a little easier, for example, working with a Map[String, Any]. Insted of a user needing to call map.get(key).asInstanceOf[Option[MyType]] , I want a method like map.getAs[MyType](key). The code:

class RichMap[K,V](map: MapLike[K,V,_]) {
    def getAs[T <: V](key: K) = map.get(key).asInstanceOf[Option[T]]
    def as[T <: V](key: K) = map(key).asInstanceOf[T]
}
implicit def toRichMap[K,V](map: MapLike[K,V,_]) = new RichMap[K, V](map) 

The problem here is that now if the developer will forget the type parameter, and call map.getAs(key), the compiler will silently infer Nothing. Because of Scala's type inferencing, the return type may be used in such a way that the compiler will compile the whole expression, but at runtime will create a failure. For example: map.getAs(key) map {_.toString}. Here, in the call _.toString, the compile will add a cast of the value to scala.runtime.Nothing$ which will fail at runtime.

 

What I want is for the following to work:

class RichMap[K,V](map: MapLike[K,V,_]) {
    def getAs[T <: V : NotNothing](key: K) = map.get(key).asInstanceOf[Option[T]]
    def as[T <: V : NotNothing](key: K) = map(key).asInstanceOf[T]
}
implicit def toRichMap[K,V](map: MapLike[K,V,_]) = new RichMap[K, V](map) 

 

 

The ': NotNothing' will insure a compile time error. It adds an implicit variable to the method signature of type NotNothing[T] which serves as evidence that T is not Nothing.

 

Here's the code to do that:

sealed trait NotNothing[-T] // {override def toString = "Not Nothing"}

object NotNothing {
    implicit object notNothing extends NotNothing[Any]
    implicit object `\n The error is because the type parameter was resolved to Nothing` extends NotNothing[Nothing]
}

 

Before explaining the code, the odd thing is the long message between backticks in the second implicit object. This is actually a valid way in Scala to name things. Since the use of the object is through implicit parameters, this will be silently used by the compiler. But once a compilation error occurs (when Nothing is inferred), the compiler will print out the name which will serve to explain the origin of the error, that can otherwise be confusing.

 

Explanation of the code: What it does is create a NotNothing type class ([1]) and also two instances. One that makes any value conform to the type class and the second conforms to just Nothing. So for any type, the first object is used. But when Nothing is inferred, the compiler has a choice of two objects and will therefore issue an error.

 

Here is how it looks:

scala> val m = Map("hello" -> "world", "bye" -> 0)
m: scala.collection.immutable.Map[java.lang.String,Any] = Map((hello,world), (bye,0))

scala> m.getAs[Int]("bye")
res2: Option[Int] = Some(0)

scala> m.getAs("bye")
:20: error: ambiguous implicit values:  both object  The error is because the type parameter was resolved to Nothing in object NotNothing of type object Foo.NotNothing. The error is because the type parameter was resolved to Nothing  and object notNothing in object NotNothing of type object Foo.NotNothing.notNothing  match expected type Foo.NotNothing[Nothing]        m.getAs("bye")               ^ 

 

 

[1] A type class is a marker class that can show how one type, T conforms to a general class of types. See http://dcsobral.blogspot.com/2010/06/implicit-tricks-type-class-pattern.html, other articles are also available via google

Developer