accessing Clojure's thread-first macro arguments - clojure

I was wondering if there was a way to access the arguments value of a thread-first macro in Clojure while it is being executed on.
for example:
(def x {:a 1 :b 2})
(-> x
(assoc :a 20) ;; I want the value of x after this step
(assoc :b (:a x))) ;; {:a 20, :b 1}
It has come to my attention that this works:
(-> x
(assoc :a 20)
((fn [x] (assoc x :b (:a x))))) ;; {:a 20, :b 20}
But are there any other ways to do that?

You can use as->:
(let [x {:a 1 :b 2}]
(as-> x it
(assoc it :a 20)
(assoc it :b (:a it))))

In addition to akond's comment, note that using as-> can get quite confusing quickly. I recommend either extracting a top level function for these cases, or trying to use as-> in -> only:
(-> something
(process-something)
(as-> $ (do-something $ (very-complicated $)))
(finish-processing))

Related

Clojure custom function for threading macro

I have a map and I want to write a custom function for updating it.
(-> {:a 1 :b 2}
(fn [x] (update x :a inc)))
This of course is a simple example and could be easily done without the function wrapped around the update, but it shows what I want to do. But this gives me the following error.
Syntax error macroexpanding clojure.core/fn at (core.clj:108:1).
{:a 1, :b 2} - failed: vector? at: [:fn-tail :arity-1 :params] spec: :clojure.core.specs.alpha/param-list
{:a 1, :b 2} - failed: (or (nil? %) (sequential? %)) at: [:fn-tail :arity-n] spec: :clojure.core.specs.alpha/params+body
I don't get why this is not working, since the threading macro should but my map as first parameter in the function, right?
You can always use macroexpand to see what happened. In your case, macroexpand will return you:
(fn {:a 1, :b 2} [x] (update x :a inc))
obviously this is not a valid function. But if you tweak it this way:
(-> {:a 1 :b 2}
(#(update % :a inc)))
the expanded form will then become valid:
(#(update % :a inc) {:a 1, :b 2})
You don't put a function itself to be called, but call the function without the first parameter, For your example it would be:
> (-> {:a 1 :b 2}
(update :a inc))
{:a 2, :b 2}
This is easier to see by expanding the macro in each case
> (macroexpand-1 '(-> {:a 1 :b 2} (update :a inc)))
(update {:a 1, :b 2} :a inc)
> (macroexpand-1 '(-> {:a 1 :b 2} (fn [x] (update x :a inc))))
(fn {:a 1, :b 2} [x] (update x :a inc))
As #jas and #rmcv pointed out, I was giving the threading macro the function itself, not the call of a function without the argument. So in short terms the solution would be
(-> {:a 1 :b 2}
((fn [x] (update x :a inc))))
I don't think any of these solutions are the simplest. I would propose choosing one of the following:
A. Use the normal threading form:
(-> {:a 1, :b 2}
(update :a inc)) => {:a 2, :b 2}
Everyone is used to seeing this and can understand it easily. Since you have already rejected this approach, I assume you think the code is clearer by using a named parameter.
B. Use a named function
(defn updater [x] (update x :a inc))
(-> {:a 1, :b 2}
updater) => {:a 2, :b 2}
(-> {:a 1, :b 2}
(updater)) => {:a 2, :b 2}
This is more how the -> form was envisioned to work. I think the 2nd version is the clearest, as it is the most consistent where all function expressions have parentheses (single arg or multi-arg).
C. Consider using the it-> macro from the Tupelo Library:
(it-> {:a 1, :b 2}
(update it :a inc)) => {:a 2, :b 2}
Much like the named function, the expression is normal Clojure form without the "invisible" parameter silently inserted into the update expression. The pronoun it serves as the temporary placeholder for the threaded value (an idea copied from Groovy). Simple, explicit, and flexible, since the it can be in the first, last, or any other parameter location:
(it-> 1
(inc it) ; thread-first or thread-last
(+ it 3) ; thread-first
(/ 10 it) ; thread-last
(str "We need to order " it " items." ) ; middle of 3 arguments
;=> "We need to order 2 items." )

Macro to pass patterns and results to core.match/match as a vector

I am struggling on how to construct a macro that lets me pass patterns and results to core.match/match in the form of a vector. I would like to be able to do this:
(let [x {:a 1}
patterns [[{:a 2}] :high
[{:a 1}] :low]]
(my-match x patterns))
> :low
I have tried the below and several other approaches which do not work, unless I pass patterns as a literal.
(defmacro my-match [e ems]
`(m/match [~e] ~#ems))
(let [x {:a 1}
patterns [[{:a 2}] :high
[{:a 1}] :low]]
(my-match x patterns))
=> CompilerException java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.lang.Symbol, compiling:(*cider-repl kontrakt*:106:10)
(let [x {:a 1}]
(my-match x [[{:a 2}] :high
[{:a 1}] :low]))
=> :low
Macros are expanded at compile time, so you cannot rely on runtime information (the value of a parameter) during expansion. The root problem is that you can't apply a macro in the same way you can apply a function.
In clojure, how to apply a macro to a list?
So you have to either resort to using eval:
(defmacro functionize [macro]
`(fn [& args#] (eval (cons '~macro args#))))
(defmacro my-match [e ems]
`(apply (functionize m/match) [~e] ~ems))
Or approach the problem in a different way (do runtime pattern matching instead of compile time pattern matching).
The simplest way to solve your problem is with a plain old map:
(ns clj.core
(:use tupelo.core))
(def x {:a 1} )
(def patterns { {:a 2} :high
{:a 1} :low } )
(spyx (get patterns x))
;=> (get patterns x) => :low
Since you have no "wildcard values", you don't need core.match at all. If you would like to match on wild-card values, please see the function wild-match? in the Tupelo library. Samples:
(wild-match? {:a :* :b 2}
{:a 1 :b 2}) ;=> true
(wild-match? [1 :* 3]
[1 2 3]
[1 9 3] )) ;=> true
(wild-match? {:a :* :b 2}
{:a [1 2 3] :b 2}) ;=> true

clojure: given a list of maps, get the total sum value of a specific key value

Input: [{:a "ID1" :b 2} {:a "ID2" :b 4}]
I want to only add up all the keys :b and produce the following:
Result: 6
I thought about doing a filter? to pull all the numbers into vector and add it all up but this seems like doing work twice. I can't use merge-with + here since the :a has a string in it. Do I use a reduce here with a function that will pull the appropriate key?
(reduce (fn [x] (+ (x :b))) 0 list-of-maps)
It would be even nicer if I could retain the map structure with updated value ({:a "ID1" :b 6}) but since I don't really need the other keys, just the total sum is fine.
I want to only add up all the keys :b and produce the following:
Result: 6
I believe workable code is:
(def m1 {:a 1, :b 2})
(def m2 {:a 11, :b 12})
(def m3 {:a 21, :b 22})
(def ms [m1 m2 m3])
(->> ms
(map :b)
(reduce +))
I feel use of ->> here can help readability in your situation.
This says to take action on ms, which is defined to be a vector of maps, threading incremental results through the remaining forms.
The first thing is to transform each entry of maps using the keyword :b as a function on each, extracing the value corresponding to that key, resulting in the sequence:
(2 12 22)
You can then apply reduce exactly as you intuit across that seq to get the result:
user=> (def m1 {:a 1, :b 2})
#'user/m1
user=> (def m2 {:a 11, :b 12})
#'user/m2
user=> (def m3 {:a 21, :b 22})
#'user/m3
user=> (def ms [m1 m2 m3])
#'user/ms
user=> (->> ms
#_=> (map :b)
#_=> (reduce +))
36
I'm a tad confused by what you intend by this part of the question:
It would be even nicer if I could retain the map structure with updated value ({:a "ID1" :b 6})
Do you want to have each value for :b across all maps contain the sum of them all in a result, or something else?
(reduce + (map :b list-of-maps))
This its simple but it works!
user=> (+ (your-map :b) (your-map :b))
or
user=> (def x [{:a "ID1" :b 2} {:a "ID2" :b 4}])
#'user/x
user=> (+ ((first x) :b) ((second x) :b))
6
user=>
or user=> (+ ((nth x 0) :b) ((nth x 1) :b))
6

Lazy self-recursive data structures in Clojure

Is there a way to describe arbitrary lazy self-recursive data structures in Clojure?
Let's say for example I wanted to do something like this:
(def inf-seq (fn rec [] (lazy-seq (cons 42 (rec)))))
(take 3 (inf-seq))
but with a map:
(def inf-map (fn rec [] (??? {:a (rec) :b 42})))
(get-in (inf-map) [:a :a :a :b])
Sequence laziness does not apply to deferred function evaluation in Clojure, which you would obviously need for constructing infinitely nested maps.
Try using Delays:
user=> (def inf-map (fn rec [] {:a (delay (rec)) :b 42}))
#'user/inf-map
user=> (inf-map)
{:a #<Delay#4e9f9a19: :pending>, :b 42}
user=> #(:a (inf-map))
{:a #<Delay#5afd479c: :pending>, :b 42}

Why does Clojure's map destructuring look reversed?

Destructuring a map looks reversed to me. Can anybody explain what is happening?
I expect that this is the right form of destructuring a map
;=> (let [{:a a :b b} {:a 1 :b 2}] [a b])
which returns Exception Unsupported binding form: :a clojure.core/destructure/pb--4541 (core.clj:4029). Clojure documentations say that below is the right way. But it looks that keys and values are reversed.
This should be the right way:
;=> (let [{a :a b :b} {:a 1 :b 2}] [a b])
[1 2]
What is happening when destructuring a map?
It is not really reversed, actually it makes sense. It says: bind to symbol 'a' to value that is associated with the keyword :a
Are you aware of this when your map uses keywords as keys?
(let [{:keys [a b]} {:a 1 :b 2}] [a b])
Much neater and elegant!
Other variants exist if your keys are symbols or strings.
Also, it makes it possible to distinguish between these to cases:
cljs.user=> (let [{foo :foo :as bar} {:foo 3 :as 4}] [foo bar])
[3 {:foo 3, :as 4}]
cljs.user=> (let [{foo :foo bar :as} {:foo 3 :as 4}] [foo bar])
[3 4]