Fork me on GitHub

Hey there, I'm trying to write a macro that binds a value to a name in the local scope, which means I don't want gensym. Is there a way to do this? My variable is always called ctx by convention, and I want to make calls to the macro very concise. Here's my code:

(defmacro defnode
  [node-sym args-binding call-form & dep-forms]
  (let [ctx-sym (gensym 'ctx)
        dep-pairs (apply hash-map dep-forms)
        dep-keys (vec (map #(-> % first name) dep-pairs))]
    `(def ~node-sym
        (fn [~ctx-sym {:keys ~dep-keys} ~@args-binding] ~call-form)
            (fn [[dep-key dep-call]]
              {dep-key `(fn [~ctx-sym ~args-binding] ~dep-call)})
            (apply hash-map dep-forms))))))
A call to (macroexpand '(defnode x-item [idx] (nth (:items ctx) idx))) gives me (def x-item (user/node (clojure.core/fn [ctx242 {:keys []} idx] (nth (:items ctx) idx)))) which is very close (ignore user/node, I'm in the repl), but I want ctx to be the bound name.


@jstaab037 like this:

`(... ~'ctx ...)


or simply bind 'ctx to ctx-sym, no need to use gensym


oh hey quotes, great idea


This is my first macro


It's soo much easier to write macros than I had expected


also a tip, you can convert the varargs into a map in the signature: [... & {:as dep-pairs}]


Gotcha, thanks!


One more thing, I now have:

(defmacro defnode
  [node-sym args-binding call-form & dep-forms]
  (let [dep-pairs (apply hash-map dep-forms)
        dep-keys (vec (map #(-> % first name symbol) dep-pairs))]
    `(def ~node-sym
        (fn [~'ctx {:keys ~dep-keys} ~@args-binding] ~call-form)
              (fn [[dep-key dep-call]]
                {dep-key `(fn [~'ctx ~@args-binding] ~dep-call)})
              (apply hash-map dep-forms)))))))
(macroexpand '(defnode x-item-pct [idx] (/ item total) :total (x-total ctx) :item (x-item ctx idx)))
gives me
(def x-item-pct (user/node (clojure.core/fn [ctx {:keys [item total]} idx] (/ item total)) {:item (clojure.core/fn [ctx idx] (x-item ctx idx))} {:total (clojure.core/fn [ctx idx] (x-total ctx))}))
I want the two maps at the end to be merged into a single map, is there a reason that's not happening?


@jstaab I think you should do ~@(apply merge ...)


Hmm that gives me (def x-item-pct (user/node (clojure.core/fn [ctx {:keys [item total]} idx] (/ item total)) [:item (clojure.core/fn [ctx idx] (x-item ctx idx))] [:total (clojure.core/fn [ctx idx] (x-total ctx))]))


Oh but yeah you're right, it's the ~@ that's causing the problem now


oh right, no need to splice then


Right, cool that's working


hi, trying to figure out why this bit of code is not considered to be in the tail end position:

; create a nested tree of maps
; ["a" "b" "c"] -> {:key "a" :children {:key "b" :children {:key "c" :children nil}}}
(defn tree [xs]
	(if (seq xs)
		{:key (first xs)
		 :children (recur (rest xs))} ))
when I change the recur to tree then it works just fine. the creation of the map literal is the last form being evaluated in this function as far as I can tell. is there a simpler / more idiomatic way to do this?

Alex Miller (Clojure team)02:10:35

It’s not in tail position because it has to build the map last to return it


thanks @alexmiller, is there a better alternative to this approach without blowing through the stack (by just recursing without recur, that is, calling … :children (tree (rest xs))} ...)?

Alex Miller (Clojure team)02:10:55

without an accumulator, you can’t make this a loop/recur

Alex Miller (Clojure team)02:10:00

and probably you want to build from the bottom, not from the top


will look into this approach, thanks again

Alex Miller (Clojure team)02:10:03

(defn tree [xs]
    (loop [r nil
           t (reverse xs)]
      (let [[k & ks] t]
        (if k
          (recur {:key k :children r} ks)

Alex Miller (Clojure team)02:10:15

r is the accumulator, t holds the remainder of the keys to look at. whether k exists is your termination check.

👍 4
Yehonathan Sharvit10:10:21

A question related to defmulti and defmethod. It seems that running defmulti a second time with the same variable name (after defmulti has been already called) has not effect. In other words, one cannot change the displatch function without reloading the whole process (e.g. without leaving the REPL). What is the rationale behind this limitation?


@viebel you can avoid restart your repl doing

(def my-multi "")
(def my-multi :my-new-dispatch-fn)
When you redefine your multimethod, you will need to reload all namespaces that do some defmethod

Yehonathan Sharvit11:10:49

Let me be more explicit

user=> (defmulti foo (constantly :a))
user=> (defmethod foo :a [_] :a)
#object[clojure.lang.MultiFn 0x1506f20f "clojure.lang.MultiFn@1506f20f"]
user=> (foo 1)
user=> (defmethod foo :b [_] :b)
#object[clojure.lang.MultiFn 0x1506f20f "clojure.lang.MultiFn@1506f20f"]
user=> (foo 1)
user=> (defmulti foo (constantly :b))
user=> (foo 1)

Yehonathan Sharvit11:10:18

I was expecting the last call to (foo 1) to return :b, because the dispatch function has changed


looking at it in macroexpand it first checks if it .hasRoot and if it’s symbol deref’d is a clojure.lang.MultiFn then defines it if wither of them is false.

user=> (macroexpand '(defmulti foo (constantly :a)))
(let* [v__5445__auto__ (def foo)] (clojure.core/when-not (clojure.core/and (.hasRoot v__5445__auto__) (clojure.core/instance? clojure.lang.MultiFn (clojure.core/deref v__5445__auto__))) (def foo (new clojure.lang.MultiFn "foo" (constantly :a) :default #'clojure.core/global-hierarchy))))



user=> (defmulti foo (constantly :a))
user=> (.hasRoot #'user/foo)
user=> (clojure.core/instance? clojure.lang.MultiFn @#'user/foo)
you could instead define it directly, eg (def foo (new clojure.lang.MultiFn "foo" (constantly :b) :default #'clojure.core/global-hierarchy)), but i seems like defmulti may not have been intended to be used that way

Yehonathan Sharvit11:10:06

This is exactly what I am asking: why defmulti has not be intended to be used that way?


it seems to do with rebuilding a dispatch table


I found this thread, which talks about it a bit:!topic/clojure/MXf8zWAv3vI . I’m sorry I haven’t answered your question

Alex Miller (Clojure team)12:10:15

It’s mentioned in that thread - if you have defmethods defined across multiple files, a reload will kill the dispatch table.

Alex Miller (Clojure team)12:10:50

That is, a multi method has global state and reloading locally loses state created from other places

Alex Miller (Clojure team)12:10:50

If someone wanted to work from a problem statement towards a change that took this into account, I think it’s possible there is a middle ground

Yehonathan Sharvit15:10:32

What about including the current limitation information in defmulti docstring?


Hello. I'm trying to sort a list of entities by two values. The values are whether an entity is "pinned to top" and then by it's creation date. The "pinned" should always be respected and be the primary sorting value, and then I want to sort by date after that. It also needs to support ascending/descending. I got it working before by doing this:

(defn- sort-entities [sort-direction entities]
  (let [pinned-entities (filter pinned? entities)
        non-pinned-entities (filter (complement pinned?) entities)]

    (if (= sort-direction :sort-by-direction/ascending)
        (->> pinned-entities (sort-by :created-at))
        (->> non-pinned-entities (sort-by :created-at)))

        (->> pinned-entities (sort-by :created-at) reverse)
        (->> non-pinned-entities (sort-by :created-at) reverse)))))
I recently found out about juxt so I'm trying to see if I optimise what I had before a bit. What I've got is this:
(defn- sort-entities [sort-direction entities]                  
  (if (= sort-direction :sort-by-direction/ascending)
    (->> entities (sort-by (juxt (complement :pinned?) :created-at)))
    ; how to do this for descending?
I don't know how to make descending sorting work. What I want is a way to sort the values in a descending direction separately on their own, but not together (which is why I used concat in my previous solution). So both pinned/non-pinned would be sorted in a descending direction, but the pinned values are always on top. Is that possible somehow?


Maybe (->> entities (sort-by (juxt :pinned? :created-at)) reverse)? Sort by pinned? instead of (complement :pinned?) to put the pinned stuff at the bottom, then reverse it and pinned will be at the top again.


yeah that seems to work, good idea thanks!


I think I somehow fucked up my library. I have an expression that worked yesterday, and today the repl gives me errors....


without more info i can’t be certain, but did you happen to do it yesterday in cljs?


It is pure clojure, not clojurescript


I am using intellij/cursive


And CTRO+Q gives me the function definition as normal,


and works.


I think perhaps it destroyed the library?


it’s just throwing an error parsing it because diff-associative-key is private, as you can see by the defn- instead of the normal defn,


any chance you were in the namespace of yesterday? that would explain why you could use a private function yesterday


depending on what you’re trying to do I could suggest alternatives, but i can’t explain why it was working yesterday and not today


@U0C7D2H1U thanks for pointing out the private vs public function issue.


it is straange that the function is private,


as it is a common functionality to compare sets by u sing only a few keys.


@U0C7D2H1U: I was only aware of private functions that end with "-" so I did not have this issue in mind...


using the var directly may have bypassed the private check


via #', i don’t suggest doing this


I basically want to compare two sets and only use one field for the difference check


When I tested it yesterday it all worked. I now have copied the code from the project to my repl and it does not longer work also. Very bizarre


maybe maping over the hashmaps in the sets and select-keys then using clojure.set operations, or copying and pasting the diff-associative-key and editing it to do what you want would work


Would something like this work for you @UCSJVFV35?

(def as [{:s "0" :name "bongo"} {:s "b" :name "bongo"}])
(def bs [{:s "0"} {:s "b"} {:s "c"}])

(def xf (map :s))

(clojure.set/difference (into #{} xf bs) (into #{} xf as))


@U0E2EQFME I need the original data in one of the sets to further do calculations. This is why I went for diff-associative. With difference function all other fields get lost.


@U0C7D2H1U: thanks, I am trying to do something along this lines... But for now it clojure does not love me... 😞


(defn diff [l r fl fr] (let [r-project (into #{} (map fr r))] (set (remove #(contains? r-project (fl %)) l))))


I found a snippet that works.


Thanks guys for your help!!

👍 4

hey all, cache question for you: I want to create a cache with the following semantics --

(-> {}
                         (cache/lru-cache-factory :threshold 10000) ;; bound the size of the cache to 10K most used 
                         (cache/ttl-cache-factory :ttl 1500) ;; do not cache infrequently fetched keys (they often need to be renewed)
                         **help** ;; invalidate items that have been in the cache > 30 seconds ;; this is so the most frequently used keys get a chance to refresh
Any thoughts on how I could achieve the last piece?


doesn't the ttl-cache already remove values older than 1.5 seconds?


beware that the printed form of a ttl cache map will show entries that don't show up with contains? or get


I think it's the middle part ;; do not cache infrequently fetched keys (they often need to be renewed) that is tricky here


and that line doesn't do what the comment describes


I thought what ttl would do is: remove values that have not been queried for over 1.5 seconds


ttl-cache-factory doesn't take access into account, only the time the key was created


does the lru cache also allow a timeout?


it doesn't - you might need to create a custom cache type?


gotcha, thanks for the clarity noisesmith if only access is taken into account, maybe just the lru + ttl composition would do what i want right? (would not be able to specify 30 seconds, but perhaps 1.5s for both infrequent requests and common ones will be ok)


right, I think that between timing out entries by creation time, and removing the least frequently accessed items, you'll get pretty close to the right thing


worth testing though


a problem I ran into with a cache the other day: using a cache to represent a "refreshable" resource (auth token), the problem is that with an atom as container there's a race condition where you can drop the freshest token and keep using an old one

👍 4

I wonder if an agent would fix that...


It's also worth noting that the TTL cache has an "interesting" edge case in that it can expire items on access as well as update, so it is possible for a "has?" check to succeed but a subsequent "get" to return nil -- some times that matters, some times it does not.


@seancorfield yeah - I think this is another version of the classic bug where you deref an atom twice in the same block of code, therefore introducing a race condition


(or deref and reset!, etc. - basically two accesses in the same logical flow)


another fun thing is that if you pr the atom, it will show expired keys, because unlike lookup, printing doesn't check validity

Mario C.21:10:40

Which method is generally preferred? A. (-> ctx :request :request-method) B. (get-in ctx [:request :request-method])


@mario.cordova.862 Without additional info, I’d lean towards A


is there a more elegant way to do this?


(defn web-search
  "Search the given String on Google and Bing,
   returns the 'name' of the first page that loads first."
  {:pre  [(string? search-term)]
   :post [#(string? %)]}
  (let [search-google (future (slurp (str "" search-term)))
        search-bing   (future (slurp (str "" search-term)))
        check-search (fn [google bing]
                         (realized? google) "google"
                         (realized? bing) "bing"
                         :else (recur s1 s2)))]
    (check-search search-google search-bing)))


core.async is much better at making a choice like that


futures are not good at it


didn't get into core.async yet, but i will


without pulling in core.async you might want to use a juc queue


or maybe an arrayblockingqueue of size 1


each search future offers the result to the queue, then the check-search takes one


which I guess would be the same as using a promise


i see. this is more a problem of parallelism kind right?


no, the problem is futures don't have a good way to choose between them


ideally you'd say something like (one-of (future ...) (future ...)) and get a future back that would have the value of whichever of the two completed first


but the future api (and the java interace it implements) doesn't provide a way to do that