Thursday, November 12, 2009

Another encoding of typeclasses in Scala

I've been thinking about different ways of doing typeclass-like things in Scala, using Scala's first-class modules (aka objects), and functions over these modules (aka classes). More generally, I'm interested in exploring whether there's anything as convenient as typeclasses that could be implemented in a dynamic language.

Haskell's typeclasses can be encoded pretty directly in Scala, using an implicit module derived from some polymorphic type parameter:

From what I understand, Haskell's implementation of typeclasses works similarly: bounded polymorphic functions receive one or more hidden dictionary (module) parameters. Except in Haskell modules are not first class objects that the programmer can access or manipulate. (Although I'm unsure how much of a restriction this is.)

Given that modules are first class in Scala, though, why don't we just pass the module in at some outer scope?

We can then put all the Applicative-derived functions nested within the body of this class. If we then give a name to our ApplicativeM[List] instance, like ListA, we can have syntax like ListA.sequence(blah), which is almost the best you can do. One thing I like about this is we avoid having to restate the bound for each and every Applicative-derived function - all functions simply reference M, where the bound on M is stated in the outer scope.

Incidentally, this sounds very similar to the encoding discussed here:

The translation from Haskell type classes to ML modules encodes type classes as signatures and instances of type classes as functors that yield structures of these signatures.

Sidebar question for ML people: In ML, can you have higher-kinded modules like Applicative, and can ML functors receive such modules as arguments? Or are you limited to accepting first-order modules like Ring?

Per-function implicit parameters is still more flexible, though, since it allows the functions derived from a typeclass to be spread out over multiple modules. The most logical place to put a function isn't necessarily in the module with the typeclass definition since the bound is often somewhat incidental. Per-function implicits scale better, too - you don't need to have a separate module for every combination of type bounds (although I doubt very many combinations occur in real programs).

I wonder if it would be possible to get around this first restriction somehow by adding a few operators to modules - perhaps union, subtraction, and renaming?

In regards to your ML question, although it didn't make it into the Definition of Standard ML, most variants of Standard ML support some form of higher-order functors as does OCaml. The semantics of this feature (primarily its interaction with generative types) varies considerably among the different implementations and formal developments.