I haven’t had much time to work on Klang since my vacation ended, but I’ve been thinking about how to solve overloading. *Note that this post contains pseudo-code that bears resemblance to, but is not actual Klang code — these are just thoughts so far.*

Given types `Double`

and `Vector2(x: Double, y: Double)`

, there are mathematical operations that take both types as operands in different combinations, but have the same name. For example, multiplication:

a: Double * b: Double = // intrinsic operation a * b a: Double * v: Vector2 = Vector2(a * v.x, a * v.y) v: Vector2 * a: Double = Vector2(a * v.x, a * v.y)

### Solution 1: Allow overloading of class methods

package klang class Double { def *(that: Double) = // intrinsic this * that } package vecmath extending Double { def *(v: Vector2) = Vector2(this * v.x, this * v.y) }

### Solution 1a: Allow overloading of package level functions

package klang def *(a: Double, b: Double) = // intrinsic a * b package klang.vecmath def *(a: Double, v: Vector2) = Vector2(a * v.x, a * v.y)

### Solution 2: Allow for Haskell-style type classes

package klang typeclass Multiplication[A, B, C] { def *(a: A, b: B): C } instance Multiplication[Double, Double, Double] { def *(a: Double, b: Double) = // intrinsic a * b } package klang.vecmath instance Multiplication[Double, Vector2, Vector2] { def *(a: Double, v: Vector2) = Vector2(a * v.x, a * v.y) }

### Pros and Cons

The first solution of allowing method/function overloading as in Java or Scala will certainly complicate things, especially when I introduce some form of inheritance (currently there is none). However, I’m not sure the second method, which is more like the Haskell way of handling overloading, is any better. Especially the third type class argument (return type) will be confusing: am I then allowed to also provide an

instance Multiplication[Double, Vector2, String]

which differs only in the last argument. Obviously it shouldn’t be so: if selecting a type class instance based on method return type was allowed, that wouldn’t work with Klang’s Scala style type inference. Maybe the type class could have an abstract type member instead:

typeclass Multiplication[A, B] { type Result def *(a: A, b: B): Result } instance Multiplication[Double, Vector2] { type Result = Vector2 def *(a: Double, v: Vector2) = Vector2(a * v.x, a * v.y) }

But I haven’t really decided whether to even have type classes or type members. There seems to be a lot more ceremony required with type classes compared to simply allowing overloading methods or module-level functions. And I’m not sure if that provides any real advantage…

I could say that defining a function `*(a: Double, b: Vector2)`

creates an implicit type class `*[A, B]`

and an implicit instance of it `*[Double, Vector2]`

. It would be equivalent to the manually defined type classes, wouldn’t it? Except in the manual case you could put division into the same type class with multiplication, but that would still require more ceremony than simple overloading.

And what of functions like `max(a: Double, b: Double)`

and `max(a: Collection[Double])`

? It would be nice to be able to do have the 2-argument version for performance reasons. Type classes wouldn’t necessarily solve that unless one could abstract over method argument arity.

I should probably read more literature to make better informed decisions about these things, just haven’t gotten around to those parts yet :)

What do you think? Do you have an opinion about which way to go or any pointers to reading material?

I like the type class approach to overloading. There’s a related idea that I’m not sure has been implemented – “Modular Type Classes”. Essentially it’s a way of simulating type classes using an SML-like module system (plus an extension to allow “importing” of canonical structures/functors into scope). This is very much like Scala “poor man’s” type classes via implicits. The paper is beyond me at the moment but the talk slides make a lot of sense.

http://www.cse.unsw.edu.au/~chak/papers/DHC07.html

http://www.mpi-sws.org/~dreyer/talks/popl07.ppt

Thanks, that looks like an interesting approach. I need to think about this some more.