Tja, da denkt man jahrelang, das AOP halt ein ganz nützliches Tool zum Loggen ist, da schreibt doch endlich mal jemand wofür das so alles gut ist und man fängt an sich über Meta-Programierung und den Ursprung von AOP - dem Meta-Objekt-Protokoll - Gedanken zu machen.
Aspekt-Orientiertes Programmieren
Funktionen als voll berechtigte Datentypen
Funktionale Sprachen erhalten ihre Eleganz dadurch, dass sie die Programmierung mit Funktionen als Argumente und Rückgabewerte erlauben. Schön erklärt wird das im SICP. Tatsächlich kann das so weit gehen, dass zum Beispiel in Lisp gar kein Unterschied mehr zwischen Funktionen und beliebigen Daten gemacht wird. Aber das ist zugegebenermaßen extrem.
FĂĽr die KĂĽrze nennt man diese Art zu programmieren auch gerne: "Higher-Order-Programmierung" und die entsprechenden Funktionen "Higher-Order-Funktionen".
In der Imperativen Programmierung werden Funktionen als Argumente oder RĂĽckgabetypen eher selten verwendet. In C gerade noch beim Standard-Algorithmus Binary-Search
void * bsearch(const void *key, const void *base, size_t nmemb, size_t size, int (*compare) (const void *, const void *));
bei dem die compare-Funktion genau so eine Higher-Order-Funktion ist. Danach ist es aber erstmal lange zappenduster, was Higher-Order-Programmierung angeht. (Hervorragende Programmierer nutzten dieses Design-Pattern aber schon immer - wenn es auch selten explizit genannt wurde)
So kam es dann auch, dass manche objektorientierten Sprachen Objekte wie Knöpfe, Textfelder etc. hatten, die man ableiten konnte, um ihr Verhalten zu verändern. Java etwa erlaubt das noch heute:
// Java class OKButtonForThatSpecificDialog extends JButton { public void doClick() { System.out.println("Yay, I was clicked"); } }
Zwei Probleme ergeben sich aus dieser Art zu programmieren:
- Erstens: jede Menge duplizierter Code, der größtenteils identisch ist. Damit erlebt man einen Alptraum, sobald man auch nur daran denkt, etwas Grundlegenderes an dem Programm zu ändern.
- Zweitens ist der Code, der tatsächlich die Kontrolle über das Programm ausführt, so in viele Dateien oder zumindest Klassen verstreut, also mitnichten an einer Stelle vereint und übersichtlich.
Beide Probleme kann man mit dem oben beschriebenen Mechanismus der Higher-Order-Funktionen elegant lösen - und tatsächlich wird das auch gemacht. In Inversion of Controll (IOC)-Frameworks etwa. Hier ruft man nicht mehr eine Funktion auf, um ein Ergebnis zu bekommen, sondern konfiguriert den Framework (Knöpfe, Textfelder, etc.) mit eigenen (Higher-Order)-Funktionen, die dieser aufruft, sobald ein Ergebnis oder Ereignis vorliegt.
Absolut typisch fĂĽr dieses Muster sind alle mir bekannten aktuell verwendeten GUI-Toolkits, die immer so funktionieren, dass man z.B. einen Knopf erzeugt, um danach mit einer (Higher-Order)-Funktion zu konfigurieren, was denn passieren soll, wenn auf ihn geklickt wird.
// Java JButton button = new JButton("Titel"); button.addActionListener(aClickResponder);
// Obj-C [aButton setTarget: self]; [aButton setAction: @selector(respondToClick:)];
Aber dieses Muster ist noch viel weiter verwendbar - in Objective-C gibt es eine Möglichkeit, auf alle Elemente eines Containers eine Methode doSomethingWithEachElement: anzuwenden, die ein Argument - das jeweilige Element - erhält.
// Obj-C [aList makeObjectsPerformSelector:@selector(doSomethingWithEachElement:)];
Äquivalent läuft das in den GUI-Toolkits von Java, Cocoa, Gnome, KDE, WxWidgets...
Fortgeschrittenere Sprachen und Toolkits verwenden dieses Design-Pattern auch noch an vielen weiteren Stellen. Besonders hervorzuheben ist dabei in letzter Zeit Ruby, wo nach diesem Muster nicht nur Iteration, sondern auch das Bearbeiten jeder Zeile in einer Datei, das Senden von Daten über einen Socket, ... verläuft.
Am extremsten ist aber vielleicht die Sprache Smalltalk, wo es ĂĽberhaupt keine normalen Kontrollstrukturen im Sinne von C/Java mehr gibt und diese Aufgabe komplett durch Higher-Order-Funktionen erledigt wird. Hier gibt es beispielsweise ein Boolean Objekt, das eine Methode #ifTrue:ifFalse:
besitzt. Diese Methode bekommt dann zwei Callbacks ĂĽbergeben und fĂĽhrt je nachdem, ob es das True- oder das False-Boolean ist, polymorph den jeweils anderen Callback aus. - Analog werden Schleifen, Exception-Handling... einfach alle Kontrollstrukturen definiert.
Das nette daran: Damit kann man innerhalb der Sprache selber Kontrollstrukturen definieren. Schick, nicht?
Umsetzung
Um mit Higher-Order-Funktionen in Java / Obj-C zu programmieren, kann man z.B. so vorgehen:
// Obj-C - (void) setAction: (SEL) aSelector forTarget: aTarget { action = aSelector; target = aTarget; } - (void) trigger { if ([target respondsToSelector: action]) { [target performSelector: action]; } }
// Java void setActionListener(ActionListener listener) { this.listener = listener; } void trigger() { listener.actionPerformed(null); }
Das wars - so einfach geht das.
TODO: Auf [http://libsigc.sourceforge.net/libsigc2/docs/manual/html/ch02.html] verweisen und vergleichen wie Callbacks mit sowas und (beste C++ Callback Bibliothek) gehen. Vergleichen wie echte Lambdas das leichter machen können.
Neue Religionen...
...sind sicherlich heute sehr selten.
Wo ich gerade beim Thema sind... Jeder sollte sein Softwarepatent des Monats wählen!
How to fold a shirt
[/blog-attachments/videos/HowToFoldAShirt.mpg Hausahltsanleitung....]
Unterdokumentierte Design Patterns
Eine Frage die mich schon länger beschäftigt, ist das Finden von Design Patterns in Funktionalen Sprachen. Lange Zeit schien es mir so, als gäbe es da gar nichts, auch viele Programmierer, die in diesen Sprachen arbeiten, konnten mit dem Begriff nichts anfangen. Dabei gibt es meiner Meinung nach eine Menge, das man von funktionalen Sprachen lernen kann - und einiges, das schon gelernt wurde.
Einige Design Patterns, die ich seitdem gefunden habe, sind:
- [wiki:BlogEntries/2006-02-23 Funktionen] als voll berechtigte Datentypen, d.h. man kann sie als Argument oder RĂĽckgabewert in der Programmiersprache verwenden
- Die Verwendung von Lambda-AusdrĂĽcken (anonymen Funktionen)
- FĂĽr Iteration, als Callback, fĂĽr Fehlerbehandlung, Kontrollfluss...
- Higher Order Funktionen - Funktionen als Argument und RĂĽckgabewert von Funktionen
- Currying zum teilweisen Anwenden von Funktionen (vor allem in Zusammenhang mit anonymen Funktionen interessant)
- Funktionen zur Kombination von Funktionen zu größeren Einheiten (sehr schön z.B. in Parsern)
- Monaden, zum sicheren Einbetten von Seiteneffekten in puren funktionalen Sprachen
- Arrows, für sichere Nebenläufigkeit in puren funktionalen Sprachen
- Unendliche Listen als Fassung von Reihen von Zahlen / Ereignissen. (Aktionen des Benutzers, Zufallszahlen, stetig genauer werdende Berechnungen einer Zahl...)
- Continuations: Ein Konstrukt mit dem sich z.B. Exceptions und Coroutinen implementieren lassen
- Coroutinen: Am einfachsten als eine Art Ultraleicht-Thread zu verstehen
Einige dieser Design Patterns werden auch in anderen Sprachen intensiv genutzt. Die Lambda-Abstraktion zum Beispiel spielt eine zentrale Rolle in Java, wenn man dort GUIs baut, da sie der ideale Callback sind. Also im Beispiel:
JButton button = new JButton("Titel"); button.addActionListener(new ActionListener() { public void actionPerformed(java.awt.event.ActionEvent e) { // this is a lambda } }
Gibt es noch mehr dieser Design Patterns? Funktionale Sprachen werden doch von jeder Menge Menschen benutzt...
Sind die irgendwo dokumentiert? Wenn nicht, dann muss ich mich wohl ranhalten und mein Verständnis selber aufschreiben.
Außerdem haben hochintegrierte Sprachen wie Lisp, Scheme, Smalltalk und Ruby noch ein weiteres Pattern, da sie sich besonders gut (oder überhaupt?) dazu eignen, Domänenspezifischen Subsprachen (DSL), also Sprachen die direkt in der Implementierungssprache eingebettet sind, zu formulieren. In Lisp bzw. Scheme ist das sogar die bevorzugte Vorgehensweise!
Mir ist allerdings noch nicht klar, welches Kriterium eine Sprache in die Lage versetzt, diese Integrierten DSLs besonders gut umzusetzen. Bisher dachte ich immer, dass dazu eine in ihrer Ăśbersimplifizierung geniale Sprachdefinition - wie in Lisp und Smalltalk - notwendig ist, aber gerade das Beispiel von Ruby scheint dem zu widersprechen.
Preisfrage
Man hat 8 Billiard-Kugeln, von denen man weiĂź das eine schwerer ist als die Anderen. Desweiteren hat man eine Balkenwage mit der man zwei mal Wiegen, also zwei Gewichte vergleichen, darf.
Lässt sich unter diesen Vorraussetzungen herausfinden welche Kugel die schwerere ist?
Viel spaĂź beim Raten. :)
Oh, and by the way: TCPA ist immer noch doof: Erklärender Film
Ober- und Unterton- oder Kehlgesang
Was es alles gibt! Ich bin fasziniert wozu die Menschliche Stimme so alles in der Lage ist. Obertongesänge kannte ich auch schon länger, aber die zweite Variante, Untertongesänge, wie sie hauptsächlich in Tuva vorkommt war mir neu.
Viel sagen will ich dazu eigentlich gar nicht, das muss man mal gehört haben:
- Kehlgesang aus Jerusalem, sowohl Sygit als auch Khöömej mehr...
- Huun-Huur-Tu eine "Band" aus Tuva mit allen Varianten von Kehlgesang (Beispiele von Khomeei.com):
- Mehr Beispiele: noch mehr Tuva und noch ein anderer Stiel, wie er von Inuits praktiziert wird
Weitere Informationen gibt's bei der Wikipedia, Oberton.org und auch bei Khomeei.com der das ganze auch schön in Spektrogrammen zeigen kann.
Notiz an selbst: Bei gelegenheit mal den Film Ghengis Blues anschauen.
Nachnotiz an selbst: Zum Lernen hier, hier (unten) und hier lang.
Präsentieren wie die Meister
Wieder mal ist ein Wochenende dafür drauf gegangen mich selbst über Präsentationstechniken fortzubilden.
Einen Riesigen Einfluss auf mich hat dabei Guy Reynolds mit seinem Blog Presentation Zen. Wie soll ich sagen - der Hammer! Einige der Präsentationstechniken die er vorstellt, sind einfach ein Must Read.
Das ist zumindest ein sehr inspirierender Anfang, von da aus weitersurfend findet man noch viele Beispiele.
Ich bin Beeindruckt.
Wer fĂĽr das alles keine Zeit hat, der sollte zumindest diese 10 Gebote befolgen!
GAPMINDER
Die Entwicklung unserer Welt, in Zahlen, ist eine komplizierte und unverständliche Materie.
Denkste.
Gapminder nutzt moderen Visualisierungstechniken konsequent aus um klarzumachen, wie sich unsere Welt in den letzten ~40 Jahren entwickelt hat. (via tedblog)
Dazu auch: Warum wir uns jetzt nicht um die globale Erwärmung kümmern sollten.
Gute Reden
Einerseits ernsthaft, lustig andererseits.
Als Nobelpreisträger darf man eben den Mund aufmachen und es wird einem hoffentlich zugehört. Hoffentlich. Der Nobelpreisträger der Literatur jedenfalls hat beschlossen über Amerikanische Politik zu sprechen. Und ich finde hervorragend!
Auf der anderen Seite wieder etwas technisches: Daniel Wilson wird von Moira Gunn ĂĽber sein Buch How to survive a robot uprising ausgefragt - sehr unterhaltsam.