Montag, 21. Februar 2011

Scala: Besseres Pattern-Matching mit Sealed Classes

Neulich bin ich auf eine recht interessante Mail in der jvm-languages Google Group gestossen. Ein Vergleich von Scala und OCaml! Wie spannend!
Besonders interessant fand ich folgendes Beispiel:

# let foo = function
| true, _ -> 0
| _, false -> 1;;
Warning P: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
(false, true)
val foo : bool * bool -> int =

OCaml liefert hier beim kompilieren einen Fehler: Das Patternmatching würde nicht alle Möglichkeiten erschöpfen. Zusätzlich bekommt man noch ein Beispiel wo der Match fehlschlägt. Tolle Sache! Das Scala Beispiel sieht hier doch eher ernüchternd aus.

scala> def foo(b: (Boolean, Boolean)): Int = b match {
| case (true, _) => 0
| case (_, false) => 0
| }
foo: ((Boolean, Boolean))Int

scala> foo(false, true)
scala.MatchError: (false,true)
at .foo(:4)
at .(:5)
at .()
at java.lang.Class.initializeClass(libgcj.so.81)
at RequestResult$.(:3)
at RequestResult$.()
at java.lang.Class.initializeClass(libgcj.so.81)
at RequestResult$result()
at java.lang.reflect.Method.invoke(libgcj.so.81)
...

Anscheinend ist Scala wohl nicht in der Lage herauszufinden welche Fälle möglich sind und kann so nicht testen ob das Matching auch wirklich alle Möglichkeiten ausschöpft. Aber genau dieser Effekt lässt sich mit Sealed Classes erreichen.

Als Beispiel definiere ich einen eigenen Booltypen mit einem Subtypen für jeweils true und false (ich nehme hier Objects da wir nicht mehr als eine Instanz von True bzw. False brauchen). Das gleiche Beispiel schlägt hier mit einer ähnlichen Fehlermeldung wie bei OCaml fehl:

abstract sealed class MyBool
case object True extends MyBool
case object False extends MyBool

object MyBool {
def test(a: (MyBool, MyBool)): Unit = a match {
case (True, _) => ()
case (_, False) => ()
}
}

MyBool.scala:6: warning: match is not exhaustive!
missing combination False True

def test(a: (MyBool, MyBool)): Unit = a match {
^
one warning found

Die Sealed Class hat zur Folge das wir keine weiteren Subtypen außerhalb unser Quelldatei anlegen können, der Compiler hat jetzt also alle Information die er braucht um den Match zu prüfen.

Mehr zum diesem Thema gibt es wieder mal bei Mario Gleichmann.

Keine Kommentare: