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>
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))))
Consider this pseudo code:
(defrc name
"string"
[a :A]
[:div a])
Where defrc would be a macro, that would expand to the following
(let [a (rum/react (atom :A))]
(rum/defc name < rum/reactive []
[:div a]))
Where rum/defc is itself a macro. I came up with the code below:
(defmacro defrc
[name subj bindings & body]
(let [map-bindings# (apply array-map bindings)
keys# (keys map-bindings#)
vals# (vals map-bindings#)
atomised-vals# (atom-map vals#)]
`(let ~(vec (interleave keys# (map (fn [v] (list 'rum/react v)) (vals atomised-vals#))))
(rum/defc ~name < rum/reactive [] ~#body))))
Which almost works:
(macroexpand-all '(defrc aname
#_=> "string"
#_=> [a :A]
#_=> [:div a]))
(let* [a (rum/react #object[clojure.lang.Atom 0x727ed2e6 {:status :ready, :val nil}])] (rum/defc aname clojure.core/< rum/reactive [] [:div a]))
However when used it results in a syntax error:
ERROR: Syntax error at (clojure.core/< rum.core/reactive [] [:div a])
Is this because the inner macro is not being expanded?
Turns out the macro was working correctly but the problem occurred because < was inside the syntax quote it got expanded to clojure.core/<, and Rum simply looks for a quoted <, relevant snippet from Rum's source:
...(cond
(and (empty? res) (symbol? x))
(recur {:name x} next nil)
(fn-body? xs) (assoc res :bodies (list xs))
(every? fn-body? xs) (assoc res :bodies xs)
(string? x) (recur (assoc res :doc x) next nil)
(= '< x) (recur res next :mixins)
(= mode :mixins)
(recur (update-in res [:mixins] (fnil conj []) x) next :mixins)
:else
(throw (IllegalArgumentException. (str "Syntax error at " xs))))...
clojure's pre-expr seems cool, but does it possible if I want to raise an Exception when the :pre is false?
thanks.
You can use Dire instead
(ns mytask
(:require [dire.core :refer [with-precondition! with-handler!]]))
(defn add-one [n]
(inc n))
(with-precondition! #'add-one
"An optional docstring."
;;; Name of the precondition
:not-two
(fn [n & args]
(not= n 2)))
(with-handler! #'add-one
{:precondition :not-two}
(fn [e & args] (apply str "Precondition failure for argument list: " (vector args))))
(add-one 2) ; => "Precondition failure for argument list: (2)"
Preconditions are conditions that must be true or else an exception is thrown. If you have a condition where you want an exception to be thrown when false, just complement the conditional or not the result.
user=> (defn magic? [n] (= 0 (rem n 42)))
#'user/magic?
user=> (defn foo [n] {:pre [(magic? n)]} n)
#'user/foo
user=> (foo 42)
42
user=> (defn bar [n] {:pre [(not (magic? n))]} n)
#'user/bar
user=> (bar 42)
AssertionError Assert failed: (not (magic? n)) user/bar
user=> (defn baz [n] {:pre [((complement magic?) n)]} n)
#'user/baz
user=> (baz 42)
AssertionError Assert failed: ((complement magic?) n) user/baz
Is there anyway i can return a value from a loop since the recursion has to be at the tail
(ns for)
(defn abc [y]
(loop [x 10]
(when (> x 2)
(if (= 2 3) (do (println "test") (recur (- x 2)))
(do (let [x (+ 1 x)
y 2] (println y) (recur (- x 2))))))))
(abc 1)
is there anyway i can return a value for the function by taking y as a parameter and updating a new value of y. However, the recur part has to be at the last line of the code hence i am unable to put y as the last line of the code.
Example
(ns for)
(defn abc [y]
(loop [x 10]
(when (> x 2)
(if (= 2 3) (do (println "test") (recur (- x 2)))
(do (let [x (+ 1 x)
y 2] (println y) (recur (- x 2)))))))
y)
(abc 1)
This would give me an error since recur has to be the last line of code. I have looked at similar questions and it says to put the return value at the end of the if loop which i tried but failed which gives me an exception thatthe recursion can only happen at the tail
I guess you meant this:
(defn abc [y]
(loop [x 10
y nil]
(if (> x 2)
(if (= 2 3)
(do (println "test")
(recur (- x 2) nil))
(do (let [x (+ 1 x)
y 2]
(println y)
(recur (- x 2) y))))
y)))
Update. Without unnecessary parts it would be
(defn abc [y]
(loop [x 10]
(if (> x 2)
(do (println 2)
(recur (- x 1)))
2)))
which is the same as
(defn abc [_]
(dotimes [_ 8] (println 2))
2)
The following code appears to force line-seq to read 4 lines from file. Is this some kind of buffering mechanism? Do I need to use lazy-cat here? If so, how can I apply a macro to a sequence as if it were variadic arguments?
(defn char-seq [rdr]
(let [coll (line-seq rdr)]
(apply concat (map (fn [x] (println \,) x) coll))))
(def tmp (char-seq (clojure.contrib.io/reader file)))
;,
;,
;,
;,
#'user/tmp
Part of what you're seeing is due to apply, since it will need to realize as many args as needed by the function definition. E.g.:
user=> (defn foo [& args] nil)
#'user/foo
user=> (def bar (apply foo (iterate #(let [i (inc %)] (println i) i) 0)))
1
#'user/bar
user=> (defn foo [x & args] nil)
#'user/foo
user=> (def bar (apply foo (iterate #(let [i (inc %)] (println i) i) 0)))
1
2
#'user/bar
user=> (defn foo [x y & args] nil)
#'user/foo
user=> (def bar (apply foo (iterate #(let [i (inc %)] (println i) i) 0)))
1
2
3
#'user/bar