I was browsing the source of clojure.core:
(defmacro and
([] true)
([x] x)
([x & next]
`(let [and# ~x]
(if and# (and ~#next) and#))))
Why not just
(defmacro and
([] true)
([x] x)
([x & next]
`(if ~x (and ~#next) ~x)))
Because the argument x is a form, which might be any expression, that can be evaluated twice in (if ~x (and ~#next) ~x)). The letevaluates it once and uses the value x# thereafter.
So the actual code avoids the effects - the side effects in particular - of evaluating the arguments twice.
#amalloy points out that the second instance of form x in (if ~x (and ~#next) ~x)) is evaluated only if the first instance evaluates false(y).
Related
Currently I have some code like this:
(defn compute-issue [some args] (or (age-issue some args) (name-issue some args)))
More issue types are coming.
Is there something like this:
(defn compute-issue [some args] (first-not-nil [age-issue name-issue] some args))
; Where first-not-nil would be something like
(defn first-not-nil [fs & args]
(if (empty? fs)
nil
(let [result (apply (first fs) args)]
(if (nil? result)
(recur (rest fs) args)
result))))
I'm new to Clojure. Am I reinventing an existing function?
There is a similar function some-fn in clojure.core:
Takes a set of predicates and returns a function f that returns the first logical true value
returned by one of its composing predicates against any of its arguments, else it returns
logical false. Note that f is short-circuiting in that it will stop execution on the first
argument that triggers a logical true result against the original predicates.
The key differences are some-fn returns another function for the actual function application, and that function will also discard false results, which it sounds like you may not want. This is another simple way to phrase it:
(defn first-not-nil [fs & args]
(first
(for [f fs
:let [r (apply f args)]
:when (some? r)]
r)))
In the same way you can convert
(if (something)
something
fallback)
into the shorter version :
(or something fallback)
I was wondering if there is a composition function/elegant builtin for threading a value into the validation of a predicate like :
(when (pred x)
x)
Into something like
(thread-pred pred x)
I could easily build some function or macro to do it, but I'd rather not go NIH here.
It took me way longer than I care to admit - but here you go:
Beware this might explode.
(defmacro -pred->
[pred body]
`(when (~pred ~body) ~body))
(macroexpand-1 '(-pred-> odd? 3))
=> (clojure.core/when (odd? 3) 3)
(-pred-> odd? 3)
=> 3
(-pred-> odd? 2)
=> nil
EDIT: as the comments correctly pointed out it would be wiser to let-bind the body to an autogensym like so:
(defmacro -pred->
[pred body]
`(let [b# ~body]
(when (~pred b#) b#)))
I was trying to implement xor macro and came up with a problem.
I couldn't use private function in a macro.
Here is the example:
private function
(defn :^private xor-result
[x y]
(if (and x y)
false
(or x y)))
macro
(defmacro xor
([] true)
([x] x)
([x & next]
`(let [first# ~x
second# ~(first next)]
(if (= (count '~next) 1)
(xor-result first# second#)
(xor (xor-result first# second#) ~#(rest next))))))
Here is the Error:
CompilerException java.lang.IllegalStateException: var: #'kezban.core/xor-result is not public
Problem solves when I remove ^:private flag.
Question is: What is the reason of this behaviour?
UPDATE: I can use private function with the following approach.
private function
(defn ^:private xor-result
[x y]
(if (and x y)
false
(or x y)))
new macro
(defmacro xor
([] true)
([x] x)
([x & next]
(let [first x
second `(first '(~#next))
result (xor-result (eval first) (eval second))]
`(if (= (count '~next) 1)
~result
(xor ~result ~#(rest next))))))
If you have a macro in ns1:
(ns ns1)
(defn- my-fun [x] (first x))
(defmacro my-macro [x] (my-fun ~x))
And use it in another namespace:
(ns ns2
(:require [ns1 :refer [my-macro]]))
(my-macro [1 2])
The compiler will call the macro during compilation phase and it will generate code in ns2 namespace and will become:
(ns ns2
(:require [ns1 :refer [my-macro]]))
(ns1/my-fun [1 2])
and this code will be eventually compiled to byte code.
As you can see the compiler will see usage of a ns1's private function in ns2 namespace and will complain about it.
To debug your macros you can use macroexpand to see the result of applying your macro.
You also need to remember that your macros work on your program data: datastructures representing your code (symbols, lists, vectors etc.). For example in your second version of the macro it works symbols as they are, not runtime values bound to them:
(macroexpand '(xor true false))
;; => (if (clojure.core/= (clojure.core/count (quote (false))) 1) true (boot.user/xor true))
(macroexpand '(xor (zero? 1) (zero? 0)))
;; => (if (clojure.core/= (clojure.core/count (quote ((zero? 0)))) 1) false (boot.user/xor false))
As you can see your xor-result function won't be called with the actual runtime values but rather with the data representing your code. xor-result is called in your macro directly during compile time. In the first version of your macro it is used inside of the code generated by the macro and is not called during compilation.
There's a hack you can use if you really do want to access private vars from within a public macro that will be used by other namespaces.
When you resolve the value of a var by referring to it in your code, Clojure checks whether the var is public or private and the compiler will complain if you attempt to access a private var. However you can refer explicitly to the var itself (rather than its value) using the #' syntax, and Clojure will allow this kind of reference even to a private var. You should use a fully-qualified name (use the full namespace name) so that you don't require any particular namespace alias to exist.
So, assuming that the function xor-result lives in a namespace called mynamespace.core, you would invoke the function like:
(#'mynamespace.core/xor-result first# second#)
If I do this:
(eval (let [f (fn [& _] 10)]
`(~f nil)))
It returns 10 as expected.
Although if I do this:
(eval (let [f (constantly 10)]
`(~f nil)))
It throws an exception:
IllegalArgumentException No matching ctor found for
class clojure.core$constantly$fn__... clojure.lang.Reflector.invokeConstructor
Since both are equivalent why is the code with constantly not working?
This question has two answers, to really get it.
First, to clarify why your eval form is not giving you the expected result in the second case, note that f has been assigned to be equal to the function (fn [& _] 10). This means when that form is evaluated, the function object is again evaluated--probably not what you had in mind.
tl;dr: f is evaluted when it is bound, and again (with ill-defined results) when the form you create is eval'd.
The reason why one (the anonymous function) works, while the other fails means we have to look at some of the internals of the evaluation process.
When Clojure evaluates an object expression (like the expression formed by a function object), it uses the following method, in clojure.lang.Compiler$ObjExpr
public Object eval() {
if(isDeftype())
return null;
try
{
return getCompiledClass().newInstance();
}
catch(Exception e)
{
throw Util.sneakyThrow(e);
}
}
Try this at the REPL:
Start with an anonymous function:
user=> (fn [& _] 10)
#<user$eval141$fn__142 user$eval141$fn__142#2b2a5dd1>
user=> (.getClass *1)
user$eval141$fn__142
user=> (.newInstance *1)
#<user$eval141$fn__142 user$eval141$fn__142#ee7a10e> ; different instance
user=> (*1)
10
Note that newInstance on Class calls that class' nullary constructor -- one that takes no arguments.
Now try a function that closes over some values
user=> (let [x 10] #(+ x 1))
#<user$eval151$fn__152 user$eval151$fn__152#3a565388>
user=> (.getClass *1)
user$eval151$fn__152
user=> (.newInstance *1)
InstantiationException user$eval151$fn__152 [...]
Since the upvalues of a closure are set at construction, this kind of function class has no nullary constructor, and making a new one with no context fails.
Finally, look at the source of constantly
user=> (source constantly)
(defn constantly
"Returns a function that takes any number of arguments and returns x."
{:added "1.0"
:static true}
[x] (fn [& args] x))
The function returned by constantly closes over x so the compiler won't be able to eval this kind of function.
tl;dr (again): Functions with no upvalues can be evaluated in this way and produce a new instance of the same function. Functions with upvalues can't be evaluated like this.
Say I want to make a Clojure macro that does the following:
If x is a list calling the function "bar"
return :foobar
else
return x as a string
However, bar is not defined; rather, it is only used internally in the macro, like so:
(foo (bar))
:foobar
(foo 1)
"1"
One could do something like this:
(defmacro foo [x]
(if (and (coll? x) (= (first x) 'bar))
:foobar
(str x)))
This works great for the (bar) case, as well as for literals. However, symbols do not work as intended, giving the symbol name instead of its associated value:
user=> (def y 2)
#'user/y
user=> (foo y)
"y"
One could call the eval function on x before passing it to str, but this causes problem when using the function in let:
user=> (let [a 3 b (foo a)] b)
java.lang.UnsupportedOperationException: Can't eval locals (NO_SOURCE_FILE:89)
Presumably, the problem has to do with symbol resolution, so maybe we try to work something out with syntax-quote:
(defmacro foo [x]
`(if (and (coll? '~x) (= (first '~x) '~'bar))
:foobar
(str ~x)))
Now, the problem is with (foo (bar)), as this expands the else clause to (clojure.core/str (bar)), which throws an exception, as bar is not defined. I then tried doing some shenanigans with eval:
(defmacro foo [x]
`(if (and (coll? '~x) (= (first '~x) '~'bar))
:foobar
(eval '(str ~x))))
But this doesn't work with let bindings again:
user=> (let [a 1 b (foo a)] b)
java.lang.Exception: Unable to resolve symbol: a in this context (NO_SOURCE_FILE:153)
So I'm really at a loss here. It seems as though fixing one problem breaks another. Is there a better, simpler way of making this macro such that it works in the following cases:
In let bindings
With (bar)
With symbols
P.S. If anybody is curious as to why I want to do this, I'm working on a DSL for Yahoo's YQL service and I want to be able to do things like (select (table :t) ...), but I need to be able to pass in symbols, as well as literals.
I believe this should work.
(defmacro foo [x]
(if (and (coll? x) (= (first x) 'bar))
:foobar
`(str ~x)))