This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-01-25
Channels
- # announcements (3)
- # asami (63)
- # babashka (5)
- # babashka-sci-dev (32)
- # beginners (56)
- # calva (2)
- # cider (28)
- # clj-commons (9)
- # clj-kondo (16)
- # cljdoc (41)
- # cljs-dev (19)
- # clojure (67)
- # clojure-europe (15)
- # clojure-nl (1)
- # clojure-poland (1)
- # clojure-uk (2)
- # clojurescript (27)
- # community-development (10)
- # data-science (2)
- # datascript (8)
- # datomic (21)
- # events (3)
- # fulcro (54)
- # graalvm (18)
- # introduce-yourself (2)
- # juxt (3)
- # lsp (6)
- # music (1)
- # nextjournal (8)
- # off-topic (44)
- # omni-trace (1)
- # reitit (13)
- # releases (3)
- # rewrite-clj (4)
- # shadow-cljs (10)
- # spacemacs (6)
- # sql (12)
- # tools-build (17)
- # tools-deps (3)
- # web-security (1)
I've defined a macro, which seems to be correct at compile-time:
(defmacro expand [tag pnames err-check-fn err-check-msg fmt fmt-args]
`{:tag ~tag :exp-fn (fn [ss rw]
(let [~pnames (:params rw)]
(if (not (apply ~err-check-fn rw))
[(error (apply ~err-check-msg rw))]
(apply (partial pp/cl-format nil ~fmt) ~fmt-args))))})
However, when I try to expand it, as so:
(expand "Foo" [a b & c] (fn [x] (= 3 (count x))) (fn [x] (str "Bad " (:fn-name x))) "~a, ~a, and ~a" (a b c))
I get the following error:
Syntax error macroexpanding clojure.core/fn at (c:\Users\fadrian\Projects\mercator\src\main\clojure\expander.clj:15:1). (expander/ss expander/rw) - failed: Extra input at: [:fn-tail :arity-1 :params] spec: :clojure.core.specs.alpha/param-list expander/ss - failed: vector? at: [:fn-tail :arity-n :params] spec: :clojure.core.specs.alpha/param-list
It seems not to be liking the (:params rw) following the ~pnames. However, I'm passing in a well-formatted binding list for pnames and rw is defined as a parameter to the expansion function defined in the macro. If it's any help, I'm ultimately want to call the expansion function as follows:
(apply <exp-fn> nil {:tag "Foo" :fn-name "Bar" :params [1 2 3 4]}
Any ideas why I'm getting this error at macro-expansion time and how to get around it?You didn't provide any error, but (a b c)
in ~fmt-args
will become just that - an unquoted list. It will be interpreted as a call of function a
on arguments b
and c
.
I edited the message to include the error. I would assume that the apply in the last line would treat the list at the end position as a list or at least throw an error telling me that the result of (a b c) was undefined. I don't get that kind of error (probably because the code seems to be dying in the binding step before that).
I've also taken out everything downstream of the binding and replaced it with a nil. I'm still getting the same error so it has to be something in the let expansion when I run the macro.
Ah, right. Use macroexpand-1
or macroexpand
on your usage of (expand ...)
and you should see that the argument vector in that inner (fn [ss rw] ...)
will become something like [my.ns/ss my.ns/rw]
. The syntax quote adds namespaces to all symbols. You gotta quote-unquote the symbols in the signature, like so: ~'ss
.
I've simplified the function once again to:
(defmacro expand [tag pnames]
`(fn [~'ss ~'rw]
(let [~pnames (:params ~'rw)]
(list a b c))))
However when I try to expand it using (expand "Foo" [a b & c]) it's telling me it can't find expander/a. I've tried removing the namespace for the a, b, and c references using ~', and when I do that the macroexpansion executes, but when I try to run it using (apply (expand "Foo" [a b & c]) nil {:params [1 2 3 4]}), the results are (nil nil nil) rather than the expected (1 2 (3 4)). For some reason it seems not to be seeing the bindings declared in the let or it's looking in the wrong namespace for them.According to macroexpand, the macro expands to this: (fn* ([ss rw] (clojure.core/let [[a b & c] (:params rw)] (clojure.core/list a b c)))). So I don't see why the list isn't picking up the bindings.
Such questions are more appropriate for #beginners
In short, if you have (defn x [a b c] ...)
then you either use (x 1 2 3)
or (apply x [1 2 3])
. The latter is only useful when you already have a collection - otherwise just use the former, without apply
.
is there a way to turn a java object into a clojure map (say, at least all properties, and their subproperties etc...)
e.g. if I have Foo that has property bar of type Bar which itself has a property baz of, then get something like {:Foo {:Bar {:baz :some-value}}}
The goal is to easily explore the structure of a java object
there are two options - bean
will use java bean properties to give you a map view
and ^^ for a fancier version (with two-way support)
(jinx - you owe me a coke 🙂 )
for sure :)
@UGH9KH1DF If you’re exploring, you might also be interested in clojure.inspector/inspect-tree
.
Is there a better/shorter way to write:
(def m {"6bcefce3b9" :ready, "28b63dc81b" :not-ready}
(into {} (for [[k v] m] [k (if (= v :ready) :starting v)]))
Basically, return the map with one value switched.@U0232JK38BZ A bit overkill but you can make it work with map-invert
and rename-keys
.
Just like this https://viebel.github.io/klipse-embed/?src=KGRlZiUyMG0lMjAlN0IlMjI2YmNlZmNlM2I5JTIyJTIwJTNBcmVhZHklMkMlMjAlMjIyOGI2M2RjODFiJTIyJTIwJTNBbm90LXJlYWR5JTdEKSUwQSUwQSgtJTNFJTIwbSUwQSUyMCUyMGNsb2p1cmUuc2V0JTJGbWFwLWludmVydCUwQSUyMCUyMChjbG9qdXJlLnNldCUyRnJlbmFtZS1rZXlzJTIwJTdCJTNBcmVhZHklMjAlM0FzdGFydGluZyU3RCklMEElMjAlMjBjbG9qdXJlLnNldCUyRm1hcC1pbnZlcnQpJTBB&lang=clojure
That is the name of the game for this type of thing. Possibly nicer to extract some update-keys
style function. But this strikes me as a “figure out how you are using and accessing you data and make a datastructure that makes that easy” type situation
yeah that^ . Otherwise you can use reduce-kv to at least stop iterating on values once you found/updated the one you want
I meant change all values of x to the value of y. Not one specific value. But I see what you mean. It was more a question of style than performance.
Of course, as the number of entries approaches some number my approach will become inneficient.
Those look like ids and states. If you are working on the resources named by the id, you should have the set of them and then just update that set of keys so that their states are :starting
. Perhaps you could also change your datashape to be {:ready #{ids ...} :not-ready #{ids ...}}
and then you (start-resources (:ready m))
, have that return the ids that successfully started and those that failed, and then update your m
map accordingly?
Thanks for the suggestion. That makes other lookups more time-consuming though ("find the state of job x"): I'd need to search each set
Hi folks, I googled this a bit, but I am not sure about it, basically can I (comp identity ...)
and pass that as xform
to (into [] ...)
? Is identity
a valid transducer?
there's some backscroll in this slack about it @richiardiandrea
well I have a function that accepts an optional xform
so I was basically wondering if I could default to identity
so it will have to be comp
-ed with a transducer (which is the initial transformation I have to do on top of the optional xform
in input)
Any conventions you all follow for naming partially applied functions?
Funnily enough, I'm looking at a clojure style guide right now that prefers partial
over anonymous fns
. I guess the fn itself doesn't have to be partial, so I don't have to specify it as parital
in my function sig
seems like a weird style guide. partial
is less useful since it only works for fns that you are apply from the left
You mean https://guide.clojure.style/#partial? Yeah I’m no particularly partial to partial either. Maybe that part of the style guide could use a tweak. It’s a https://github.com/bbatsov/clojure-style-guide/issues.
the keyword function will allow you to turn any string into a keyword, and they will all work fine like any other keyword, except when it comes to printing and reading
clojure.data.json and cheshire both have options for doing it or not (I think they have different defaults)
Yeah, I'm using cheshire. I want to avoid keywords that won't be readable/printable, so I'd like to check what's allowed. I vaguely remembered that the keyword?
function is much broader in what it accepts than the reader
user=> (keyword (String. (doto (byte-array 5) (#(.nextBytes (java.util.Random.) %)))))
:f��5�
user=> (keyword (String. (doto (byte-array 5) (#(.nextBytes (java.util.Random.) %)))))
:@
user=> (keyword (String. (doto (byte-array 5) (#(.nextBytes (java.util.Random.) %)))))
:��I
user=> (keyword (String. (doto (byte-array 5) (#(.nextBytes (java.util.Random.) %)))))
:V���G
user=> (keyword (String. (doto (byte-array 5) (#(.nextBytes (java.util.Random.) %)))))
:+�o�
user=>
the keyword function well let you make a keyword object out of any string, it just won't print in a way that is guaranteed to print in way that is readable, or round trip through the reader
the reader docs https://clojure.org/reference/reader#_literals talk about what is allowed for keywords, but in practice the reader is even more relaxed about it
and to preemptively answer this question https://clojure.org/guides/faq#unreadable_keywords