Ich habe meine kleine Makrosammlung für Common Lisp, die bislang lediglich einige Makros enthält, die das Schreiben anonymer Funktionen vereinfachen sollen, ein wenig überarbeitet. Der Code ist jetzt in kleinere Stücke aufgeteilt, Unittests wurden erzeugt (hauptsächlich aus den Benutzungskommentaren, die von Anfang an da waren), und die Syntax der Makros wurde entscheidend verbessert.

Alter Code ist nicht kompatibel mit der neuen Version , doch erscheint mir dies noch als kein großes Problem, da ich momentan von einer Anwenderbasis von genau einer Person (mir selbst) ausgehe. Kommentare hat jedenfalls noch niemand in das Journal hier geschrieben...

Die Installation funktioniert wie gehabt:

(asdf-install:install "http://matthias.benkard.de/code/mulkutils-0.2.0.tar.gz") 

Es ist allerdings mit Kompilierfehlern der Testcases zu rechnen. Ich bin diesbezüglich noch am Rätseln. Anwender müssen das System mulkutils-tests ja nicht laden: Sie dürften sich eher für das System mulkutils interessieren, das eigentlich problemlos kompilierbar sein sollte.

Die Veränderungen ###

Syntax ####

Die Syntax für die einfache Form der FN -Makros wurde so verändert, daß sie für Common-Lisp-Programmierer einfacher zu lesen ist. Anstelle von (fn OPERATOR ARGUMENTE ...) muß nunmehr (fn #'OPERATOR ARGUMENTE ...) geschrieben werden. Dies betont die Tatsache, daß OPERATOR tatsächlich der Name eines Operators ist. Als praktische Nebenwirkung eröffnet es die Möglichkeit, Funktionsobjekte als Operatoren zu verwenden.

Korrekte lexikalische Suche der Argumentnamen ####

Zuvor war es nicht möglich, die Funktion (efn (efn () _)) ohne Argumente aufzurufen, obwohl der korrekte Rückgabewert eine Funktion ohne Argumente hätte sein sollen. Dies lag daran, daß die Makros der FN-Familie nicht in der Lage waren, innere von äußeren Argumenten oder gar lexikalischen Variablen zu unterscheiden, weshalb das innere Argument » _ « als sowohl der inneren als auch der äußeren FN-Form zugehörig erkannt wurde. Dieser offensichtliche Fehler wurde in der neuen Version durch die Verwendung eines Codewalkers aus Marco Baringers ARNESI-System behoben.

Neues Makro FN* ####

Die Familie der FN -Makros hat Nachwuchs bekommen: Es gibt jetzt ein Makro namens FN* , das genauso funktioniert wie FN , außer daß es eine Funktion erzeugt, die überschüssige Argumente nicht einfach verwirft, sondern sie noch dem angegebenen Operator übergibt. Damit erfüllt sie einen Zweck, der einerseits dem eines CURRY-Makros gleichkommt, andererseits aber analog zu FN funktioniert und dessen Features erbt.

Ich bin mir noch nicht sicher, ob die Makros FN und EFN später einmal ein Symbol für restliche Argumente einführen werden. Wenn, dann wird es vermutlich __ heißen.

Dokumentation ###

Da sowohl die Syntax über den Haufen geworfen und idiomatisiert als auch neue Funktionalität hinzugefügt sowie dokumentiert fehlerhaftes Verhalten behoben wurde, reproduziere ich im folgenden noch einmal die bisher vorhandene Dokumentation.

#| Basic usage  
 | ===========  
 |  
 | (fn #'+ _ 10)  
 | (fn (+ _ 10))  
 | (fn #'+ _0 _1)  
 | (fn #'+ _ _1)  
 | (fn #'+ _ (/ _1 2))  
 | (mapcar (fn (cons _ _)) '(1 2 3))       ;=> ((1 . 1) (2 . 2) (3 . 3))  
 | (funcall (fn (+ _ 10 _3)) 20 30 40 50)  ;=> 80  
 |  
 |  
 | Simple variant FN1  
 | ==================  
 |  
 | (funcall (fn () _) 42)                ;=> 42  
 | (funcall (fn _) 42)                   ;=> error (usually)  
 | (funcall (fn1 _) 42)                  ;=> 42  
 | (funcall (fn +))                      ;=> error (usually)  
 | (funcall (fn1 +))                     ;=> value of +  
 | (funcall (fn #'+))                    ;=> 0  
 | (funcall (fn1 #'+))                   ;=> #<FUNCTION +>  
 |  
 |  
 | Argument-number-safe variant EFN  
 | ================================  
 |  
 | (funcall (fn (fn () _)))              ;=> #<LAMBDA ...>  
 | (funcall (efn (efn () _)))            ;=> #<LAMBDA ...>  ; new!  
 | (funcall (fn1 (fn1 _)))               ;=> #<LAMBDA ...>  
 | (funcall (efn1 (efn1 _)))             ;=> #<LAMBDA ...>  ; new!  
 | (funcall (fn1 _3) 1 2 3 4 5)          ;=> 4  
 | (funcall (efn1 _3) 1 2 3 4 5)         ;=> error  
 |  
 |  
 | Currying variant FN* (cf. LIST vs. LIST*)  
 | =========================================  
 |  
 | (funcall (fn* #'+ 1) 2)               ;=> 3  
 | (funcall (fn* #'+) 1 2)               ;=> 3  
 | (funcall (fn* (/ (* _ 4))) 3 6)       ;=> 2  
 |#