Getting around static typing in Scala

I really like static typing, but sometimes it can get in your way. For instance if you have a collection of objects and you want to perform an operation on the objects of a certain subclass you can run into problems. I’ve run the following code in Scala 2.8

// this is easy since everything are Strings
scala> List("one", "two", "three")
res0: List[java.lang.String] = List(one, two, three)

scala> res0.foreach(x => println(x.length()))

// with a mixed collection it gets harder
scala> List("one", 2, "three", 4.0)
res2: List[Any] = List(one, 2, three, 4.0)

scala> res2.foreach(x => println(x.length()))
<console>:6: error: value length is not a member of Any

// it is not enough to just filter out the objects that are Strings
scala> res2.filter(_.isInstanceOf[String])
res4: List[Any] = List(one, three)

// as we still have a List of Any
scala> res4.foreach(x => println(x.length()))
<console>:7: error: value length is not a member of Any

// both filtering and mapping is needed
scala> res2.filter(_.isInstanceOf[String]).map(_.asInstanceOf[String])
res6: List[String] = List(one, three)

// finally it works
scala> res6.foreach(x => println(x.length()))

// another option is to use pattern matching
scala> res2.foreach(_ match { case x:String => println(x.length); case _ => })

I wanted a more elegant solution with less boiler plate so after playing around with implicit conversions and reified types I managed to come up with rather nice solution:

// define a class that implements a restrictTo method
scala> class IterableWithRestrictTo[A](from:Iterable[A]) {
     | def restrictTo[B](implicit m: scala.reflect.Manifest[B]) = {
     | from.filter(m.erasure.isInstance(_)).map(_.asInstanceOf[B])
     | }
     | }
defined class IterableWithRestrictTo

// define implicit conversion
scala> implicit def makeIterableWithRestrictTo[A](from : Iterable[A]) = {
     | new IterableWithRestrictTo(from)
     | }
makeIterableWithRestrictTo: [A](from: Iterable[A])IterableWithRestrictTo[A]

scala> List("one", 2, "three", 4.0)
res0: List[Any] = List(one, 2, three, 4.0)

// restrict to objects of type String
scala> res0.restrictTo[String]
res1: Iterable[String] = List(one, three)

// works since the collection have correct type
scala> res1.foreach(x => println(x.length()))

Using the name restrictTo clearly shows that not only is there a type conversion, but also a restriction to a subset of the elements. Notice that the manifest is needed to get the reified type, since the type B is actually erased at compile time. Read more about reified types in Scala.

Let me know you have another solution!

This Post Has 6 Comments

  1. Paolo Losi

    in scala 2.8 you can easily “refine” the type of your list.

    scala> List(1,2,”paolo”,”losi”).partialMap({case s:String => s})
    res2: List[String] = List(paolo, losi)

    Note that the result is of type List[String] and not List[Any].
    this solve the state problem, right?

    1. Jan Kronquist

      #Paolo Losi: Thanks! This was exactly what I was looking for!

  2. Anders Janmyr

    Yet, another reason to go dynamic.

    Apart from the unbelievably boring chore of waiting for the compiler it saves us from fighting with the type checker!

    # Ruby code
    >> res = %w(anders, peter kalle)
    => [“anders,”, “peter”, “kalle”]
    >> res.each { |x |puts x.length }
    => [“anders,”, “peter”, “kalle”]

    Pure Bliss!

    1. Jan Kronquist

      #Anders: Not really, as it doesn’t work if the list contains mixed types. In my example I had both Int, Float and String. You still have to do the filtering, otherwise you might get surprises in runtime. But of course, that is what dynamic programming is all about ;-)

  3. Johan Nyström

    Thanks for posting this. FYI, List.partialMap has now been renamed to List.collect.

Leave a Reply