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 ;)

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.

Montag, 31. Januar 2011

Scala: Spass mit Streams

Ich wurde ja neulich mal gefragt ob ich mal was über Streams in Scala bloggen könnte (auch wenn es hier eher um IO ging). Da mir da allerdings ein gutes Beispiel gefehlt hat, habe ich das erst einmal auf die lange Bank geschoben (irgendwie blogge ich derzeit glaub ich etwas zu wenig :/).

Aber ok, dank eines hervorragenden Posts von Mario Gleichmann bin ich dann doch noch auf eine ganz brauchbare Idee gekommen. (Schon allein Vollständigkeit halber empfehle ich den Post von Mario zu lesen!)

Im folgenden Code geht es einfach nur darum die nachfolgende Jahreszeit zu bestimmen.

sealed abstract class Season
case object Spring extends Season
case object Summer extends Season
case object Fall extends Season
case object Winter extends Season

object Main {
lazy val seasons: Stream[Season] =
Spring #:: Summer #:: Fall #:: Winter #:: seasons

def nextSeason(now: Season): Season = {
@scala.annotation.tailrec
def getNextFromStream(s: Stream[Season]): Season =
s match {
case a @ x #:: y #:: _ =>
if (now eq x)
y
else
getNextFromStream(a tail)
}
getNextFromStream(seasons)
}

def main(args: Array[String]): Unit = {
println(nextSeason(Spring))
println(nextSeason(Summer))
println(nextSeason(Fall))
println(nextSeason(Winter))
}
}

Um die Jahreszeiten abzubilden nutze ich hier einen Stream (also eine unendliche Liste) die ich rekursiv durchgehe bis ich die aktuelle Jahreszeit gefunden habe und einfach die Nächste ausgebe.

Dieses Beispiel wirkt zugegebenermaßen etwas wie mit Kanonen auf Spatzen zu schiessen, zeigt aber die Ausdrucksstärke von Streams und Lazy-Evaluation. Ein sehr realer Anwendungsfall ist hier z.B. ein Roundrobin-Verfahren. Anstelle also stumpf eine Liste durchzugehen und am Ende wieder an den Anfang springen in Zukunft einfach mal über einen Stream nachdenken ;)

Samstag, 25. Dezember 2010

Die Sache mit Apple

"Hallo, ich bin raichoo und ich habe ein MacBook". Das sind Sätze mit denen man in vielen Kreisen schnell Freunde finden kann (Vorsicht Ironie!). Aber mal ehrlich, in den letzten Tagen kann man sich besonders schöne Kommentare gefallen lassen. Mein Favorit ist hier "Apfelnazi" (Goodwin's Law lässt grüßen). Die Diskussionskultur lässt hier ziemlich zu wünschen übrig, wer Leute nach dem Betriebssystem beurteilt hat in meinen Augen doch eher einen massiven Schaden.

Aber was bewegt die Leute dazu so zu reagieren? Auf der einen Seite mag das ganze auf einem pseudoelitären Gefühl basieren. Apple ist Mainstream. Gegen Mainstream sein ist rebellisch, cool und was weiß ich noch alles. Ich bin ja selber keine Ausnahme, meine Lebensweise ist sicherlich nicht die eines Ottonormalverbrauchers. Aber diese extremen Anfeindungen gehen doch echt schon zu weit.
Doch zurück zu den Beweggründen. Wikileaks ist im Moment sicherlich ein sehr bewegendes Thema, und das Verhalten vieler Firmen wie Visa, Paypal, Mastercard und auch Apple ist nicht nur verwerflich, es ist in meinen Augen auch undemokratisch.
Was also machen? Boykottieren? Guter Plan, Boykott war immer schon die wirksamste Waffe des Verbrauchers, denkt man sich. Die Problematik die sich hieraus aber ergibt ist folgende: der überwiegende Großteil der Firmen wird so reagieren. Ein bisschen Druck vom Staat eventuell noch ein paar Zuwendungen unter der Hand und schon wird der Stecker gezogen. Unsere Freiheit wird also potentiell von jeder Firma beschnitten die nur den nötigen Einfluss hat, oben genannte Beispiele befinden sich nur gerade in dieser Situation besonders in so einer Position. Man kann sich also praktisch totboykottieren, der Hydra wachsen die Köpfe einfach nach.

Wie also reagieren? Alle Apple Produkte aus dem Fenster werfen in den nächsten Mediamarkt rennen, ein Plastikwaffeleisen kaufen das nach einem Jahr auseinanderfällt und Ubuntu installieren? Ich sag es ehrlich: Linux ist derzeit einfach technisch keine Alternative für mich, und FreeBSD's Treibersituation auf Notebooks ist auch nicht wirklich berauschend. Windows mag ich nicht, usw.. Erreicht wird damit doch praktisch eh nichts. In meinen Augen ist beim Thema Wikileaks das effektivste Mittel doch eh: so hartnäckig spenden wie es nur geht. Wege dafür gibt es genug. Unterstützung ist weitaus mächtiger als Boykott.

Wenn mir also jemand ein Notebook mit 13" Formfaktor, 6 Stunden+ Akkulaufzeit, Alugehäuse, großen Touchpad auf dem mindestens ein FreeBSD rennt (damit meine ich das ALLE Komponenten auch zuverlässige Treiber haben) und das dazu auch noch KEINERLEI Komponenten von irgendeiner Firma enthält die irgendwo mal Scheisse gebaut hat zeigen kann, bring it on. (Das ist ernst gemeint, würde mich interessieren ob es da draußen irgendwas gibt was dem auch nur ansatzweise entspricht)

Montag, 20. September 2010

Scala: Klassen erweitern mal anders

Datentypen erweitern ist mehr oder weniger etwas das man des öfteren machen muss, und das ist nicht umbedingt immer unproblematisch. Nehmen wir mal an wir hätten folgende Klasse:

class Test(val x: Int)

Ganz klar: hochkomplizierter Code. Test quillt an Features nur so über, aber uns fehlt gerade eine Methode damit
wir Objekte dieser Klasse in unserem Code nahtlos verwenden können. Diese Methode nennen wir bla. Das Problem ist allerdings: Wir haben den Code von Test nicht und können ihn deswegen nicht um bla erweitern.
Jetzt gibt es verschiedene Wege wie wir diese Methode in Test einbinden könnten. Der offensichtlichste ist sicherlich Vererbung. Blöd ist nur: Wir haben bla schon geschrieben und würden ihn ungern duplizieren, vor allem weil wir den Code bereits schön generalisiert haben.

Eine Lösung ist hier ziemlich elegant mit Traits, Selftyping und Structural Types zu machen.

Als erstes definieren wir einen Structural Type der eine genaue Schnittstelle definiert. Wir nennen ihn xAble und er macht nichts weiter als zu definieren das alle Klassen die eine Methode x anbieten vom Typ xAble sind.

type xAble = { def x: Int }

Was wir jetzt brauchen ist unsere generalisierte Implementation von bla. Hier verwenden wir, Selftyping. Das ist ein Feature das es uns erlaubt innerhalb eines Traits so zu tun als wäre es von einem bestimmten Typ ohne davon zu erben.

trait Gimme[A <: xAble] {
self: A =>
def bla(): Unit = println(self.x + 1)
}

Wie man sieht muss der muss der Typparameter A ein Subtyp von xAble sein, also die methode x liefern.

Alles was wir jetzt noch machen müssen ist das Trait Gimme in das Objekt des Typs Test zu mischen und wir haben das ganze um bla erweitert.

object Main {

class Test(val x: Int)

type xAble = { def x: Int }

trait Gimme[A <: xAble] {
self: A =>
def bla(): Unit = println(self.x + 1)
}

def main(args: Array[String]): Unit = {
val t = new Test(666) with Gimme[Test]
t.bla()
}
}

Das ganze Zeit mal wieder wie extrem mächtig das Scala Typsystem ist und was für elegante Features die Sprache zum lösen immer wiederkehrender Probleme bietet. Ich hoffe dieses Beispiel gibt euch eine kleine Idee was man damit machen kann. ;)

Donnerstag, 24. Juni 2010

FreeBSD 8.1 und die JVM

Alios hat gerade seinen Server auf den neusten Stand gebracht und prompt funktionierte Sakura natürlich wieder nicht. Das Problem war das die JVM wohl anscheinen den IPv6 Stack genommen hat und sich damit nicht verträgt, ein Workaround ist die JVM einfach dazu zu zwingen den IPv4 Stack zu nehmen. Das geht mit der -Djava.net.preferIPv4Stack=true Option.

Wenn euch also das nächste mal ein Socket beim Connect aus unerfindlichen Gründen abraucht, versucht es mal damit.

100 Posts!