Archiv für Juli 2011

25
Jul
11

Resteverwertung

Als Ergänzung zu meinem Pattern-Matching-Artikel heute ein kleiner Trick, der mir eher zufällig über den Weg gelaufen ist.

Ich hatte dazumal folgendes Codebeispiel gebracht, um zu zeigen, wie man die restlichen Elemente einer Collection beim Pattern Matching erfolgreich ignorieren kann:

def test(list: List[String]) = list match {
  case List("1","2", _*) => "one, two ..."
  case _ => "dunno"
}

Jetzt fragt man sich natürlich, wie man vorgehen muss, wenn man den Rest eben nicht ignorieren will, sondern auch diesen an eine Variable gebunden haben möchte. Die “naive” Lösung mit etwas wie rest* funktioniert jedenfalls schon einmal nicht. Aber es geht trotzdem, und zwar dank Klammeraffen:

def test(list: List[String]) = list match {
  case List("1","2", rest @ _*) => "one, two with rest " + rest
  case _ => "dunno"
}

test(List("1","2","3","4"))
//--> java.lang.String = one, two with rest List(3, 4)

Achtung, der Rest kann natürlich auch eine leere Liste sein. Für Listen ist dieser Trick eigentlich unnötig, denn hier hat man ja schon den Extraktor ::, aber das Ganze funktioniert natürlich auch für andere Extraktoren mit variabler Argumentanzahl, wie z.B. Seq.

So, dann wünsche ich fröhliches Extrahieren, vor allem meinen Erstlesern bei codekicker.de.

20
Jul
11

Wasch mich, aber mach mich nicht nass!

Es ist kein Geheimnis: Java macht keinen richtigen Spaß mehr, wenn man es mit anderen modernen Sprachen vergleicht. Was macht man, wenn man weiter die JVM und die unzähligen Java-Bibliotheken nutzen will? Richtig, man schaut kräftig beim Marktführer Scala ab, und schreibt eine eigene Sprache. Die natürlich besser ist. So geschehen bei Gosu, RedHat’s Ceylon und nun JetBrain’s Kotlin.

Ich finde das traurig. Richtig, es heißt “Konkurrenz belebt das Geschäft”, aber diese drei Sprachen sind unnötig wie ein Kropf. Es stimmt, Scala kann kompliziert sein und hat auch einige weniger schöne Stellen. Auf der anderen Seite ist es einsteigerfreundlich, innovativ und mächtig, aber vor allem ist es da. Es hat lange gebraucht, um wirklich Bewegung in die Sache zu bringen, eine Community aufzubauen, Tool-Support zu organisieren, ja überhaupt wahrgenommen zu werden. Was inzwischen fast wie ein Selbstläufer aussieht, ist das Ergebnis langer, harter Arbeit. Ich denke, die neuen Sprachschöpfer unterschätzen diesen Aspekt einer Sprache ganz gewaltig.

Viel eigene Innovation ist bei keinem der Kandidaten zu sehen. Aber was mich wirklich stört, ist der Versuch, sich nur die Rosinen aus dem Kuchen zu picken. Das ist nämlich die Art von Geisteshaltung, die Java’s Stillstand erst verursacht hat. Eine gute Sprache ist offen für neue Entwicklungen, und lenkt diese in geordnete Bahnen. Gerade diese Offenheit macht Scala so attraktiv. Doch die Strategie der neuen Sprachen ist die gleiche wie Java: Wasch mich, aber mach mich nicht nass! Features, die “zu kompliziert” sind oder eventuell missbraucht werden können, werden abgelehnt. In Java waren das etwa Operator-Überladungen, Closures oder Konzepte zur Erweiterung von Interfaces (wie Extensionsmethoden oder Mixins). Jetzt werden aus den gleichen fadenscheinigen Gründen Dinge wie implizite Umwandlungen, abstrakte Typ-Member oder Typpolymorphismus höherer Ordnung abgelehnt – obwohl diese Ideen längst den Praxistest bestanden haben. Was immer wieder übersehen wird ist, dass viele Features, die für die tägliche Arbeit unnötig scheinen, in Bibliotheken und Frameworks essentiell sein können: Jeder, der in Scala eine Liste mapped, verwendet dabei Typpolymorphismus höherer Ordnung – aber er muss dazu nicht einmal wissen, was das ist.

Ich denke dass für viele Detail-Lösungen der neuen Sprachen auch Platz in Scala gewesen wäre, wenn die Macher auf Kooperation gesetzt hätten. Aber jetzt wird viel Arbeit und Gehirnschmalz in Dinge investiert, die es zum größten Teil schon gibt, und Projekte gestartet, die keinen Erfolg haben können. Ein Blick in die Geschichte zeigt, was mit gut gemeinten, aber zu konservativen Ansätzen passiert: Nice hatte viele gute Ideen und nette Details, aber nicht genug, um die Java-Community wirklich zu begeistern, und neue Perspektiven zu eröffnen.

Ich will kein aufgehübschtes Java, und auch kein weichgespültes Scala. Sicher ist es nett, wenn hier und da eine Syntax-Kante geglättet wird. Aber wenn mir am Ende die Ausdrucksstärke fehlt, um meine Gedanken in Code umzusetzen, nützt mir das alles nichts. Ich will keine Sprache zwischen Java und Scala – wenn überhaupt, dann zwischen Scala und Haskell, mit mehr Abstraktionsmöglichkeiten, nicht weniger. Scala ist sicher nicht der Endpunkt der Entwicklung, aber es schwimmt in die richtige Richtung. Nass, aber sauber.

Update

Hier noch ein interessanter Blog-Post, der in die gleiche Richtung und dabei etwas mehr ins Detail geht: Scala, Kotlin, Ceylon… let’s start by being honest.

16
Jul
11

Interview

Normalerweise poste ich nicht einfach nur Links, das kann Planet Scala besser. Aber beim Interview von Martin Odersky auf Dr. Dobbs mache ich eine Ausnahme: Es ist wirklich lesenswert, weil es hier nicht nur um aktuelle Probleme, sondern auch die allgemeine Richtung geht, die die Sprache in der Zukunft nehmen soll.

07
Jul
11

Eigene Extraktoren

Heute nur ein paar Zeilen zu eigenen Extraktoren. Ich denke, einen guten Überblick über die verschiedenen Möglichkeiten gibt Jesse Eichar in seinem Daily Scala Blog. Ich will heute einen ganz einfachen Satz von Extraktoren vorstellen, mit denen man sich in vielen Fällen Guards sparen kann. Angenommen, wir haben folgenden Code:

case class Edge(from:String, to:String)

def makeEdge(p:(String,String)) = p match {
  case (from,to) if from != to => Edge(from, to)
  case _ => error("Can't create edge with identical end points")
}

Wäre es nicht schön, wenn wir einen Extraktor hätten, der auf Ungleichheit prüfen kann? Kein Problem:

object Ne {
   def unapply[T](pair:(T,T)):Option[(T,T)] = 
     if (pair._1 != pair._2) Some(pair) else None
}

def makeEdge(p:(String,String)) = p match {
  case Ne(from,to) => Edge(from, to)
  case _ => error("Can't create edge with identical end points")
}

Nach dem gleichen Schema lässt sich eine ganze Familie von Extraktoren basteln. Für Größenvergleiche benötigen wir zusätzlich einen impliziten Ordering-Parameter, und für den Gleichheits-Extraktor geben wir natürlich nur einen Wert zurück – zwei gleiche wären ja ziemlich witzlos. Hier die ganze Sammlung:

object Eq {
   def unapply[T](pair:(T,T)):Option[T] = 
      if (pair._1 == pair._2) Some(pair._1) else None
}

object Ne {
   def unapply[T](pair:(T,T)):Option[(T,T)] = 
     if (pair._1 != pair._2) Some(pair) else None
}

object Lt {
   def unapply[T](pair:(T,T))(implicit ord: Ordering[T]):Option[(T,T)] = 
     if (ord.lt(pair._1,pair._2)) Some(pair) else None
}

object Le {
   def unapply[T](pair:(T,T))(implicit ord: Ordering[T]):Option[(T,T)] = 
     if (ord.lteq(pair._1,pair._2)) Some(pair) else None
}

object Gt {
   def unapply[T](pair:(T,T))(implicit ord: Ordering[T]):Option[(T,T)] = 
     if (ord.gt(pair._1,pair._2)) Some(pair) else None
}

object Ge {
   def unapply[T](pair:(T,T))(implicit ord: Ordering[T]):Option[(T,T)] = 
     if (ord.gteq(pair._1,pair._2)) Some(pair) else None
}

Für eine Beschränkung habe ich leider noch keine Lösung gefunden, und zwar für Alternativen. Man kann zwar schreiben:

def g(p:(Int,Int)) = p match {
  case (10,20) | (20,10) => println("yes!")
  case _ => println("nope")
}

Aber es ist verboten, in den einzelnen Alternativen Variablen zu belegen, z.B.:

//doesn't work
def g(p:(Int,Int)) = p match {
  case (10,n) | (n,10) => println(n)
  case _ => println("nope")
}

Im Prinzip sollte das für den Compiler kein Problem sein, wenn in beiden Zweigen die gleiche Anzahl Variablen mit den gleichen Typen verwendet wird. Leider scheint es auch mit eigenen Extraktoren nicht möglich zu sein, das gewünschte Verhalten abzubilden. Oder hat jemand vielleicht eine zündende Idee?

So, ich denke das reicht für heute. Demnächst gibt es das Kohl-Ziege-Wolf-Puzzle in Haskell, aber das braucht noch etwas Feinschliff, vor allem der Kohl.




Follow

Bekomme jeden neuen Artikel in deinen Posteingang.