Doh! Major face palm. I had such a bad case of tunnel vision on the macro definition I forgot to look up the stack once I'd fixed the macro. Thanks. Good to have extra eyes.
On Mon, Jul 21, 2014 at 1:04 PM, Matthew DeVore <matv...@gmail.com> wrote: > I haven't taken the time to fully grok your macro, but the error is not > the fault of your macro, but of the function invocation. (foo x) is causing > the error because x is undefined. foo is a plain function, not a macro, so > it tries to evaluate each argument. (foo 'x) works fine for me, as does > (let [x 42] (foo x)) > > I guess the fact that (foo x) recycled the name of the parameter used in > the function definition is what made the error message confusing? > > > On Monday, July 21, 2014 9:49:10 AM UTC-7, Dave Tenny wrote: > >> In my case, I'm trying to use a the expression '[x] that is available to >> the macro in the resulting product. So I don't want the value of x at all, >> just a vector with the symbol x. >> >> That vector is in a variable I have in the macro named >> 'positional-parameters', so I substitute '~positional-parameters to with >> the hope of getting '[x] in the resulting definition. >> >> Here's a fragment of the overall defs. As you can see I'm just messing >> around, I was trying to write a defun with 'supplied-p' lambda list >> semantics, and my clojure/macrology is awful. It's the macro near the >> bottom. You can append this to >> https://github.com/dtenny/clojdt/blob/master/src/jdt/cl.clj if you want >> to git clone and compile it. >> >> (def lambda-list-keyword-symbols >> "Symbols without namespace specifications" >> (into #{} (map symbol ["&allow-other-keys" "&key" "&rest" "&aux" >> "&optional"]))) >> >> (defn lambda-list-keyword? >> "If x is a lambda list keyword, in any case, return the canonicalized >> form as a symbol. >> If it starts with an ampersand but is not a valid lambda list keyword, >> throw an exception." >> [x] >> (let [sym-name (.toLowerCase (name x))] >> (if (= (first sym-name) \&) >> (if-let [lambda-sym (lambda-list-keyword-symbols (symbol sym-name))] >> lambda-sym >> (throw (Exception. (str "'" x "' is not a valid lambda list >> keyword.")))) >> nil))) >> >> (defn lambda-list-segment >> "Return three values in a vector. >> >> The first value is a vector of non-lambda-list-keywords >> in l before the next lambda list keyword in the list. Order is >> preserved. >> >> The second value is the canonicalized lambda list keyword (as a >> clojure symbol, e.g. '&aux) >> that terminated accumulation, or nil if we reached the end of the list. >> >> The third value is the rest of the list following the lambda list >> keyword, or nil if there aren't >> any more values. >> >> E.g. (lambda-list-segment '(a b &optional c)) >> => [[a b] &optional (c)]" >> [l] >> (loop [result [] l l] >> (if (empty? l) >> [result nil nil] >> (let [head (first l) >> tail (rest l) >> lambda-keyword (lambda-list-keyword? head)] >> (if lambda-keyword >> [result lambda-keyword tail] >> (recur (conj result head) tail)))))) >> >> (defn partition-lambda-list >> "Return a map keyed by lambda list keyword (or the pseudo keyword >> '&positional' >> for positional args), and valued by a vector lambda vars associated >> with the >> type of lambda list element, in the order specified by the caller. >> E.g. (partition-lambda-list [a b c &optional (d nil d-supplied-p) &key >> e]) >> => {&positional [a b c] &optional [(d nil d-supplied-p)] &key [e]} >> Ignore case in things appearing to be lambda list keywords. >> Complain if any symbol begins with an ampersand and is not a valid >> lambda list keyword. >> Preserve order of parameters." >> [lambda-list] >> (let [first-chunk (lambda-list-segment lambda-list) >> positionals (first first-chunk)] >> (loop [result {'&positional positionals} >> next-lambda-key (second first-chunk) >> lambda-list (nth first-chunk 2)] >> (if (empty? lambda-list) >> (if next-lambda-key >> (merge result {next-lambda-key []}) ; in case it's >> &allow-other-keys >> result) >> (let [chunk (lambda-list-segment lambda-list)] >> (recur (merge result {next-lambda-key (first chunk)}) >> (second chunk) >> (nth chunk 2))))))) >> >> (defn parse-defun-decls >> "Parse defun elements following the lambda list. >> If there are declarations, strip them from the sequence. >> Return a vector of two things. >> 1) the docstring, or nil if there isn't one. >> 2) forms following the optional declarations and docstring, nil if >> there aren't any." >> [s] >> (let [declare? (fn [l] >> (and (list? l) >> (.equalsIgnoreCase (name (first l)) "declare")))] >> (loop [s s] >> (if (empty? s) >> [nil nil] >> (let [head (first s)] >> (cond (string? head) [head (rest s)] ; **BUG** won't work if >> declare is after docstring >> (declare? head) (recur (rest s)) >> :else [nil s])))))) >> >> (defn positional-values >> "Given a sequence of formal parameter names for positional values >> and a sequence of actual arguments to a defun'ed function call, >> return a vector of values from actual args to be bound to parameters. >> Return [] if there aren't any." >> [parameters actual-args] >> (if parameters >> (if (< (count actual-args) (count parameters)) >> (throw (Exception. (str "Insufficient arguments in " actual-args >> " to match positional parameters in " >> parameters))) >> (loop [values [] args actual-args] >> (if (empty? args) >> values >> (recur (conj values (first args)) (rest args))))) >> [])) >> >> ;; *FINISH*: define/support RETURN-FROM >> >> (defmacro defun >> "defun function-name lambda-list [[declaration* | documentation]] form* >> => function-name >> Arguments and Values: >> function-name---a function name. >> lambda-list---an ordinary lambda list. >> declaration---a declare expression that is discarded. >> documentation---a string; not evaluated. >> forms---an implicit progn. >> >> Notes: >> This is meant to be as common-lisp oriented as possible in a clojure >> environment. >> Thus a function definition is (defun foo (&optional bar) ...) >> and NOT (defun foo [clojure-y args] ...)" >> [function-name lambda-list & decls-doc-forms] >> (let [param-map (partition-lambda-list lambda-list) >> [docstring forms] (parse-defun-decls decls-doc-forms) >> args-var (gensym "args") >> positional-parameters (get param-map '&positional)] >> (if docstring >> (comment ; *FINISH* docstring variant >> ;; `(defn ~function-name ~docstring [& ~args-var] >> ;; (let [~@(lambda-list-bindings param-map args-var)] >> ;; ~@forms)) >> ) >> `(defn ~function-name [& ~args-var] >> ;; *FINISH*: other param types once we get positional values >> working >> (let [~positional-parameters (#_positional-values print >> '~positional-parameters ~args-var)] >> ~@forms))))) >> >> (println (macroexpand-1 '(defun foo (x) (println x)))) >> ;(println (macroexpand '(defun foo (x) (println x)))) >> (defun foo (x) (println x)) >> (foo x) ; <<<---- generates the error >> >> >> >> >> >> On Sun, Jul 20, 2014 at 5:46 PM, Matthew DeVore <mat...@gmail.com> wrote: >> >>> I don't have a Clojure REPL handy at the moment but it looks like the x >>> symbol is being resolved in the macro context rather than the expansion >>> context. In the macro source, where you have a plain x, try to replace it >>> with ~'x ... This blog post may be relevant: >>> http://amalloy.hubpages.com/hub/Unhygenic-anaphoric- >>> Clojure-macros-for-fun-and-profit >>> >>> -- >>> You received this message because you are subscribed to the Google >>> Groups "Clojure" group. >>> To post to this group, send email to clo...@googlegroups.com >>> >>> Note that posts from new members are moderated - please be patient with >>> your first post. >>> To unsubscribe from this group, send email to >>> clojure+u...@googlegroups.com >>> >>> For more options, visit this group at >>> http://groups.google.com/group/clojure?hl=en >>> --- >>> You received this message because you are subscribed to a topic in the >>> Google Groups "Clojure" group. >>> To unsubscribe from this topic, visit https://groups.google.com/d/ >>> topic/clojure/grxYLrjnz60/unsubscribe. >>> To unsubscribe from this group and all its topics, send an email to >>> clojure+u...@googlegroups.com. >>> >>> For more options, visit https://groups.google.com/d/optout. >>> >> >> -- > You received this message because you are subscribed to the Google > Groups "Clojure" group. > To post to this group, send email to clojure@googlegroups.com > Note that posts from new members are moderated - please be patient with > your first post. > To unsubscribe from this group, send email to > clojure+unsubscr...@googlegroups.com > For more options, visit this group at > http://groups.google.com/group/clojure?hl=en > --- > You received this message because you are subscribed to a topic in the > Google Groups "Clojure" group. > To unsubscribe from this topic, visit > https://groups.google.com/d/topic/clojure/grxYLrjnz60/unsubscribe. > To unsubscribe from this group and all its topics, send an email to > clojure+unsubscr...@googlegroups.com. > For more options, visit https://groups.google.com/d/optout. > -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.