clojure macro to generate functions

后端 未结 2 1175
北海茫月
北海茫月 2021-01-16 04:50

I\'m trying to write a macro that will generate n functions. Here\'s what I have so far:

; only defined this because if I inline this into make-placeholders
         


        
2条回答
  •  北海茫月
    2021-01-16 05:14

    You are getting confused about the distinction between runtime and compile-time, and between macros and functions. Solving a macro problem with eval is never1 the right answer: instead, make sure that you return code that does what you want to happen. Here's a minimal change to make your original version work.

    The main changes are:

    1. defn-from is a function, not a macro - you just want a convenient way to create lists, which the main macro is responsible for inserting into the result form. You do not want a macro here, because you don't want it expanded into the body of make-placeholders.

    2. make-placeholders starts with a do, and does its for outside of a syntax-quote. This is the most important part: you want the code returned to the user to look like (do (defn ...)), as if they'd typed it all in by hand - not (for ...), which could only ever def a single function.


    (defn defn-from [str mdata args & body]
        `(defn ~(symbol str) ~mdata ~args ~@body))
    
    ; use list comprehension to generate n functions
    (defmacro make-placeholders [n]
      (cons `do
            (for [i (range 0 n)]
              (defn-from (str "_" i) {:placeholder true}
                '[& args]
                `(nth ~'args ~i)))))
    
    user> (macroexpand-1 '(make-placeholders 3))
    (do (clojure.core/defn _0 {:placeholder true} [& args] (clojure.core/nth args 0)) 
        (clojure.core/defn _1 {:placeholder true} [& args] (clojure.core/nth args 1)) 
        (clojure.core/defn _2 {:placeholder true} [& args] (clojure.core/nth args 2)))
    

    1 Very, very rarely


    Edit

    You can also do this completely without macros, by using a function to create functions and using the lower-level operation intern instead of def. It turns out to be much simpler, in fact:

    (letfn [(placeholder [n]
              (fn [& args]
                (nth args n)))]
      (doseq [i (range 5)]
        (intern *ns* (symbol (str "_" i))
                (placeholder i))))
    

提交回复
热议问题