Consider this form:
(def v [42 "foo" 99.2 [5 12]])
I've read that if I have to declare some unused variables in a let form I should denote them with _
like in this destructuring form:
(let [[x _ _ [y z]] v]
(+ x y z))
My question is how does the assignment to _ happens? Since this do not throw an exception I assume that the 2nd _ overrides the first one but I'm not sure. So how do this work?
This use of _ is purely conventional: from Clojure's point of view, it's just a regular symbol which can be used to name a local. So, you can simply check the value of _ directly to confirm your understanding:
(let [[x _ _ [y z]] v]
_)
;= 99.2
As for what goes on under the hood, the simplest way to check that is to macroexpand the let form:
(macroexpand-1 '(let [[x _ _ [y z]] v] _))
The result of the above, reformatted for clarity, looks like this:
(let* [vec__7 v
x (clojure.core/nth vec__7 0 nil)
_ (clojure.core/nth vec__7 1 nil)
_ (clojure.core/nth vec__7 2 nil)
vec__8 (clojure.core/nth vec__7 3 nil)
y (clojure.core/nth vec__8 0 nil)
z (clojure.core/nth vec__8 1 nil)]
_)
So the second _ simply shadows the first one.
let* is the implementation detail behind let; it is a special form directly understood by the compiler, to which the let macro adds destructuring support.
Related
I am trying to write a macro that will allow me to do the following
(without-nesting
(:= x 1)
(:= y 2)
(:= z 3)
(db-call x y z)
(:= p 33)
(db-call x y z p))
becomes
(let [x 1
y 2
z 3]
(db-call x y z)
(let [p 33]
(db-call x y z p)))
So far my implementation has been the following
(defn assignment?
[[s _]]
(= s ':=))
(defmacro without-nesting
[& body]
(let [[bindings xs] (split-with assignment? body)
[non-bindings remaining] (split-with (complement assignment?) xs)]
`(let [~#(mapcat rest bindings)]
~#non-bindings
~(when (seq remaining)
`(without-nesting ~#remaining)))))
I'm having issues when remaining is going to be empty. In my current implementation a nil gets placed which prevents the last form in non-bindings to return its value. I have no clue on how to proceed with a recursive macro. Can someone help
UPDATE:
So I was able to get rid of the nil but I just want to know if there's a better way to deal with this
(defmacro without-nesting
[& body]
(let [[bindings xs] (split-with assignment? body)
[non-bindings remaining] (split-with (complement assignment?) xs)]
`(let [~#(mapcat rest bindings)]
~#non-bindings
~#(if (seq remaining)
[`(without-nesting ~#remaining)]
[]))))
Also would this be a good way to write code? Or do you see any caveats? For me it looks more linear as compared to nested let
I do see how ppl may abuse this. In the case of let, if the nesting becomes too much, then it's a hint to refactor the code. This might hide that
Just use let. It is already recursive. To incorporate function calls where you only care about the side effects, the convention is to bind to an underscore.
(let [x 1
y 2
z 3
_ (db-call x y z)
p 33
_ (db-call x y z p)])
(match
[[1 2 3]]
[(:or [_ _ 2]
[3 _ _])] :a0
[(:or [_ _ 1]
[1 _ _])] :a1
:else :else)
=> :else
In this first snippet I expected it to return :a1.
Weird.
This works:
(match
[[1 2 3]]
[(:or [_ _ 2]
[1 _ _])] :a0
[(:or [_ _ 1]
[3 _ _])] :a1
:else :else)
=> :a0
Is this an expected behaviour?
I'd say it is not because the way you are using :or is never mentioned in the manual. It is supposed to be used in expressions like that:
[4 (:or 5 6 7) _] :a1
So your code should rather look like
(match
[[1 2 3]]
[[_ _ 2]] :a0
[[3 _ _]] :a0
[[_ _ 1]] :a1
[[1 _ _]] :a1
:else :else)
But just maybe you should consult with the author. It is difficult to say what was the original intention.
I believe this is a bug in specialize-or-pattern-row. I believe the groupable? test there is wrong because, in your case, it succeeds for your two OrPatterns, and so the second OrPattern is replaced with the expansion of the first (the ps are the subpatterns of the first OrPattern).
You could work around this by adding a dummy pattern to your second :or which will force groupable? to return false:
(match
[[1 2 3]]
[(:or [_ _ 2]
[1 _ _])] :a0
[(:or [_ _ 1]
[3 _ _]
:dummy)] :a1
:else :else)
A possibly better specialize-or-pattern-row (copy-as is there to preserve any :as metadata on the overall OrPattern by copying the :as on to each of the subpatterns):
(defn copy-as [dest src]
(if-let [as (-> src meta :as)]
(vary-meta dest assoc :as as)
dest))
(defn specialize-or-pattern-row [row pat ps]
(let [p (first row)]
(if (identical? p pat)
(map (fn [p] (update-pattern row 0 (copy-as p pat))) ps)
[row])))
Quoting an answer from reddit:
Seems like a bug in core.match. I used a slightly simpler statement that has the same problem.
(match
[[1]]
[(:or [3] [])] :a0
[(:or [1] [])] :a1
:else :else)
This also returns :else. I ran it through macroexpand and extracted the logic. It turns into something like this.
(let
[x [1]]
(cond (and (vector? x) (== (count x) 1) (= (nth x 0) 3)) :a0
(and (vector? x) (== (count x) 0)) :a0
(and (vector? x) (== (count x) 1) (= (nth x 0) 3)) :a1
(and (vector? x) (== (count x) 0)) :a1))
On the 5th line there you can see the error, it is 3 instead of 1. For some reason it takes the value that is in the first :or pattern instead of the second rows value.
Seems that this patch resolves the problem.
Thanks you all!
P.S.: I haven't tested the patch, though.
This was asked here, but the answers are all unacceptable.
I'm trying to apply some defensive programming techniques to clojure and I'm finding some things cumbersome.
Like checking function parameters:
(defn my-tolower [s]
(if (nil? s) nil
(.toLowerCase s)
))
Is there a cleaner way to do this?
I'm aware of :pre, but that throws an exception.
It seems you simply want some->, no?
(defn my-tolower [s]
(some-> s .toLowerCase))
(my-tolower "HELLO") => "hello"
(my-tolower nil) => nil
or just inline it without the wrapper function:
(some-> "HELLO" .toLowerCase) => "hello"
(some-> nil .toLowerCase) => nil
Since nil is falsey you could use when:
(when s (.toLowerCase s))
if you want the test you could use some? instead of nil?:
(if (some? s) (.toLowerCase s))
there are other aproaches too:
fnil, probably what I'd do
clojure.core/fnil
([f x] [f x y] [f x y z])
Takes a function f, and returns a function that calls f, replacing
a nil first argument to f with the supplied value x. Higher arity
versions can replace arguments in the second and third
positions (y, z). Note that the function f can take any number of
arguments, not just the one(s) being nil-patched.
provide a default value for a nil accepting fn. /
(let [f (fnil str/lower-case "")]
(f nil))
""
or catching the NPE
(let [f str/lower-case]
(try (f nil)
(catch NullPointerException ne nil)))
""
or just str
(.toLowerCase (str nil))
""
alternativly defprotocol and extend-protocol for nil maybe
Suppose we have a list with an _ in an arbitrary location. For example: (a b c _ e f). I'm trying to write a macro which, for such a list, finds the _ and replaces it with another value (say, z): (a b c z e f).
What is the best approach for this?
Are you sure you need a macro? Using replace should work fine as long as you quote the list:
(replace '{_ z} '(a b c _ e f)) ; => (a b c z e f)
the #Josh answer is right, first you should decide if you really need macro for something. I can imagine a synthetic example. Let's pretend you want to define a function but need to track this _ value for some reason (for logging maybe), to use it like this:
(defn-with-placeholder my-fn [a b c _ e] z
(println a b c z e))
that's how you do this:
(defmacro defn-with-placeholder [name args placeholder & body]
`(defn ~name ~(vec (replace {'_ placeholder} args))
~#body))
notice that i've used the same replace approach, which was proposed earlier.
let's test it in repl:
user> (defn-with-placeholder my-fn [a b _ d] placeholder
(println a b placeholder d))
#'user/my-fn
user> (my-fn 1 2 3 4)
1 2 3 4
nil
ok now it's pretty useless. Lets go further with exercise, and make a definition, that will gather all the omitted parameters to some collection (like functions rest parameters & args, but on different positions)
So we could define a macro defn-with-omitted that works like this:
(defn-with-omitted my-fn-2 [a _ c _ e f _ h] other-args
(println :parameters a c e f h)
(println :other-parameters other-args))
in repl:
user> (my-fn-2 1 100 2 200 3 4 300 5)
:parameters 1 2 3 4 5
:other-parameters {1 100, 3 200, 6 300}
nil
it gathers all the omitted data and puts it to other-args map, with arg-position to arg mapping.
To do this first of all we need to create a function which processes the arglist and gathers all omitted arguments:
(defn process-args [args]
(reduce-kv (fn [[args omitted] idx arg]
(if (= '_ arg)
(let [sym-name (gensym "omitted")]
[(conj args sym-name)
(assoc omitted idx sym-name)])
[(conj args arg) omitted]))
[[] {}]
args))
here's what it does:
user> (process-args '[a _ b c _ _ f g])
[[a omitted29608 b c omitted29609 omitted29610 f g]
{1 omitted29608, 4 omitted29609, 5 omitted29610}]
notice that i've used gensym here, not to shadow possible outer definitions.
so now it's quite easy to make the macro:
(defmacro defn-with-omitted [name args omitted-name & body]
(let [[args omitted] (process-args args)]
`(defn ~name ~args
(let [~omitted-name ~omitted]
~#body))))
let's check the expansion:
(defn-with-omitted my-fn-2 [a _ c _ e f _ h] other-args
(println :parameters a c e f h)
(println :other-parameters other-args))
expands to:
(defn my-fn-2 [a omitted29623 c omitted29624 e f omitted29625 h]
(let [other-args {1 omitted29623, 3 omitted29624, 6 omitted29625}]
(println :parameters a c e f h)
(println :other-parameters other-args)))
which is exactly what we want.
I've started learning core.logic and I'm totally lost. I am trying to write a core.logic relation which refactors an expression, renaming symbols. I want a relation that returns for a given expression, list of symbols and a list of symbols to rename those symbols:
(defn rename [exp from to]...
the expression with all the symbols in from becoming the corresponding one in to:
e.g. (rename '(defn multiply [x y] (* x y)) [x y] [a b])
returns (defn multiply [a b] (* a b))
but it needs to be aware of scope,
so (rename '(defn q [x] ((fn [x] (* x 5)) x)) [x] [a])
would return (defn q [a] ((fn [x] (* x 5)) a))
I don't know where to start solving this - any hints would be greatly appreciated!
This problem is more suitable for FP as it is just a tree traversal and replace operation, where as LP is more about specifying constrains and asking all possible solution around those constrains for a specific input. But if you really want to do this logical way, I tried something that does try to do it LP way but it doesn't handle a lot of cases and is just a starting point.
(defrel replace-with a b)
(fact replace-with 'x 'a)
(fact replace-with 'y 'b)
(defn replace [a b]
(conde
[(replace-with a b)]
[(== a b)]))
(defn replace-list [from to]
(conde
[(== from []) (== to [])]
[(fresh [f t f-rest t-rest]
(resto from f-rest)
(resto to t-rest)
(firsto from f) (firsto to t)
(conda [(replace-list f t)]
[(replace f t)])
(replace-list f-rest t-rest))]))
(first (run 1 [q]
(fresh [from]
(== from '(defn multiply [x y] (* x y)))
(replace-list from q))))
==> (defn multiply (a b) (* a b))