Für why the lucky stiff ist die Sprache Ruby keine Konstante (nicht einmal ein Eigenvektor), sondern ein Spielzeug. Das sieht man an seinem jüngsten Blogeintrag auf RedHanded in der Kategorie bits, mit dem schönen Namen EigenCharges. Die Leser waren wie üblich begeistert bis schockiert, unter anderem cypher, der auch diesen Beitrag angestoßen hat. Zeit für den Syntax-Sonntag! Im Gegensatz zu Superman kommen wir allerdings nicht immer pünktlich.
Why schreibt folgendes:
class RubyTalk < Filter + to("ruby-talk@ruby-lang.org") + cc("ruby-talk@ruby-lang.org") - from("common.troll@gifted.net") end
Da stehen der kleinen Japanerin die roten Haare zu Berge! Sieht ein wenig aus wie eine UML-Klasse. Ist aber legaler Ruby-Code. Soll vermutlich heißen: RubyTalk bekommt Standardoptionen, nämlich zwei Empfänger und einen blockierten Troll. Das Neue hierbei ist, wie die Vorzeichen (Ladungen, Charges) genutzt werden, um den Optionen zusätzliche Aussagekraft zu verleihen: Mit cc wird ein Empfänger hinzugefügt, und der arme Troll wird über das from-Feld identifiziert und nicht mehr beachtet. Ein positiv geladenes from-Feld könnte hingegen einfach den Absender definieren.
Keine Angst, dahinter steckt noch keine Funktionalität. Mit why's Code kann man rein gar nichts mailen oder posten. Es ist nur eine syntaktische Spielerei. Frizzeln wir sie doch mal auseinander:
+ to("ruby-talk@ruby-lang.org") bewirkt folgendes:
Filter.to("ruby-talk@ruby-lang.org").+@()
Zu dem ominösen +@ komme ich gleich noch.
Da die Methoden für die einzelnen Optionen sehr ähnlich sind, hat why sie einfach dynamisch erzeugt. Der folgende Code ist total simples Meta-Ruby, wie es einem auf RedHanded wöchentlich über den Weg läuft:
class Filter class << self attr_accessor :rules [:from, :to, :cc, :subject].each do |m| define_method(m) do |*a| r = Rule.new(m, *a) (@rules ||= []) << r r end end end end
Alles klar? Wenn nicht: Seeing Metaclasses Clearly ist eine gute Einführung in Rubys metamagische Fähigkeiten. Beschäftigt euch damit! Es macht wirklich Spaß, mit Ruby zu zaubern. Für diesen Artikel soll es reichen, dass für jedes der vier Symbole so eine Methode angelegt wird:
class Filter def Filter.from(*a) r = Rule.new(:from, *a) (@rules ||= []) << r r end end
Ein Aufruf von to erzeugt also ein neues Rule-Objekt. Die erzeugten Regeln werden in @rules gespeichert - einer Klasseninstanzvariable. Sieht aus wie eine normale Instanzvariable? Ist auch eine. Sie gehört zu der Klasse, in der to aufgerufen wird, denn Klassen sind auch nur Objekte. Zwischenergebnis: to liefert eine Instanz von Filter::Rule.
Nein. +@ ist ein interner Name für den unären +-Operator. Unär heißt, dass der Operator nur einen Operanden hat. Das ist gar nichts besonderes: Das unäre Vorzeichen +1993 bedeutet eben etwas anderes als die binäre Addition 17 + 4. Beides sind aber nur Funktionen: Man könnte auch neg(1993) oder add(17, 4) schreiben. Ruby unterscheidet diese beiden Methoden ebenfalls und nennt sie +@ (unär) und + (binär). Beim Minuszeichen analog -@ und -. Beim Einlesen des Ruby-Programms wird entschieden, ob solch ein mehrdeutiger Operator unär oder binär gemeint ist. Hier ist es eindeutig die +@-Methode:
+ cc("ruby-talk@ruby-lang.org")
Wie gesagt, Ruby unterscheidet im Allgemeinen anhand der Verwendung, welche Methode sie aufruft. Die internen Namen benutzt man, wenn man die Methoden überschreiben möchte, wie why es hier getan hat:
class Filter class Rule attr_accessor :field, :terms, :action def initialize(f, *a) @field = f @terms = a end def -@; @action = :reject end def +@; @action = :accept end end end
Schön, die Filter-Klasse enthält also noch eine Hilfsklasse namens Rule. Wie wir bereits wissen, liefert uns ein Aufruf von Filter.to() so ein Regel-Objekt. Die Instanzvariablen des Objektes werden auf verschiedene Weise gesetzt:
Der Aufruf + to("ruby-talk@ruby-lang.org") erzeugt also eine Instanz von Filter::Rule mit:
Freiheit. Schönheit. Einfachheit. Vereinfachung der Sprache für den Anwender. Man könnte auch andere Varianten implementieren:
class RubyMine < Filter2 from << "superman@townsville.sw" + tag(:super) --- indent - /VIAGRA/ # insert freaky new idea here end
why's neue Sprache hat einen Schönheitsfehler: Aufrufe von unären Methoden, deren Ergebnis keiner Variablen zugewiesen wird, erzeugen Warnungen:
warning: useless use of +@ in void context
Das macht diese Lösung proplematisch bis unbrauchbar. Ob Das Ruby-Core-Team daran gedacht hat, als es die Warnung implementierte? Ich habe sie jedenfalls bisher nur dann gesehen, wenn ich sie nicht haben wollte.
Weg damit! Mehr Freiraum für DSLs!
Kommentar schreiben