Mittwoch, 23. Februar 2011

Dynamic-Dispatch in Scala

Ich bin ja hin und wieder schon mal richtig neidisch auf Clojures Multimethods, möchte aber nicht wirklich meine statisch typisierte Welt verlassen (auch wenn Clojure vermutlich die einzige Sprache wäre die ich von allen dynamischen Sprachen wählen würde).

Lange Rede, kurzer Unsinn: Ich will Multimethods in Scala! Auf den ersten Blick klingt das als würde man sich eine Sprache zu etwas hinbiegen wollen was sie gar nicht ist, aber in Scala geht das ganze erschreckend schmerzlos.

Zum dispatchen verwende ich einfach ein paar partielle Funktionen die man mit orElse beliebig aneinanderketten kann. Der Vorteil davon ist das ich keinen Code aufmachen muss wenn ich einen weiteren Fall hinzufügen will sondern durch Komposition zum Ziel komme.

trait Dynamic

class A extends Dynamic
class B extends Dynamic

object Dynamic {
// 2 dynamische argumente
type Dyn2 = (Dynamic, Dynamic)

// infix notation fuer PartialFunction
type ~>[A, B] = PartialFunction[A, B]

val f: Dyn2 ~> String = {
case (a: A, b: A) => "A A"
}

val g: Dyn2 ~> String = {
case (a: A, b: B) => "A B"
}

// d ist die dispatchfunktion
def dispatch[A, B](d: A ~> B, a: A): Option[B] =
if (d.isDefinedAt(a)) Some(d(a))
else None
}

In Aktion sieht das Ganze so aus:


scala> :l ./dynamic.scala
Loading ./dynamic.scala...
defined trait Dynamic
defined class A
defined class B
defined module Dynamic

scala> import Dynamic._
import Dynamic._

scala> dispatch(f, (new A, new A))
res0: Option[String] = Some(A A)

scala> dispatch(f, (new A, new B))
res1: Option[String] = None

scala> dispatch(f orElse g, (new A, new B))
res2: Option[String] = Some(A B)

Wie man schön sehen kann werden auch die nicht abgedeckten Fälle behandelt indem wir einfach eine Option verwenden, dadurch könnten wir unseren Dispatch auch in einer for-Comprehension verwenden (Monaden für den Gewinn!)

Viel Spass beim ausprobieren ;)

Hier ein kleiner Nachtrag wozu Multimethods gut sind. Für alle die Das Visitorpattern schon immer gehasst haben ;)

Keine Kommentare: