This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-10-23
Channels
- # 100-days-of-code (2)
- # aws (1)
- # beginners (105)
- # boot (36)
- # calva (4)
- # cider (56)
- # clara (37)
- # cljdoc (16)
- # cljs-dev (19)
- # clojure (44)
- # clojure-dev (20)
- # clojure-italy (24)
- # clojure-nl (3)
- # clojure-serbia (2)
- # clojure-spec (15)
- # clojure-uk (44)
- # clojurescript (41)
- # code-reviews (3)
- # core-async (12)
- # cursive (24)
- # datomic (4)
- # emacs (1)
- # figwheel-main (10)
- # fulcro (168)
- # funcool (2)
- # hyperfiddle (15)
- # jobs (2)
- # jobs-discuss (79)
- # juxt (19)
- # lein-figwheel (1)
- # leiningen (2)
- # luminus (14)
- # mount (8)
- # nrepl (9)
- # off-topic (9)
- # other-languages (1)
- # pathom (32)
- # reitit (6)
- # ring-swagger (3)
- # shadow-cljs (10)
- # slack-help (11)
- # spacemacs (20)
- # sql (29)
- # tools-deps (28)
- # vim (29)
- # yada (4)
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
(node
(fn [~ctx-sym {:keys ~dep-keys} ~@args-binding] ~call-form)
~@(map
(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 ...)
also a tip, you can convert the varargs into a map in the signature: [... & {:as dep-pairs}]
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
(node
(fn [~'ctx {:keys ~dep-keys} ~@args-binding] ~call-form)
~@(merge
(map
(fn [[dep-key dep-call]]
{dep-key `(fn [~'ctx ~@args-binding] ~dep-call)})
(apply hash-map dep-forms)))))))
and
(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?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))]))
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?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))}
...)?
without an accumulator, you can’t make this a loop/recur
and probably you want to build from the bottom, not from the top
(defn tree [xs]
(loop [r nil
t (reverse xs)]
(let [[k & ks] t]
(if k
(recur {:key k :children r} ks)
r))))
r is the accumulator, t holds the remainder of the keys to look at. whether k exists is your termination check.
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?
I don’t know about the rationale, but check out https://clojuredocs.org/clojure.core/remove-method and https://clojuredocs.org/clojure.core/remove-all-methods
@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
Let me be more explicit
user=> (defmulti foo (constantly :a))
#'user/foo
user=> (defmethod foo :a [_] :a)
#object[clojure.lang.MultiFn 0x1506f20f "clojure.lang.MultiFn@1506f20f"]
user=> (foo 1)
:a
user=> (defmethod foo :b [_] :b)
#object[clojure.lang.MultiFn 0x1506f20f "clojure.lang.MultiFn@1506f20f"]
user=> (foo 1)
:a
user=> (defmulti foo (constantly :b))
nil
user=> (foo 1)
:a
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))))
and
user=> (defmulti foo (constantly :a))
#'user/foo
user=> (.hasRoot #'user/foo)
true
user=> (clojure.core/instance? clojure.lang.MultiFn @#'user/foo)
true
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 wayThis is exactly what I am asking: why defmulti
has not be intended to be used that way?
I found this thread, which talks about it a bit: https://groups.google.com/forum/#!topic/clojure/MXf8zWAv3vI . I’m sorry I haven’t answered your question
It’s mentioned in that thread - if you have defmethods defined across multiple files, a reload will kill the dispatch table.
That is, a multi method has global state and reloading locally loses state created from other places
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
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)
(concat
(->> pinned-entities (sort-by :created-at))
(->> non-pinned-entities (sort-by :created-at)))
(concat
(->> 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.
I think I somehow fucked up my clojure.data 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’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
, https://github.com/clojure/clojure/blob/master/src/clj/clojure/data.clj#L33
any chance you were in the namespace of clojure.data 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.
@U0C7D2H1U: I was only aware of private functions that end with "-" so I did not have this issue in mind...
When I tested it yesterday it all worked. I now have copied the code from the clojure.data 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))))
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
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
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."
[search-term]
{: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]
(cond
(realized? google) "google"
(realized? bing) "bing"
:else (recur s1 s2)))]
(check-search search-google search-bing)))
oh, thanks
didn't get into core.async yet, but i will
https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/TransferQueue.html#tryTransfer(E)
i see. this is more a problem of parallelism kind right?