2015-07-31 3 views
6

Ich versuche ein ELisp Makro zu schreiben, um eine multiple Funktionen basierend auf einigen gemeinsamen Daten zu generieren. Zum Beispiel, wenn ich die fn Namen berechnen möchte ich schreibe so etwas wie (ich ignoriere Hygiene für den Moment bin ich vorbei ein Symbol wörtliche in das Makro so Auswertung sollte keine Rolle spielen):Kann nicht Funktionen im Makro mit Namen von make-Symbol definiert aufrufen

(cl-defmacro def-fns (sym) 
    "SYM." 
    (let ((s1 (make-symbol (concat (symbol-name sym) "-1"))) 
     (s2 (make-symbol (concat (symbol-name sym) "-2")))) 
    `(progn (defun ,s1() (+ 1 2 3)) 
      (defun ,s2() "six")))) 

was erwarte ich 2 fns zu erzeugen, wenn sie aufgerufen werden, genannt foo-1 und foo-2.

Ich soll dann in der Lage sein, die Makro- und FNS wie so aufzurufen:

(def-fns foo) 
(foo-1) 
;; => 6 
(foo-2) 
;; -> "six 

Auch die macroexpansion von (def-fns foo) in Emacs legt nahe, dass dies der Fall sein sollte:

(progn 
    (defun foo-1 nil (+ 1 2 3)) 
    (defun foo-2 nil "six")) 

Wenn jedoch Ich werte die def-fns Definition aus und rufe sie auf nicht erzeuge diese Funktionen. Warum ist das der Fall? Diese Technik funktioniert in Common Lisp und in Clojure (die sehr ähnliche Makrosysteme haben), also warum nicht in ELisp?

Antwort

9

Ihr Code würde auch nicht in CL funktionieren.

Das Problem ist mit make-symbol - es hat ein neues Symbol erzeugt, so dass

(eq (make-symbol "A") (make-symbol "A")) 
==> nil 

Dies bedeutet, dass Ihr Makro die Funktionen schafft, sondern bindet sich Symbole, die Sie nicht mehr auf einen Griff haben.

Wenn Sie bewerten (foo-1), Emacs Lisp Leser versucht, die Funktion der Bindung des interniert Symbol foo-1, nicht das frische uninterned Symbol Ihr Makro erstellt zu finden.

Sie benötigen intern stattdessen verwenden: es macht das Symbol "allgemein zugänglich", so zu sprechen:

(eq (intern "a") (intern "a)) 
==> t 

So wird der korrigierte Code wie folgt aussieht:

(defmacro def-fns (sym) 
    "SYM." 
    (let ((s1 (intern (concat (symbol-name sym) "-1"))) 
     (s2 (intern (concat (symbol-name sym) "-2")))) 
    `(progn (defun ,s1() (+ 1 2 3)) 
      (defun ,s2() "six")))) 
(def-fns foo) 
(foo-1) 
==> 6 
(foo-2) 
==> "six" 

Hinweise :

  1. Wenn Sie CL verwendet haben, das Symbol ohne Symbol s wäre als #:foo-1 gedruckt worden und die Quelle Ihres Problems wäre Ihnen offensichtlich gewesen.
  2. Es ist außerordentlich selten, dass Sie wirklich müssen make-symbol verwenden. Normalerweise möchten Sie entweder intern oder gensym verwenden.
+0

In der Tat. Es ist eine Weile her, seit ich irgendwelche CL/Clojure/Elisp-Stil-Makros geschrieben habe und es ist ein leichter Fehler, es zu machen, anscheinend sogar einfacher in Elisp als in CL :) – jjpe

Verwandte Themen