How this Clojure macro expansion works? - clojure

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

Related

How does a macro work if it returns a sequence instead of a list?

I came across this macro definition for unless from the brave and true book
(defmacro unless
"Inverted 'if'"
[test & branches]
(conj (reverse branches) test 'if))
I believe the rest param is a sequence, and the conj returns a sequence, so this entire macro returns a sequence. I thought you needed to return a list for the return to be evaluated properly
On further investigation, why does (eval (sequence [+ 1 4 4])) do the same thing as (eval (list 1 4 4)). Where does it say that sequences are evaluated like lists? I don't see that in the docs. –
You have just proven that a list, a seq, and a sequence are all treated as function call sites by the Clojure compiler. That is your answer.
=> (def xxx [+ 2 3]) ; a function and 2 integers in a vector
=> (eval xxx) ; a vector doesn't work
[#object[clojure.core$_PLUS_ 0x375dfecb "clojure.core$_PLUS_#375dfecb"] 2 3]
=> (eval (seq xxx)) ; a seq works
5
=> (eval (sequence xxx)) ; a sequence works
5
=> (eval (apply list xxx)) ; converts xxx to a list (+ 2 3) which works
5
When the Clojure docs are ambiguous or are missing some detail, a small experiment like the above will answer your question.
If the answer applies to a specific area of the Clojure website, function docstring, or clojuredocs.org or clojure-doc.org, you may consider submitting a pull-request to update & improve the docs.

Alternative Clojure Threading Macro

I'm having a little trouble with a threading macro I'm trying to write. I've posted a stripped down version to show what problem I'm having.
(defmacro <->
[v & fs]
`(do
(-> ~v ~#fs)
~v))
The macro is equivalent to the thread first -> macro, but instead of returning the result of the threading operation, it returns the original value it was passed.
The trouble, I'm having is that when I do something like:
(<-> 1
(<-> println)
(<-> println))
I would expect the output to be
1
1
=> 1
but because the macro evaluates outside in, the macroexpand looks like:
(do
(do
(println
(do
(println 1)
1))
(do
(println 1)
1)) 1)
and the result is
1
1
1
=> 1
I can see why this is happening since the macro is evaluated from outside in, but I'm not sure how to fix it so the macro actually works as expected (i.e. evaluating the value v before threading it to the next form).
Your macro expands to a form in which its v argument is evaluated twice. You need to evaluate v only once, and let-bind the result so you can refer to that value later.
(defmacro <-> [v & fs]
(let [$v (gensym "$v_")]
`(let [~$v ~v]
(-> ~$v ~#fs)
~$v)))
Note the use of gensym to generate a fresh symbol.

Output with println in clojure on Hackerrank

Hi I am starting to write clojure code and practicing Hackerrank questions.
The problem requires me to take input as
2
RGRG
BGYG
where 2 is number of test cases followed by 2 strings.
I have written following code to take input and print the output of it where fullballs? is my function :
(defn Start [FuncToCall inputParse outputParse]
(let [lines (line-seq (java.io.BufferedReader. *in*))
input (rest lines)
times (first lines)]
(for [i (range (Integer. times))]
(outputParse (FuncToCall (inputParse (nth input i)))))
))
(Start fullballs?
(fn [x] x)
(fn [x]
(if x
(println "True")
(println "False"))
x))
However, Hackerrank says that nothing gets printed on the stdout.
Also when i am trying it int cider repl it is not something like usual
(False
False
false false)
for my two test cases..
Is this problem with for or where is my code wrong ?
for is lazy. This means that unless and until you force evaluation of the result, side effects will not be executed.
The reason this works in your REPL is that it tries to print out the result of your function. This forces evaluation of the lazy sequence produced by for.
Use doseq instead.
For further reading.
I don't understand the second half of your question: "It is not something like usual for my two test cases."

cond in Clojure with thousands of clauses

Running the following code in Clojure gives a StackOverflow Error:
(cond
(= 1 2) 1
(= 2 3) 2
(= 3 4) 3
...
(= 1022 1023) 1022
(= 1023 1024) 1023
:else 1024)
I would like to create a function/macro that can handle a huge number of clauses without creating a stack that overflows.
Please advise as to how I might attempt this.
If you look at the full stack trace, you'll see that cond emits a deeply-nested if structure; the exception then occurs when the compiler tries to parse this structure. The problem might have more to do with simply compiling deeply nested Clojure code than the specific use of cond.
I was able to come up with the following macro that takes a list of clauses, wraps them in thunks to provide the deferred evaluation that you get with if, and then uses some to find the first logical true test expression. Its performance probably isn't as good due to the creation of so many anonymous functions, but it gets around the stack overflow exception.
(defmacro cond' [& clauses]
`(:result
(some (fn [[pred-thunk# val-thunk#]]
(if (pred-thunk#) {:result (val-thunk#)}))
(partition 2 (list ~#(map (fn [c] `(fn [] ~c)) clauses))))))
Note the wrapping and unwrapping of the returned value in a map, to ensure that some correctly handles a value clause that evaluates to nil.
A cond with 513 clauses in unlikely to be used in practice.
Here is a functional implementation of your example.
(or (some identity (map #(if (= %1 %2) %1)
(range 1 1024)
(range 2 1025)))
1024)
The requirement is like this:
Given a list of condition and result mappings, e.g.
[ [cond1 r1] [cond2 r2] ...], where
cond1: (= 1 1),
r1: 1
Find the result - rn - where condn evaluates to true
Solution using some is perfect here in solving the problem, but I think we can avoid using macro.
(defn match [[condition result]]
(when condition result))
(some match [[(= 1 2) 100] [(= 2 3) 200] [(= 3 3) 300]]) ;; => 300

How to make this macro work as expected?

(defmacro switch [choices choice] '(do (choices choice)))
(macroexpand-1 '(switch {1 (print 1) 2 (print 2)} (+ 1 1)))
gives: (do (choices choice))
Just to learn macros, I wanted to emulate switch case where I will give a dictionary having case as keys and code to execute as values.
I wanted (+ 1 1) to get evaluated as 2 and then be used as key to get codde to execute with do.
However the expanded macro gives us code which doesn't resolve choices and choice both.
I tried unquoting choice and choices, doesn't work.
What am I missing here ?
Unquoting does work, but you need to switch from the regular quote to syntax-quote (backtick) for your quoting:
(defmacro switch [choices choice]
`(do (~choices ~choice)))
Note that the do here is unnecessary.
Note that with this version of switch, both print calls in your example will be evaluated, because they will simply appear as value expressions in a map literal in switch's output. To print conditionally, you'll have to use some conditional construct (if, case or something which expands to one of them). I'll also point out that the choices argument must be a literal map, or else the macro won't be able to get at the keys and values (and in any case the values would have been evaluated by the outer context).
Here's a version written with the above in mind:
(defmacro switch [choices choice]
`(case ~choice ~#(apply concat choices)))
Example from the REPL:
user=> (switch {1 (print 1) 2 (print 2)} (+ 1 1))
2nil
(2 is printed by the print call, nil is the return value.)