In an attempt to better learn macros I've been playing around with a few simple examples including recreating a simplified thread-last. I'm having trouble understanding why one version below results in a stack-overflow and the other doesn't.
;; version one - blows up w/ stack overflow
(defmacro ->>> [e1 & exprs]
`(if ~exprs
(->>> ~(concat (first exprs) (list e1)) ~#(rest exprs))
~e1))
;; version two, works just fine
(defmacro ->>> [e1 & exprs]
(if exprs
`(->>> ~(concat (first exprs) (list e1)) ~#(rest exprs))
e1))
My initial reaction was that it must be due to the fact that in the first example, although the resulting expansion looks as if it would run just fine if it were normal code, since it's a macro the recursive call continually expands and the if test never happens. In the second version, the if test happens before any list is returned for run-time evaluation giving a chance to break out.
However, I'm not sure if this mental model is correct because the following example (Clojure Brave & True) looks rather similar to the first version above, and works fine:
(defmacro our-and
([] true)
([x] x)
([x & next]
`(if ~x (our-and ~#next) ~x)))
EDIT: To clarify I mean that the above our-and is similar structurally (not semantically) in that it returns a list which contains the recursive call to the macro, similar to version one of my thread-last replica above.
Your mental model is correct. It might help to think of a macro as a function that takes code and returns code, and is run at compile-time. This should clear up the differences between the first example and our-and.
In the first example, we have a function that takes code and always return code that uses the ->>> macro, resulting in infinite macro expansion. Remember, the if expression in that quoted code will evaluate at run time, but you're getting the stack overflow at compile time when macro evaluation occurs.
In our-and, we have a function that has three clauses. In two of the clauses, evaluated first, it returns code that doesn't contain itself. In the third clause, it returns code containing itself. This makes it similar to example 2, not example 1.
Please also see this answer for more details on writing Clojure macros.
Sometimes it is easier to start with a plain function and vectors of data. Here is an example:
(ns tst.demo.core
(:use tupelo.core demo.core tupelo.test))
(defn ->>>
[val & exprs]
(spyx val)
(spyx exprs)
(if (empty? exprs)
val
(let [[expr & others] exprs
>> (spyx expr)
>> (spyx others)
[fn & args] expr
>> (spyx fn)
>> (spyx args)
fncall (concat [fn val] args)
>> (spyx fncall)
result (concat ['->>> fncall] others)]
(spyx result) )))
with output:
val => :val
exprs => ([:form1 1 2 3] [:form2 4 5])
expr => [:form1 1 2 3]
others => ([:form2 4 5])
fn => :form1
args => (1 2 3)
fncall => (:form1 :val 1 2 3)
result => (->>> (:form1 :val 1 2 3) [:form2 4 5])
(->>> :val [:form1 1 2 3] [:form2 4 5])
=> (->>> (:form1 :val 1 2 3) [:form2 4 5])
So you can see it threaded the :val into the right spot (thread-first style) and is set up for the recursive call. Getting closer to a macro, we make a helper fn:
(defn my-thread-first-impl
[val & exprs]
(spyx val)
(spyx exprs)
(if (empty? exprs)
val
(let [[expr & others] exprs
>> (spyx expr)
>> (spyx others)
[fn & args] expr
>> (spyx fn)
>> (spyx args)
fncall (concat [fn val] args)
>> (spyx fncall)
result `(my-thread-first-impl ~fncall ~#others)]
result)))
; (defmacro my-> [forms] )
(dotest
(spyx (my-thread-first-impl :val
'(fn-1 1 2 3)
'(fn-2 4 5) ))
val => :val
exprs => ((fn-1 1 2 3) (fn-2 4 5))
expr => (fn-1 1 2 3)
others => ((fn-2 4 5))
fn => fn-1
args => (1 2 3)
fncall => (fn-1 :val 1 2 3)
=> (tst.demo.core/my-thread-first-impl (fn-1 :val 1 2 3) (fn-2 4 5))
Final version with macro & dummy fn's
(defn fn-1 [& args]
(vec (cons :fn-1 args)))
(defn fn-2 [& args]
(vec (cons :fn-2 args)))
(defn my-thread-first-impl
[val & exprs]
(spyx val)
(spyx exprs)
(if (empty? exprs)
val
(let [[expr & others] exprs
>> (spyx expr)
>> (spyx others)
[fn & args] expr
>> (spyx fn)
>> (spyx args)
fncall (concat [fn val] args)
>> (spyx fncall)
result `(my-> ~fncall ~#others)]
result)))
(defmacro my->
[& forms]
(apply my-thread-first-impl forms))
& result:
(my-> :val
(fn-1 1 2 3)
(fn-2 4 5))
=> [:fn-2 [:fn-1 :val 1 2 3] 4 5]
Related
I'm running into some limitations of Clojure macros. I wonder how to optimize the following code?
(defmacro ssplit-7-inefficient [x]
(let [t 7]
;; Duplicated computation here!
`(do [(first (split-with #(not (= '~t %)) '~x))
(drop 1 (second (split-with #(not (= '~t %)) '~x)))])))
(ssplit-7-inefficient (foo 7 bar baz))
;; Returns: [(foo) (bar baz)]
Here are some approaches that don't work:
(defmacro ssplit-7-fails [x]
(let [t 7]
`(do ((fn [[a b]] [a (drop 1 b)]) (split-with #(not (= '~t %)) '~x)))))
(ssplit-7-fails (foo 7 bar baz))
;; Error: Call to clojure.core/fn did not conform to spec.
(defmacro ssplit-7-fails-again [x]
(let [t 7]
`(do
(let [data (split-with #(not (= '~t %)) '~x)]
((fn [[a b]] [a (drop 1 b)]) data)))))
(ssplit-7-fails-again (foo 7 bar baz))
;; Error: Call to clojure.core/let did not conform to spec.
Note that split-with splits only once. You can use some destructuring to get what you want:
(defmacro split-by-7 [arg]
`((fn [[x# [_# & z#]]] [x# z#]) (split-with (complement #{7}) '~arg)))
(split-by-7 (foo 7 bar baz))
=> [(foo) (bar baz)]
In other use cases, partition-by can be also useful:
(defmacro split-by-7 [arg]
`(->> (partition-by #{7} '~arg)
(remove #{[7]})))
(split-by-7 (foo 7 bar baz))
=> ((foo) (bar baz))
It is not so easy to reason about macros in Clojure - (in my view macroexpand-1 alienates the code a lot - in contrast to Common Lisp's macroexpand-1 ...).
My way was first to build a helper function.
(defn %split-7 [x]
(let [y 7]
(let [[a b] (split-with #(not= y %) x)]
[a (drop 1 b)])))
This function uses destructuring so that the split-with is "efficient".
It does nearly exactly what the macro should do. Just that one has to quote
the argument - so that it works.
(%split-7 '(a 7 b c))
;;=> [(a) (b c)]
From this step to the macro is not difficult.
The macro should just automatically quote the argument when inserting into the helper function's call.
(defmacro split-7 [x]
`(%split-7 '~x))
So that we can call:
(split-7 (a 7 b c))
;; => [(a) (b c)]
Using this trick, even generalize the function to:
(defn %split-by [x y]able like this
(let [[a b] (split-with #(not= y %) x)]
[a (drop 1 b)]))
(defmacro split-by [x y]
`(%split-by '~x ~y))
(split-by (a 7 b c) 7)
;; => [(a) (b c)]
(split-by (a 7 b c 9 d e) 9)
;; => [(a 7 b c) (d e)]
The use of (helper) functions in the macro body - and even other macros - or recursive functions or recursive macros - macros which call other macros - shows how powerful lisp macros are. Because it shows that you can use the entirety of lisp when formulating/defining macros. Something what most language's macros usually aren't able to do.
I have been writing a fairly simple piece of code to get the hang of Clojure and I've run into an issue where when I pass each line into a REPL in order (while using a test case to substitute the values that would be passed as part of the function), I get the expected result but when I try to compile it as a function I get the error Execution error (ClassCastException) at testenv.core/sum-of-two-largest-squares (core.clj:14). class clojure.lang.PersistentList cannot be cast to class clojure.lang.IFn (clojure.lang.PersistentList and clojure.lang.IFn are in unnamed module of loader 'app')
The relevant function is as follows (note that I've moved each step into variables in order to figure out the problem)
(defn sum-of-two-largest-squares [a b c]
(
(def x (into () (map math/abs [a b c])))
(def m (apply min x))
(def y (into () (map square (remove (fn [n] (= n m)) x))))
(apply + y)
)
)
You can't just put parenthesis around things without expecting it to change the meaning.
What works when run in a REPL is:
(defn abs [n] (java.lang.Math/abs n))
(defn square [n] (* n n))
(def x (into () (map abs [a b c])))
(def m (apply min x))
(def y (into () (map square (remove (fn [n] (= n m)) x))))
(apply + y)
...and strictly, this still works if injected into a function, as long as you take out the extra parenthesis (though it works slowly and with unwanted side effects due to the inappropriate use of def):
(defn sum-of-two-largest-squares [a b c]
(def x (into () (map abs [a b c])))
(def m (apply min x))
(def y (into () (map square (remove (fn [n] (= n m)) x))))
(apply + y)
)
(sum-of-two-largest-squares a b c)
However, a good-practice alternative would use let instead:
(defn abs [n] (java.lang.Math/abs n))
(defn square [n] (* n n))
(defn sum-of-two-largest-squares [a b c]
(let [x (into () (map abs [a b c]))
m (apply min x)
y (into () (map square (remove (fn [n] (= n m)) x)))]
(apply + y)))
Here is a more typical solution to this problem
(ns tst.demo.core
(:use tupelo.core tupelo.test))
(defn square [x] (* x x))
(defn sum-of-two-largest-squares [a b c]
(let-spy
[sorted (sort [a b c])
largest-two (rest sorted)
squares (mapv square largest-two)
result (apply + squares)]
result))
(dotest
(is= 41 (spyx (sum-of-two-largest-squares 3 4 5)))
)
with result:
-----------------------------------
Clojure 1.10.3 Java 15.0.2
-----------------------------------
Testing tst.demo.core
sorted => (3 4 5)
largest-two => (4 5)
squares => [16 25]
result => 41
(sum-of-two-largest-squares 3 4 5) => 41
Ran 2 tests containing 0 assertions.
0 failures, 0 errors.
It uses my favorite template project. Just change let-spy back to let once finished writing/debugging.
I am trying to make a guess the number game in clojure but I keep getting an error saying I can only recur from tail position
(def n (rand-int 100))
(prn n)
(println "You have 10 guesses :D")
(println "HINT: My number is between 1 and 100")
(dotimes [i 10]
(def guess (read-line))
(if (= guess str(n))
(recur (println "Correct!") (println "Incorrect"))))
(I am new to clojure)
dotimes is used to execute the body for sideeffects that exact amount given; there is no means to break - except throwing
loop (or functions) are recur targets. Next you would have to count down the attempts so you can stop, if the user did not guess it:
(loop [attempts 10]
; ...
(recur (dec attempts)))
There are also other problematic things:
Don't def inside other forms. Use let instead.
str(n) will throw, as it will try to call n (ClassCastException java.lang.Long cannot be cast to clojure.lang.IFn)
recuring with println looks fishy, since println returns always nil
How do you end dotimes? You don't. Try using loop instead. There are a lot of issues with your code but that's a start.
though this is discouraged and counterclojurish to even think of short circuiting the execution this way, it is still totally possible with macros (purely for education and fun)
(defmacro return [& x]
`(list '~'return (do ~#x)))
(defmacro dotimes+ [[i n] & body]
`(loop [~i 0 res# nil]
(cond (and (list? res#) (= '~'return (first res#))) (second res#)
(< ~i ~n) (recur (inc ~i) (do ~#body))
:else res#)))
can be used like this:
user> (dotimes+ [i 10]
(println i)
(if (== i 5) (return :short-circuited)))
;; 0
;; 1
;; 2
;; 3
;; 4
;; 5
:short-circuited
user> (dotimes+ [i 10]
(println i)
(if (== i 5) (return)))
;; 0
;; 1
;; 2
;; 3
;; 4
;; 5
nil
user> (dotimes+ [i 10]
(println i))
;; 0
;; 1
;; 2
;; 3
;; 4
;; 5
;; 6
;; 7
;; 8
;; 9
nil
notice, that it still expects the return macro to be called in tail position (similar to recur in loop macro)
(dotimes+ [x 4]
(println "attempt" (inc x))
(let [answer (read-line)]
(println "answer is:" answer)
(if (= answer "yes")
(return "YEAH!!!")
(println "WRONG!"))))
I'm writing a macro to allow pass the clauses as a parameter to functions:
(defmacro parse-cmd [command & body]
(let [parts (str/split command #" ")
cmd (first parts)
args (into [] (rest parts))
clauses (partition 2 2 body)]
`(case ~cmd
~#(mapcat (fn [c] [(nth c 0) `(apply ~(nth c 1) ~args)]) clauses))))
(defn mysum [a b]
(+ (Integer. a) (Integer. b)))
(parse-cmd "SET 1 1" "SET" "GET" println)
2
This works well when cmd is a string, however with a var:
(def cmd "SET 1 1")
(parse-cmd cmd "SET" "GET" println)
I get ClassCastException clojure.lang.Symbol cannot be cast to java.lang.CharSequenceq clojure.string/split (string.clj:222)
I guess I should prevent the evaluation of the let too, but I can't make it work:
(defmacro parse-cmd [command & body]
`(let [parts# (str/split ~command #" ")
cmd# (first parts#)
args# (into [] (rest parts#))
clauses# (partition 2 2 ~body)]
(case cmd#
(mapcat (fn [c#] [(nth c# 0) `(apply ~(nth c# 1) args#)]) clauses#))))
With this definition I get:
ClassCastException java.lang.String cannot be cast to clojure.lang.IFn kvstore.replication/eval12098 (form-init7453673077215360561.clj:1)
let's macroexpand this (for your second macro)
(parse-cmd "SET 1 1" "SET" mysum "GET" println)
it expands to:
(let [parts__31433__auto__ (str/split "SET 1 1" #" ")
cmd__31434__auto__ (first parts__31433__auto__)
args__31435__auto__ (into [] (rest parts__31433__auto__))
clauses__31436__auto__ (partition
2
2
("SET" mysum "GET" println))]
(case
cmd__31434__auto__
(mapcat
(fn [c__31437__auto__] [(nth c__31437__auto__ 0)
(seq
(concat
(list 'apply)
(list (nth c__31437__auto__ 1))
(list 'args__31432__auto__)))])
clauses__31436__auto__)))
there are two problems here:
1) you generate this code: ("SET" mysum "GET" println), which obviously causes your exception, because "SET" is not a function
2) you generate the wrong case expression, I see that you have forgotten to unquote-splice your mapcat
Let's try to fix this:
first of all unquote mapcat; then you can move clauses out of your generated let, because it can be totally done at compile-time:
(defmacro parse-cmd [command & body]
(let [clauses (partition 2 2 body)]
`(let [parts# (str/split ~command #" ")
cmd# (first parts#)
args# (into [] (rest parts#))]
(case cmd#
~#(mapcat (fn [c] [(nth c 0) `(apply ~(nth c 1) args#)]) clauses)))))
now let's check the expansion:
(let [parts__31653__auto__ (str/split "SET 1 1" #" ")
cmd__31654__auto__ (first parts__31653__auto__)
args__31655__auto__ (into [] (rest parts__31653__auto__))]
(case
cmd__31654__auto__
"SET"
(apply mysum args__31652__auto__)
"GET"
(apply println args__31652__auto__)))
ok. looks better. let's try to run it:
(parse-cmd "SET 1 1" "SET" mysum "GET" println)
we have another error now:
CompilerException java.lang.RuntimeException: Unable to resolve symbol: args__31652__auto__ in this context, compiling:(*cider-repl ttask*:2893:12)
so expansion also shows us this:
args__31655__auto__ (into [] (rest parts__31653__auto__))
...
(apply mysum args__31652__auto__)
so there are different symbols for args# here. That's because the scope of the generated symbol name is one syntax-quote. So inner syntax-quote with apply generates the new one. You should use gensym to fix that:
(defmacro parse-cmd [command & body]
(let [clauses (partition 2 2 body)
args-sym (gensym "args")]
`(let [parts# (str/split ~command #" ")
cmd# (first parts#)
~args-sym (into [] (rest parts#))]
(case cmd#
~#(mapcat (fn [c] [(nth c 0) `(apply ~(nth c 1) ~args-sym)]) clauses)))))
ok now it should work properly:
ttask.core> (parse-cmd "SET 1 1" "SET" mysum "GET" println)
2
ttask.core> (parse-cmd cmd "SET" mysum "GET" println)
2
great!
I would also recommend you to use destructuring in a mapcat function and quoted let, to make it more readable:
(defmacro parse-cmd [command & body]
(let [clauses (partition 2 2 body)
args-sym (gensym "args")]
`(let [[cmd# & ~args-sym] (str/split ~command #" ")]
(case cmd#
~#(mapcat (fn [[op fun]] [op `(apply ~fun ~args-sym)]) clauses)))))
But if it's not just an exercise in writing macros, you shouldn't use the macro for that, since you pass just string and function references here, so anyway you shall evaluate everything in runtime.
(defn parse-cmd-1 [command & body]
(let [[cmd & args] (str/split command #" ")
commands-map (apply hash-map body)]
(apply (commands-map cmd) args)))
I was trying to do something like below,
val = initValue;
if (test1(val)) { val = fn1(val); }
if (test2(val)) { val = fn2(val); }
return val;
The only way I found within clojure core was using cond->. I was hoping I should be able to do this
(cond-> initValue
test1 fn1
test2 fn2)
However, the condition in cond-> is not a function. It doesn't seem to allow me to pass the result of fn1 to test2.
What's the idiomatic way to do this?
Okay. Well you have a data dependency between the function results and your predicates, so the "nicest" thing I came up using only clojure.core is to compose as-> and cond->
(as-> initValue data
(cond-> data (test1 data) (f1 data))
(cond-> data (test2 data) (f2 data)))
Another approach would be my own update-when helper...
(defn ->when-update
"Function of a value, a predicate, an updater and optional
varargs. If the predicate is true of the value, returns (apply f x
args), otherwise returns x.
Example:
(-> 1 (->when-update #(<= 0 %) inc))"
[x pred f & args]
(if (pred x)
(apply f x args)
x))
which lets us write
(-> initValue
(->when-update test1 f1)
(->when-update test2 f2))
Will this do?
(defmacro cond-chain [init & stuff]
(let [pairs (partition 2 stuff)
step-form (fn [[test func]]
`((fn [x#] (if (~test x#) (~func x#) x#))))]
(list* '->> init (map step-form pairs))))
For example,
(cond-chain 7, even? inc, odd? #(* 2 %))
;14
(cond-chain 7, odd? inc, odd? #(* 2 %))
;8
(cond-chain 7, zero? inc, even? #(* 2 %))
;7
As you can see, it constructs a form that conditionally applies each of a series of functions.
Or, without resorting to macros:
(defn cond-chain [init & stuff]
(let [pairs (partition 2 stuff)]
(reduce (fn [acc [test func]] (if (test acc) (func acc) acc)) init pairs)))