In clojure, can this ever be true?
(= #'x #'y)
Examples of what doesn't work:
user> (def x 1)
#'user/x
user> (def y x)
#'user/y
user> (= #'x #'y)
false
user> (def y #'x)
#'user/y
user> (= #'x #'y)
false
(refer 'clojure.core :only '[=] :rename '{= equal?})
(= #'= #'equal?)
;= true
Related
Say there is the need to check if an argument passes one truth test of a given predicate collection.
codewise:
(fn [x]
(or (pred1 x) (pred2 x) (pred3 x) (pred4 x)))
due to the implementation of or, this short circuits after the first truthy value. As intended.
How can this be rewritten by using a collection of predicates:
[pred1 pred2 pred3 pred4]
A funky way would be:
(fn [x preds]
(some? ;; nil->false
(some true? (map #(% x) preds))))
It also turns out that this one does not short circuit. Might be due to Clojure's chunking of lazy sequences.
Can we do this better?
clojure has a some-fn function for that:
user> ((some-fn true? false? nil?) true)
true
user> ((some-fn false? nil?) true)
false
or for your case:
user> (defn any-pred? [x preds]
((apply some-fn preds) x))
another classic way is to do it recursively:
user> (defn any-pred? [x preds]
(when-let [[pred & preds] (seq preds)]
(or (pred x) (any-pred? x preds))))
user> (any-pred? true [false?])
nil
user> (any-pred? true [true?])
true
user> (any-pred? true [false? true?])
true
user> (any-pred? true [false? nil?])
nil
user> (any-pred? true [false? nil? true?])
true
I think it's map that's doing the chunking in your solution.
Try
(defn any-true? [preds]
(fn [x]
(loop [preds preds]
(and (seq preds)
(or ((first preds) x)
(recur (rest preds)))))))
((any-true? [odd? even?]) 3) ;true
((any-true? []) 3) ;nil
((any-true? [even?]) 3) ;nil
((any-true? [odd? #(/ % 0)]) 3) ;true
The last example shows that the evaluation is lazy.
When I need short circuit, I use reduce with reduced.
(defn any-valid? [w & pred-fn-coll]
(reduce (fn [v pf]
(if (pf w)
(reduced true)
v)) false pred-fn-coll))
(any-valid? 1 even? odd?)
;=> true
(any-valid? 1 even? even?)
;=> false
Alternatively,
(defn somep? [x [p & ps :as preds]]
(if-not (empty? preds)
(or (p x) (somep? x ps))))
or
(defn somep? [x [p & ps :as preds]]
(if-not (empty? preds)
(let [res (p x)]
(if-not res
(recur x ps)
res))))
I have been writing some Clojure recently, and I found myself using the following pattern frequently enough:
(let [x (bam)
y (boom)]
{:x x
:y y})
So I went ahead and wrote the following macro:
(defmacro make-keyword-map [& syms]
`(hash-map ~#(mapcat (fn [s] [(keyword (name s)) s]) syms)))
With that, code now looks like:
(let [x (bam)
y (boom)]
(make-keyword-map x y)
Would this sort of macro be considered idiomatic? Or am I doing something wrong, and missing some already established pattern to deal with something of this sort?
Note, that you can also replace all of:
(let [x (bam)
y (boom)]
{:x x
:y y})
with just:
{:x (bam)
:y (boom)}
which will evaluate to the same thing.
If your let expressions depend on one another, then how about a macro like so:
(defmacro make-keyword-map [& let-body]
(let [keywords-vals (flatten (map (juxt keyword identity)
(map first (partition 2 let-body))))]
`(let ~(vec let-body)
(hash-map ~#keywords-vals))))
that way (make-keyword-map x (foo 1) y (bar 2) z (zoom x)) expands to:
(clojure.core/let [x (foo 1) y (bar 2) z (zoom x)]
(clojure.core/hash-map :x x :y y :z z))
So something like this would work:
user=> (defn foo [x] (+ x 1))
#'user/foo
user=> (defn bar [x] (* x 2))
#'user/bar
user=> (defn zoom [x] [(* x 100) "zoom!"])
#'user/zoom
user=> (make-keyword-map x (foo 1) y (bar 2) z (zoom x))
{:z [200 "zoom!"], :y 4, :x 2}
Not sure how idiomatic that is, but it also saves you a let, compared to your original example.
When I use if-let like
(if-let [a 2 b nil] (+ a b))
I get an IllegalArgumentException:
clojure.core/if-let requires exactly 2 forms in binding vector...
Similar for when-let...
This is not what I would expect. If-let could try all bindings and break when one fails and evaluate the else expression.
The same complaint can be found in the comments at clojuredocs. I found an answer here which did not really satisfy since the poster seems to have the equivalent of a nested if-let-structure in mind.
What reasons are there to limit the bindings of the *-let macros?
UPDATE:
As it seems to be unclear, what my expectations of if-let are:
It should evaluate all bindings sequentially.
When all succeed, it should evaluate the 'then'-case.
If one binding fails it should immediately break and evaluate the 'else'-case.
In case of failure the bindings, even succeeded ones, should not be available in the 'else'-expression
Try this out:
(defmacro if-let-multi
([bindings then-exp]
(let [values (take-nth 2 (rest bindings))]
`(if (and ~#values) (let ~bindings ~then-exp) false)))
([bindings then-exp else-exp]
(let [values (take-nth 2 (rest bindings))]
`(if (and ~#values) (let ~bindings ~then-exp) ~else-exp))))
Here it is in action:
user> (if-let-multi [a 2 b nil] (+ a b))
false
user> (if-let-multi [a 2 b 3] (+ a b))
5
user> (if-let-multi [a 2 b nil] (+ a b) "NO WAY")
"NO WAY"
if-let and let serve different purposes and if-let is not just a more restricted version of let. of instance if-let differs from let in that the value is bound only for the then clause and not the else.
user> (if-let [ans (+ 1 2 3)] ans :foo)
6
user> (if-let [ans (+ 1 2 3)] ans ans)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: ans in this context, compiling:(NO_SOURCE_PATH:1)
user> (let [ans (+ 1 2 3)] ans ans)
6
if-let is intended to make life easier in the case where you are binding a value simply to test and use it.
Try this out.
(defmacro if-lets
([bindings true-expr] `(if-lets ~bindings ~true-expr nil))
([bindings true-expr false-expr]
(cond
(or (not (seq bindings)) (not (zero? (rem (count bindings) 2))))
`(throw (IllegalArgumentException. "if-lets requires 2 or multiple of 2 forms in binding vector in user:1"))
(seq (drop 2 bindings))
`(if-let ~(vec (take 2 bindings))
(if-lets ~(vec (drop 2 bindings))
~true-expr
~false-expr)
~false-expr)
:else
`(if-let ~(vec bindings)
~true-expr
~false-expr))))
This macro passed these tests below.
(deftest ut-if-lets
(testing "if-lets macro (normal cases)"
(is (= 0 (if-lets [x 0] x)))
(is (= 0 (if-lets [x 0] x 1)))
(is (= 1 (if-lets [x nil] x 1)))
(is (= 0 (if-lets [x 0 y x] y)))
(is (= 0 (if-lets [x 0 y x] y 1)))
(is (= 1 (if-lets [x nil y x] y 1)))
(is (= 0 (if-lets [x 0 y x z y] z)))
(is (= 0 (if-lets [x 0 y x z y] z 1)))
(is (= 1 (if-lets [x nil y x z y] y 1)))
(is (= true (if-lets [x true] true false)))
(is (= false (if-lets [x false] true false)))
(is (= true (if-lets [x true y true] true false)))
(is (= false (if-lets [x false y true] true false)))
(is (= false (if-lets [x true y false] true false)))
(is (= true (if-lets [x true y true z true] true false)))
(is (= false (if-lets [x false y true z true] true false)))
(is (= false (if-lets [x true y false z true] true false)))
(is (= false (if-lets [x true y true z false] true false)))
)
)
(deftest ut-if-lets-ab
(testing "if-lets macro (abnormal cases)"
(is (= (try (if-lets [] true false) (catch Exception e (.getMessage e)))
"if-lets requires 2 or multiple of 2 forms in binding vector in user:1"))
(is (= (try (if-lets [x] true false) (catch Exception e (.getMessage e)))
"if-lets requires 2 or multiple of 2 forms in binding vector in user:1"))
(is (= (try (if-lets [x true y] true false) (catch Exception e (.getMessage e)))
"if-lets requires 2 or multiple of 2 forms in binding vector in user:1"))
)
)
For practice, I've defined
(defmacro quote-paren
"body -> `(body)"
[& body]
`(~#body))
which has the expected transformation (quote-paren body) => ``(body)`. It seems to satisfy a few basic tests:
user=> (macroexpand-1 `(quote-paren 3 4 5))
(3 4 5)
user=> (macroexpand-1 `(quote-paren println "hi"))
(clojure.core/println "hi")
user=> (macroexpand-1 `(quote-paren (println "hi")))
((clojure.core/println "hi"))
However, I've been testing it with this do-while macro (modified from here):
(defmacro do-while
[test & body]
(quote-paren loop []
~#body
(when ~test
(recur))))
(def y 4)
(do-while (> y 0)
(def y (dec y)))
But the result is
IllegalStateException Attempting to call unbound fn: #'clojure.core/unquote-splicing clojure.lang.Var$Unbound.throwArity (Var.java:43)
I don't understand this, because from what I can see the `quote-paren' macro works fine (with ~#body plugged in):
user=> (macroexpand-1
`(quote-paren loop []
(def y (dec y))
(when ~test
(recur))))
(clojure.core/loop [] (def user/y (clojure.core/dec user/y)) (clojure.core/when #<core$test clojure.core$test#1f07f672> (recur)))
But trying to macroexpand do-while causes an "unbound fn". Is there something subtle I'm missing?
missing the syntax-quote before quote-paren
user> (defmacro do-while
[test & body]
`(quote-paren loop []
~#body
(when ~test
(recur))))
#'user/do-while
which then expands properly:
user> (macroexpand '(do-while (> y 0)
(def y (dec y))))
(loop* [] (def y (dec y)) (clojure.core/when (> y 0) (recur)))
and seems to work:
user> (def y 4)
#'user/y
user> (do-while (> y 0)
(def y (dec y)))
nil
user>
I would like to see if a symbol has been "def" ed, but I can't see any ifdef syntax
user> (resolve 'foo)
nil
user> (def foo 3)
#'user/foo
user> (resolve 'foo)
#'user/foo
resolve or ns-resolve may do what you're looking for:
user> (def a 1)
#'user/a
user> (def b)
#'user/b
user> (resolve 'a)
#'user/a
user> (resolve 'b)
#'user/b
user> (resolve 'c)
nil
To get a boolean:
user> (boolean (resolve 'b))
true
EDIT: per MayDaniel's comment, this isn't exactly what you asked for, but it will get you there. Here's an implementation of bounded? (probably not the best name):
(defn bounded? [sym]
(if-let [v (resolve sym)]
(bound? v)
false))
user> (map bounded? ['a 'b 'c])
(true false false)
Can use find-var for this
(bound? (find-var 'user/y))