I am a bit puzzled with the behavior of if. The following code works fine:
(if true
(let [x "whatever"]
(println "TRUE 1")
(println "TRUE 2")))
returns:
TRUE 1
TRUE 2
nil
But if the let expression is removed:
(if true
(
(println "TRUE 1")
(println "TRUE 2")))
it returns a NullPointerException as well:
TRUE 1
TRUE 2
NullPointerException user/eval8051 (NO_SOURCE_FILE:4)
I suspect it is due to the println returning a nil. But then why does it work when a let is introduced? Is there a better way to do this?
The key part is this block
((println "TRUE 1")
(println "TRUE 2"))
What this does is evaluate the printlns, turning the expression into this:
(nil nil)
And then, because of the additional parenthesis, it tries to call nil as a function, with nil as the argument. Since nil is not a function, it throws the exception. The reason it worked in the first case was because it had the let to evaluate instead. Since a let will evaluate every expression in its body (and doesn't try to treat the results as a function), it behaves correctly.
If you want to evaluate multiple expressions, you should use do
(if true
(do (println "TRUE 1")
(println "TRUE 2")))
or, since there's no "else" part, you can just use when
(when true
(println "TRUE 1")
(println "TRUE 2"))
The important thing to remember is that in Clojure, unlike in C-style languages, you can't just add parenthesis around expressions without changing the meaning. If you wrap something in parenthesis (without quoting it), it's going to try and evaluate it as a function invocation.
Related
I am in the process of learning Clojure, through "Clojure for the Brave and True".
In macro lessons, I was trying the below macro,
(defmacro report
[to-try]
`(let [result# ~to-try]
(if result#
(println (quote ~to-try) "was successful:" result#)
(println (quote ~to-try) "was not successful:" result#))))
And below are couple of my experiments with the macro and the respective outputs.
1
(map #(report %) ['(= 1 2) '(= 1 1)])
; p1__26622# was successful: (= 1 2)
; p1__26622# was successful: (= 1 1)
2
map #(report %) ['false 'true])
; p1__26612# was not successful: false
; p1__26612# was successful: true
And my questions are,
Why in the former case the macro printed true for both values?
In my understanding the second is exactly equivalent to the former. But why it gives a different result?
Why in the former case the macro printed true for both values?
Your report macro is receiving quoted lists as inputs, not expressions that can be evaluated to true/false. Any list is truthy, even if it contains an expression that would evaluate to false. This would give you the expected result:
(report (= 1 2)) ;; evaluates to false
In my understanding the second is exactly equivalent to the former. But why it gives a different result?
It's not exactly equivalent because the first example is examining lists and the second is examining quoted booleans. Your second example evaluates 'false as false because that's how if treats it:
(if 'false 0 1) => 1
I don't understand how to get the full macro expansion.
With this code
(when true (when true true))
I would like to get the full macro expansion
(if true (do (if true (do true)))
But I can't
I understand macroexpansion-1 will resolve the first level of expansion :
(macroexpand-1 '(when true (when true true)))
(if true (do (when true true)))
But why when I call again macroexpand-1 (that's what should do macroexpand) :
(macroexpand-1 '(if true (do (when true true))))
I got the exact same result ?
(if true (do (when true true)))
I was expecting a full macro expansion.
Does macro expansion only works with top level forms ?
I'm aware of an expand-all function in the clojure.walk namespace, so I
suppose macroexpand doesn't work on nested structures. Am I right ?
You are right.
See also https://clojuredocs.org/clojure.core/macroexpand
Where it states :
Note neither macroexpand-1 nor macroexpand expand macros in subforms.
And indeed macroexpand-all does the recursive expansion :
> (clojure.walk/macroexpand-all '(when true (when true true)))
(if true (do (if true (do true))))
See also https://clojuredocs.org/clojure.walk/macroexpand-all
where it states :
Recursively performs all possible macroexpansions in form.
Your example
(macroexpand-1 '(if true (do (when true true))))
might confuse you, but it does as the docs tell :
(macroexpand-1 form) If form represents a macro form, returns its
expansion, else returns form.
So since 'if' is not a macro, it just returns if, without going into subforms...
Why do the following statements return different results? And further, how would one write the second statement to receive the expected result of false?
(clojure.core/and false true)
=> false
((resolve 'clojure.core/and) false true)
=> true
The kind folks at #clojure on freenode helped me with an answer.
First, one should try to avoid resolving macros at run-time.
Second, the macro function is implemented as a function that takes in two parameters, besides of the any (&) args. Hence, the correct way to write the second statement above would be
((resolve 'clojure.core/and) nil nil false true) =>
**(clojure.core/let [and__3973__auto__ false] (if and__3973__auto__ (clojure.core/and true) and__3973__auto__))**
Since we are still using a macro, it simply will expand it to code, instead of returning an actual value.
The reason AND is implemented as a macro, is to make short-circuiting possible.
You can see from the REPL:
(defmacro and
"Evaluates exprs one at a time, from left to right. If a form
returns logical false (nil or false), and returns that value and
doesn't evaluate any of the other expressions, otherwise it returns
the value of the last expr. (and) returns true."
{:added "1.0"}
([] true)
([x] x)
([x & next]
`(let [and# ~x]
(if and# (and ~#next) and#))))
Without the macro, an AND function would evaluate all of the predicate given to it without short-circuiting.
In my particular case, this is exactly what I needed; both for AND and OR non short-circuiting functions.
Here follows both functions in case anyone ever needs them:
(defn and* [& xs] (every? identity xs))
(defn or* [& xs] (not= (some true? xs) nil))
Why does it happen that after I execute defmulti within a when-not, the previously unresolved symbol resolves fine, but not bound to the value?
user=> (resolve 'buux)
nil
user=> (when-not (resolve 'buux) (defmulti buux class))
nil
user=> (resolve 'buux)
#'user/buux
user=> (bound? #'buux)
false
user=> (defmulti buux class)
#'user/buux
user=> (bound? #'buux)
true
defmulti will be expanded with a let-block that uses def to define the symbol. As a matter of fact, the expression returned by defmulti will not be evaluated, but it will be generated as form using let, Thus, the object becomes defined globally. This results in your test-condition (for when not) to succeed after the var has been defined, before the multi-fn was created and the root binding of the var was affected. Your defmulti block was never executed (also the when-not expression returned nil), but expanded.
Further explanation:
Here you can see how that happens:
(macroexpand '(defmulti buxx class))
Now you can see the form that the macro call will generate:
(clojure.pprint/write (macroexpand '(defmulti buxx class))
:with-dispatch clojure.pprint/code-dispatch)
=>
(let*
[v__4080__auto__ (def buxx)]
(clojure.core/when-not
(clojure.core/and
(.hasRoot v__4080__auto__)
(clojure.core/instance? clojure.lang.MultiFn #v__4080__auto__))
...
This results in (def buux) being expanded. If you evaluate (def buux) in your repl you can make the same tests.
From the docstring of def:
def yields the var itself (not its value).
This means, when being expanded, it is being replaced with a (possibly unbound) var.
So when being expanded, def always creates a var but the optional form that returns the new value (for the var) will be only evaluated when the expanded def is evaluated. Macros and special forms will be expanded before they are actually evaluated. E. g. testing with
(defmacro i-have-side-effects
[]
(println "I was invoked!")
42)
(when-not true
(println (i-have-side-effects)))
=>
#'user/i-have-side-effects
I was invoked!
nil
So probably you should not define a multi-method conditionally anyway.
Why does the following Clojure program throw a NullPointerException?
user=> (defn x []
"Do two things if the expression is true."
(if true ((println "first expr") (println "second expr")) false))
user=> (x)
first expr
java.lang.NullPointerException (NO_SOURCE_FILE:0)
second expr
This is a simplified version of my actual use case, where I want to execute maybe three statements (pull values from the DB) before returning a map - {:status 200, :body "Hello World"} inside of the branch.
It is trying to treat the result of the first println as a function to call on the second println function.
You need a do.
(defn x []
"Do two things if the expression is true."
(if true (do (println "first expr") (println "second expr")) false))
(x)
The do special form (progn in CL, begin in Scheme) executes each of its arguments in sequence and returns the result of the last one.
If nil is ok as a return value in the else case, consider using when which has an implicit do block:
(defn x []
"Do two things if the expression is true."
(when true
(println "first expr")
(println "second expr")))
Not that it matters in your particular case, but do know the difference between (do ...) which will load each form in its own classloader, and an empty let form (let [] ...) which evaluates the whole form in a single classloader.