I have written a if-pred? macro as follows
(defmacro if-pred?
([pred lst then] `(if (~pred ~lst) ~then nil))
([pred lst then else] `(if (~pred ~lst) ~then ~else)))
Now I want to construct a if-empty? macro from that.
(defmacro if-empty? [lst then & else]
(if-pred? empty? lst then else))
I want to use if-empty? like:
(if-empty? '()
(println "empty")
(println "not-empty"))
But apparently if-empty? is not working. When I run the above code not-empty gets printed immaterial of whether the list I pass to if-empty? is really empty or not. After printing not-empty java.lang.NullPointerException is thrown.
You're not quoting the expansion of (defmacro if-empty? ...), and you're forcing the optional else argument into a list.
(defmacro if-empty?
([lst then else]
`(if-pred? empty? ~lst ~then ~else))
([lst then]
`(if-pred? empty? ~lst ~then)))
It's your if-empty? macro. You forgot to backtick the body. Should be:
(defmacro if-empty? [lst then & else]
`(if-pred? empty? ~lst ~then ~else))
Macros need to return code. Try this:
(defmacro if-empty? [lst then & else]
`(if-pred? empty? ~lst ~then ~#else))
As the macro if-empty? has different signature from if-pred? with the variable arity of the else part it needs a do block to function correctly.
your if-empty? macro has a few problems, first it's not back-quoted which will cause
So if you need the if-empty? to work so that it will evaluate single expression if the given sequence is empty and several ones if the sequence is not empty following macro should work:
(defmacro if-empty? [lst then & else] `(if-pred? empty? ~lst ~then (do ~#else)))
If the the & before args was an accident following should work:
(defmacro if-empty? [lst then else] `(if-pred? empty? ~lst ~then ~else))
As a side note as these macro's arent predicates i wouldn't use the ? in the end of their name.
Related
Here's my macro, what it is supposed to do is to wrap a body in let with bindings from vars-alist
(defmacro with-vars-alist (vars-alist &rest body)
`(let (,#(mapcar (lambda (cell) (list (car cell) (cdr cell))) vars-alist))
,#body))
When I am looking at what it expands to using following code
(defvar my-vars-alist '((var1 . "var1")
(var2 . "var2")))
(macroexpand-1 (with-vars-alist my-vars-alist `(concat ,var1 ,var2)))
I get an error cons: Wrong type argument: sequencep, my-vars-alist
However checking it (sequencep my-vars-alist) return t.
The error is probably has some simple solution, but I am just unable to find it.
Remember that arguments to macros are un-evaluated, which means that when you pass my-vars-alist as an argument, it is passed verbatim as the symbol my-vars-alist.
Therefore during the macro expansion, vars-alist evaluates to the symbol my-vars-alist rather than the list ((var1 . "var1") (var2 . "var2")).
So the error isn't complaining that the variable my-vars-alist doesn't contain a sequence as its value, but rather that the symbol my-vars-alist is not itself a sequence (which is correct -- it's a symbol).
checking it (sequencep my-vars-alist) return t.
Which is also correct, as there my-vars-alist is evaluated as a variable to its value of ((var1 . "var1") (var2 . "var2"))
So you need to eval that argument. e.g.:
,#(mapcar (lambda (cell) (list (car cell) (cdr cell)))
(eval vars-alist))
As vars-alist is already being evaluated to the symbol my-vars-alist, this change means that we are passing that symbol my-vars-alist to eval, which evaluates it as a variable to obtain the list needed for mapcar.
You probably also wanted to quote the form you pass to macroexpand-1 (or use M-x pp-macroexpand-last-sexp).
I looks like what you're trying to do is let-bind the keys of an alist to their values, so you can use the keys as variables in the let body. There's a built in macro for this in recent Emacs:
(let-alist '((a . 1) (b . 2))
(message "a: %d, b: %d" .a .b))
I'm trying to write my first ever macro in Clojure. I want to mimic Ruby's %w{} operator, which works like this:
irb(main):001:0> %w{one two three}
=> ["one", "two", "three"]
I want to write a function similar in Clojure that returns a vector of words. Here is how it would look:
user=> (%w one two three)
=> ["one" "two" "three"]
I know this is something that cannot be defined as an ordinary function because the symbols would be evaluated before applying and we would see something like this:
user=> (%w one two three)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: one in this context, compiling:(NO_SOURCE_PATH:1:1)
Here is my attempt at a macro:
(defmacro %w [& words]
(map str (vec words)))
But it doesn't work.
user=> (%w one two three)
ClassCastException java.lang.String cannot be cast to clojure.lang.IFn user/eval801 (NO_SOURCE_FILE:1)
Why is this happening?
ANSWERS
So the problem was that the macro actually returned the correct output, but then the repl tried to evaluate it and "one" is not a valid function.
Thanks to the answers below, here are two correct macros that solve this problem:
(defmacro %w-vec [& words]
"returns a vector of word strings"
(mapv str (vec words)))
(defmacro %w-list [& words]
"returns a list of word strings"
(cons 'list (map str words)))
It does not work because after macro-expansion clojure tries to evaluate ("one" "two" "three"), inducing your error message
user=> (%w one two three)
ClassCastException java.lang.String ("one") cannot be cast to clojure.lang.IFn (interface for callable stuff) user/eval801 (NO_SOURCE_FILE:1)
now you could do that
(defmacro %w [& words]
(mapv str (vec words)))
generating a vector
or that
(defmacro %w [& words]
(cons 'list (mapv str (vec words))))
generating (list "one" "two" "three")
or with syntax quote
(defmacro %w [& words]
`(list ~#(map str (vec words))))
Macros can be thought of as regular functions that take unevaluated code (as data structures) and return new code (as data), which then gets evaluated.
Your macro is returning ("one" "two" "three"), which will evaluate as a call of the function "one" with arguments "two" "three".
Straightforward solutions would be to make your macro return either (list "one" "two" "three") or a vector ["one" "two" "three"].
I'm always fancy that there is a method to resolve symbols automatically. For example, if I am in the "user" namespace and have not "used" symbols in the `clojure.string' namespace, instead of:
(clojure.string/split "a-b-c" #"-")
I want to write as this:
(call split "a-b-c" #"-")
I have simply implemented a `call' macro as this:
(defmacro call [sym & args]
`(let [fn# (first (distinct (remove nil? (map #(ns-resolve % '~sym) (all-ns)))))]
(if-not ((meta fn#) :macro)
(fn# ~#args)
(eval (list fn# ~#(map #(list 'quote %) args))))))
the following tests are always ok:
(call list 'a 'b)
(call apropos "list")
(call doc list)
(call doc clojure.string/split)
the problem happens when I pass a macro as the argument of `doc':
(call doc clojure.repl/doc)
then, there is a exception:
CompilerException java.lang.RuntimeException: Can't take value of a macro: #'clojure.repl/doc, compiling:(NO_SOURCE_PATH:112)
So, that's why? thanks
You can resolve the symbol at compile time in the macro like this:
(defmacro call [sym & args]
(let [f (first (distinct (remove nil? (map #(ns-resolve % sym) (all-ns)))))]
`(~f ~#args)))
I'm trying to write a Lisp macro that writes a bunch of macros, but I'm having problems generating macro code that uses the splice operator (in build-bind) that expands inside expressions first.
(defmacro define-term-construct (name filter-p list-keywords)
(let* ((do-list-name (output-symbol "do-~a-list" name))
(with-name (output-symbol "with-~a" name))
(do-filter-name (output-symbol "do-~as" name)))
`(progn
(defmacro ,do-list-name
(ls (&key ,#(append list-keywords '(id operation))) &body body)
(with-gensyms (el)
`(loop-list (,el ,ls :id ,id :operation ,operation)
(let (XXX,#(build-bind ,,name ,el))
(when (,',filter-p ,el)
(,',with-name ,el
,#body)))))))))
After the first pass I want to get:
(define-term-construct some some-p (args name))
->
(PROGN
(DEFMACRO DO-SOME-LIST (LS (&KEY ARGS NAME ID OPERATION) &BODY BODY)
(WITH-GENSYMS (EL)
`(LOOP-LIST (,EL ,LS :ID ,ID :OPERATION ,OPERATION)
(LET (,#(BUILD-BIND ,SOME ,EL))
(WHEN (SOME-P ,EL)
(WITH-SOME ,EL
,#BODY)))))))
Any idea what quote/quasiquotes should I use to get the desired code?
The output that you say that you want want to get has unbalanced commas. ,# already balances the backquote, so you cannot have ,SOME and ,EL. That's two levels of unquoting/splicing inside only one level of backquoting.
I suspect you want:
`(WITH-GENSYMS (EL) ... (LET (,#(BUILD-BIND 'SOME EL)) ...))
The some symbol comes in as an argument to the original macro and has to end up as a quoted symbol when passed to the build-bind function. The EL is evaluated straight. It's just a local variable introduced by the WITH-GENSYMS binding construct, and it is not in backquote context anymore because it is inside the splice.
Transliterating that back to the the original outer macro's backquote: SOME becomes ,name:
,#(build-bind ',name el) ;; two commas out balance two backquotes in
The symbol is spliced in under the umbrella of a protecting quote which will make sure it is treated as a symbol and not a variable.
The el does not need to be spliced in; it's not variable material but a hard-coded feature of the template being generated. If you were to put ,el it would look for an el variable in the define-term-construct macro's scope, where no such thing exists.
When applying a macro multiple times with a another macro, bare Symbols are not inserted into the current context:
(defmacro ty [type]
`(deftype ~type []))
(defmacro empties [& args]
(doseq [arg args]
`(ty ~arg))
)
(empties Base Person Animal)
;equivalent to:
;(ty Base)
;(ty Person)
;(ty Animal)
(derive ::Person ::Base)
(derive ::Animal ::Base)
(ty Me)
(prn ::Me)
(prn Me)
(empties Empty)
(prn ::Empty)
(prn Empty)
The last line gives: "Unable to resolve symbol: Empty in this context", even though when using the direct macro ty, it works. Any way to solve this? If possible without eval it would be much better.
(defmacro empties [& args]
(doseq [arg args]
`(ty ~arg)))
(empties Base Person Animal)
;equivalent to:
;(ty Base)
;(ty Person)
;(ty Animal)
This is wrong. Your empties call means that the macro expansion function for empties gets as arguments the symbols Base, Person, and Animal. It then evaluates the ty macro call for each, but does not return anything, as doseq always returns nil. So, the expanded code from that empties call is nil. You need to return a single form from your macro function. You should wrap the multiple forms into a do, and actually return all the subforms to that:
(defmacro empties [& args]
`(do ~#(map (fn [arg]
`(ty ~arg))
args)))
FWIW, I prefer to write #Svante's solution as
(defmacro empties [& args]
(cons `do
(for [arg args]
`(ty ~arg))))
which is also pretty close to the structure of your doseq approach.