Abstract Type Members - Augmenting Scala classes

March 11th, 2009 posted by codders

So, Scala’s cool.

About a month ago, a colleague was whining about having to write Java and it was hard not to sympathise. He’s a Python man, and Java’s Kingdom Of Nouns can seem a little clumsy. Sometimes, you just want to map{}, and trying to do that in Java can leave you feeling dirty.

There are proposals for first class functions and closures in Java - Neal Gafter is your man there. They look fun n’all, but they are apparently a little way off. What I needed in order to silence my colleague was less irritating Java in production straight away. In order to satisfy the needs of m’colleague, m’self and m’company I would need a couple of things.

Pour lui

  • Concise variable declarations - none of this
    Map<String,List<Integer>> map = new HashMap<String, List<Integer>>()

    nonsense

  • Functions as first class values
  • Excellent library support

Pour moi

  • Strong, static typing
  • Eclipse integration
  • Monads. Everybody loves monads

For practicality’s sake

  • Maven integration
  • Interoperation with the existing Java code base

I had a look on Wikipedia for a suitable JVM language since that would ensure the easy interoperation and give us a decent set of libraries. It turns out Scala has everything we need. It’s mixed paradigm functional / object-oriented, it’s got a Hindley-Milner style type inference system, and it’s got a really active community. The eclipse/maven integration isn’t quite there yet, but it gets much better as you get closer to the bleeding edge.

Some Code

There have been too many cool things to mention really, but here’s the first one that seemed apt for a blog post. Scala has some nice XML support, but lacks some of the functions you might like on an XML Node like, for example, ‘getAttributeValue(attributeName)’. In Haskell, you could define a new typeclass and create an instance for Node. In Java / C++ you could delegate (if you like pain). In Ruby, you could mixin. The answer in Scala appears to be… an Abstract Type Member.

Disclaimer:
I don’t know what the right answer actually is, but this works and seems kinda cool

import scala.xml.NodeSeq
import scala.xml.Node

object XmlTest
{
  implicit def nodeToNodePlus(node: Node):NodePlus =
  {
    return new NodePlus { type T = Node; val init = node }
  }

  abstract class NodePlus
  {
    type T <: scala.xml.Node
    val init: T
    private var value: T = init

    def getAttributeValue(name: String): String =
    {
      return value.attribute(name).get.first.text
    }
  }

  def main(args: Array[String])
  {
    println("Hello")
    val xmlDoc = <hi><hello id="world"/></hi>
    val hello = (xmlDoc\"hello").first
    println(hello.getAttributeValue("id"))
  }
}

The mechanics of what’s going on there are… err… pretty opaque to me. But to break that down a little:

  • An object is a singleton class, equivalent in most senses to a static class in Java.
  • An implicit is a function from A to B which the compiler will use whenever inference tells it that you need a B but have an A (yes, evil, but okay if used carefully).
  • type T is an Abstract Type Member. I think.
  • <: is a subclass restriction

… all of which allows me to introduce a new function to an existing class without access to that class’s constructor. Handy.

That’s probably garbage. No doubt I’ll find out that’s not the way to do it or that it won’t do what I expect, but it seems to work. The take-away is that Scala is an easy transition from Java, is much prettier and cooler, and that I can’t think of a good reason to write any more Java code.

… having slept on it…

Ah. You see. What I did there was… I got carried away. My NodePlus class is actually no better than a wrapper - I can’t actually do any Node operations on it. The implicit mechanism makes it look like I can, but I can’t. And the Abstract Type Member was a bit of a red herring. What the Abstract Type Member is doing is letting me parameterise my type which is, I guess, a kind of useful. Given that Scala types accept formal parameters, though, it’s not completely clear how much win is involved.

I’ll let you know if I find out how to do the thing I actually want to do.