tags:

views:

316

answers:

4

I often face the problem of wanting to add additional methods to classes I don't control. For instance, I might want to have a function prettyPrint that can operate on different object types that do not have a unified api (e.g. special __str__ methods).

The Nice language and R accomplishes this using multimethods, which avoid the Visitor Pattern nicely. For instance, R has the plot() function. Individual programmers can create new classes to define data types (e.g., network graphs or stock ticker data). A secondary user/programmer could then write a plot function to fill out the functionality, even though they don't have access to the graph or stock ticker code, or the code of other plot functions.

Given that I would like to add lots of functionality later, using class.method() seems infeasible. Lots of class_plot() functions for each type also seems like a bad idea. Defining one big plot() function that checks types isn't extensible.

What are alternatives to multimethods? In particular, I'm interested in designs that might work in Jython and Scala.

+1  A: 

Python and Jython allow mixin classes, so you can use those to hold your "extension" methods:

class Derived(Base, Mixin1, Mixin2, ...):
    ...

Clearly if you want a host of plot methods for different types then you need to have implementations somewhere - this could lead to multiple mixin classes. An alternative is to use a dictionary where the key is the type and the value is the function (or set of functions): see this answer.

I'm also assuming that you don't want to monkey-patch classes, which is also technically possible (though not advisable).

Vinay Sajip
+1  A: 

Scala has the concept of Implicits. Here's an analogy to C#'s extension methods.

Hank Gay
+5  A: 

Easiest way to add a method to a class in Scala:

implicit def defSum(l: List[Double]): Double = new AnyRef { def sum = l.foldLeft(0.0)(_ + _) }

Or, on Scala 2.8, with the particular case of a method handling numbers,

implicit def defSum[T](l: List[T])(implicit n: Numeric[T]) = new AnyRef {
  import n.mkNumericOps
  def sum = l.foldLeft(n.zero)(_ + _)
}

Only this isn't needed on Scala 2.8, because it defines sum already.

You can also add traits when instantiating:

// Immutable class
case class Point(x: Int, y: Int)

// Somewhere else in the code    
trait MyPlot {
  self: Point =>
  import self._

  def plot = println("At "+x+", "+y)
}

// Usage
val p = new Point(1,2) with MyPlot
p.plot

What you cannot do is add a method dynamically (ie, at run-time instead of compile-time) to an object. In the first case, you are saying "add this method to this class". On the second case you are saying "create this object with this trait". These are ok, but "add this method to this object" is not.

Daniel
+1  A: 

If you don't want to implement multimethods yourself, there is a packaged implementation of multimethods for Python. I expect it works on Jython as well.

Another option would be to check out Clojure, which has multimethods built in.

Matthias Benkard