This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-06-15
Channels
- # announcements (2)
- # aws (1)
- # beginners (200)
- # calva (136)
- # cider (32)
- # clj-kondo (1)
- # cljs-dev (14)
- # cljsrn (19)
- # clojure (147)
- # clojure-argentina (1)
- # clojure-dev (144)
- # clojure-nl (2)
- # clojure-spec (14)
- # clojure-turkiye (1)
- # clojure-uk (1)
- # clojurescript (7)
- # data-science (1)
- # datomic (5)
- # duct (6)
- # figwheel (2)
- # fulcro (4)
- # graalvm (10)
- # graphql (1)
- # immutant (1)
- # joker (4)
- # off-topic (30)
- # om (1)
- # re-frame (11)
- # reagent (2)
- # reitit (4)
- # shadow-cljs (63)
- # testing (1)
- # tools-deps (7)
Do you mean, if a key k is in a map m, you want the return value to be a map m2 that contains only the key k and its associated value?
select-keys
can do that, and more.
user=> (select-keys {:a 1 :b 2 :c 3} [:b])
{:b 2}
user=> (select-keys {:a 1 :b 2 :c 3} [:b :a])
{:b 2, :a 1}
It returns an empty map (0 keys) if none of the given keys are found in the map.
if you use not-empty
that empty map becomes nil (useful for conditionals)
(ins)user=> (not-empty (select-keys {:a 0} [:b]))
nil
(ins)user=> (not-empty (select-keys {:a 0 :b 1} [:b]))
{:b 1}
yeah was using find but I knew clojure had something better built-in for this case, I noticed this is a very common thing to do (the select-keys
one)
Not sure if you are familiar with the Clojure cheat sheet, but it tries to organize the core Clojure functions by what they do, and/or what kind of data they operate on: https://jafingerhut.github.io Also the version I linked shows doc strings when you hover the cursor over a function name.
If you choose one of the ClojureDocs variants, clicking on a function name takes you to the http://ClojureDocs.org page on the function, with user-supplied examples or comments/gotchas.
yes, knew about this one https://clojure.org/api/cheatsheet, very helpful but stop using it thinking I'm going to missed some more obscured but useful stuff, is there a list somewhere of what its not included?
There is such a list, but, well, it isn't categorized and organized 🙂
This list is about a year old, so might be missing some things from Clojure 1.10 -- don't remember when that came out relative to May 2018 when this list was generated: https://github.com/jafingerhut/clojure-cheatsheets/blob/master/src/clj-jvm/TODO.txt#L207-L424
I guess I could publish that list a little more prominently for folks that feel like they might be missing out, but really I expect that most of what you are missing out on are the common useful Clojure and Java libraries, which that cheatsheet isn't even trying to cover.
quickly skimming looks like read and read-string are in both (the cheatsheet and exclude list), I'll see if can try cleaning it up a bit
The versions of read
and read-string
in the cheatsheet are only the clojure.edn versions, by design. I wanted to make it harder to find and use the more dangerous ones in clojure.core
Or maybe I'm wrong on that. It shows the version in the 3rd party lib clojure.tools.reader, but not the ones in clojure.edn? That seems a bit odd to me.
Yeah, I should add the ones in clojure.edn namespace, too.
was spending more time here https://clojure.github.io/clojure/clojure.core-api.html
No problem. If you find useful stuff missing, or mis-organized, feel free to create an issue on the Github repo, or drop me a line somehow
It does mention a few third party libraries that are not part of Clojure itself, but is nowhere near extensive in that regard. The Clojure Cookbook is something I've been going through recently, and it is fairly good for that kind of thing for a lot of tasks.
`select-keys' is just showed onced inside some example (not related to maps) in 'clojure programming' and zero in 'programming clojure'
Not trying to worry you here, but I just added clojure.edn/read and /read-string to my version of the cheatsheet (the http://clojure.org version is updated less often -- should do that soon-ish). I also updated the list of symbols not in the cheatsheet, and it is now 400+ long.
yes. Grimoire was started at a time when clojuredocs had not updated to the latest Clojure version for a while, and now ClojureDocs is up to date again and Grimoire may be no longer maintained (unless someone volunteers)
(clojure.repl/doc secretsmanager/describe-secret)
-------------------------
amazonica.aws.secretsmanager/describe-secret
([describe-secret-request])
wat .. what am I supposed to make of this? what is a describe-secret-request
?https://cljdoc.org/d/amazonica/amazonica/0.3.143/api/amazonica.aws.secretsmanager#describe-secret ... not any better
guessing my way to success ¯\(ツ)/¯
Well, it's secret .... 🙂
More seriously, perhaps the authors of amazonica were expecting that people would look up the docs for the corresponding AWS APIs, presumably documented elsewhere?
I do not know how Amazonica relates (if at all) to the aws-api library published by Cognitect, but you can take a look there to see if its docs are any nicer: https://github.com/cognitect-labs/aws-api/
@andy.fingerhut: I started with aws-api
, but I'm having to backtrack because it doesn't seem to play nice with jdk8, which is a constraint for me atm
new question: I made a spec (s/def ::stage string?)
... but I'd like to more specifically constrain the value to either be one of PENDING
, CURRENT
, or PREVIOUS
... is there a special spec
-y way of defining this?
I believe one way this is done is to spec it as a set of the values you wish to allow, e.g. #{"PENDING" "CURRENT" "PREVIOUS}
Sets in Clojure can be "called" as if they were functions, and return the first argument if it is in the set, else nil.
nice, that works, thanks!
what's the idiomatic way to store and share s/def
s?
I imagine I can't ::refer
to them across namespaces
specs are referenced just by name.
As long as the namespace in which they are defined has been required, you can access them by name.
Does that help @johnjelinek?
as long as they're not overloaded in the current namespace, right?
specs use qualified keywords -- they're unique
:foo/bar
regardless of namespace, for example.
If you have (s/def ::bar ...)
then it'll be :whatever.this.namespace/bar
so it'll be unique to the namespace it is defined in.
so, if you have a (s/def ::secret-id string?)
and that's a common primitive you want to use in other (s/def...
)s, would you put that in a common namespace and have everything use the qualified name? :wat/secret-id
?
Specs don't have to correspond to namespaces. They're "just" qualified keywords.
It's just a convenience to use existing namespaces as those qualifiers sometimes.
(defn -handleRequest [this is os context]
(let [w (io/writer os)]
(-> (util/json-is->map (io/reader is))
(pprint)
[{::secret-id :secret-id
::client-request-token :client-request-token
::step :step}]
(route-event)
(json/write w))
(.flush w)))
is this how I would destructure and map properties off a map into qualified keywords?hmm ... I suppose not
yea, it doesn't like this:
(let [{::secret-id :secret-id} event]
::secret-id)
it'll take this though:
(let [{secret-id :secret-id} event]
secret-id)
hmm ... this doesn't work though:
(defn -handleRequest [this is os context]
(let [w (io/writer os)]
(-> (util/json-is->map (io/reader is))
(pprint)
(fn [e]
(let [{id :secret-id
token :client-request-token
step :step} e]
{:spec/secret-id id
::client-request-token token
::step step}))
[{::secret-id :secret-id
::client-request-token :client-request-token
::step :step}]
(route-event)
(json/write w))
(.flush w)))
(let [{:prefix/keys [foo bar] ...] ...)
That will match :prefix/foo
and :prefix/bar
.
You can't (->
into a (fn [e]
form. ->
is a pure syntax transform
You really need to get into the habit of experimenting with small forms in the REPL and macroexpand
ing things.
The above code makes no sense at all with ->
and you'd see that if you developed a REPL-based workflow.
I really do recommend Eric Normand's online course REPL-Driven Development. Definitely worth paying for.
Part of the problem with code like that is if you ->
a value into a pprint
call, you get nil
out. This is why you need to work with expressions in the REPL more.
can you linkme to the course? I'll buy it!
Nearly 30 lessons and about 8-9 hours of video. Totally worth it.
nice!
(clojure.repl/doc kashoo/refresh-bearer-token)
dal.kashoo/refresh-bearer-token
([credentials])
refresh kashoo bearer token
Spec
args: (keys :req [:dal.kashoo/client-id :dal.kashoo/client-secret :dal.kashoo/refresh-token])
ret: map?
but then when I call it, it's failing {:pre [(s/valid? ::credentials credentials)]}
(let [api-key ...
credentials {:kashoo/client-id (:client_id api-key)
:kashoo/client-secret (:client_secret api-key)
:kashoo/refresh-token (:refresh_token bearer-token)}]
(kashoo/refresh-bearer-token credentials))
I've already required [dal.kashoo :as kashoo]
☝️
so, why is this failing to validate?
Assert failed: (s/valid? :dal.kashoo/credentials credentials)
::kashoo
instead of :kashoo
.
thanks
You want ::
so it resolves the alias.
I thought I could only do ::
if I'm in the namespace where it's qualified
::g
will resolve to the current namespace.
(I'm actually getting all the pastes above from my REPL where I'm trying it)
But you're not trying it in small enough pieces. That's the habit you need to develop.
To be productive in Clojure you need to start thinking at the level of the form, not the function, not the file.
And you need to get used to evaluating small pieces.
In the above case, the spec says you need :dal.kashoo/client-id
which :kashoo/client-id
is not -- and you can verify that at the REPL.
But you can also verify that with your kashoo
alias, ::kashoo/client-id
would resolve to :dal.kashoo/client-id
And I'd also recommend Stu Halloway's Debugging With The Scientific Method and Running With Scissors.
is that a book?
Another online presentation.
It's a very different workflow from other languages. But it's very tangible. Everything is available in the REPL. Docs, source, tests, exploration.
is the size of this function hairy?
(defn- create-secret []
(let [bearer-token (-> (secretsmanager/read-secret {:spec/secret-id dev-kashoo-bearer-token})
(get :secret-string)
(util/json->map))
api-key (-> (secretsmanager/read-secret {:spec/secret-id (:masterarn bearer-token)})
(get :secret-string)
(util/json->map))
credentials {::kashoo/client-id (:client_id api-key)
::kashoo/client-secret (:client_secret api-key)
::kashoo/refresh-token (:refresh_token bearer-token)}
access-token (kashoo/refresh-bearer-token credentials)
bearer-token (merge bearer-token {:access_token (:access_token access-token)
:refresh_token (:refresh_token access-token)})]
bearer-token))
Way too big. And way too imperative.
That should be four or five small functions -- that represent expressions you can easily test in the REPL.
ok, so, basically make each of those let
assignments map to a single function
but that wouldn't fix the imperative piece
Sure, there's going to be some imperative piece at the top of the call chain.
But you want each piece to be easy to eval/test in the REPL.
That way you catch problems quickly and easily -- and early.
It's also probably not a good idea to bind bearer-token
twice in that let
.
(assuming you're familiar with a strictly TDD workflow 🙂 )
yea, I'm familiar -- we have a watcher that runs our golang test suite on every file save
Ugh. No.
That's a horrible workflow 😞
takes 30 seconds every time we make a change to see red or green
But it's what people get used to in other languages as the "best they can do"
30 seconds is a LONG time.
Hahaha...
Feedback should be instantaneous.
Write an expression, eval, see the result. Instantly. Over and over again.
Those first two strings are screaming out for a function.
(defn read-secret [data]
(-> (secretsmanager/read-secret {:spec/secret-id data})
(:secret-string)
(util/json->map)))
Naming is hard 🙂
But, yeah, small functions. Easily testable in the REPL. Do just one thing. Compose the results.
I did this one:
(defn read-secret-string
"read-secret-string only returns the secret-string of a secret"
[secret]
{:pre [(s/valid? ::secret secret)]
:post [(s/valid? map? %)]}
(-> (read-secret secret)
(get :secret-string)
(util/json->map)))
and refactor:
(defn- create-secret []
(let [bearer-token (secretsmanager/read-secret-string {::spec/secret-id dev-kashoo-bearer-token})
api-key (secretsmanager/read-secret-string {::spec/secret-id (:masterarn bearer-token)})
credentials {::kashoo/client-id (:client_id api-key)
::kashoo/client-secret (:client_secret api-key)
::kashoo/refresh-token (:refresh_token bearer-token)}
access-token (kashoo/refresh-bearer-token credentials)
bearer-token (merge bearer-token {:access_token (:access_token access-token)
:refresh_token (:refresh_token access-token)})]
bearer-token))
that's cool ... input-level destructuring
I think merge
is not the right thing to use here:
(merge
(map-kashoo-credentials {:client_id "a" :client_secret "b"})
(map-kashoo-credentials {:refresh_token "a"}))
; =>
#:dal.kashoo{:client-id nil, :client-secret nil, :refresh-token "a"}
function:
(defn- map-kashoo-credentials
[{:keys [client_id client_secret refresh_token]}]
{::kashoo/client-id client_id
::kashoo/client-secret client_secret
::kashoo/refresh-token refresh_token})
Your problem is you are creating maps with nil
values and overwriting non-`nil` values.
instead do
(merge
#:dal.kashoo{:client_id "a" :client_secret "b"}
#:dal.kashoo{:refresh_token "a"})
Or just
(merge
#::kashoo{:client_id "a" :client_secret "b"}
#::kashoo{:refresh_token "a"})
(again, try this stuff in REPL -- which is what I just did)
user=> (alias 'kashoo (create-ns 'dal.kashoo))
nil
user=> (merge #::kashoo{:client_id "a" :client_secret "b"} #::kashoo{:refresh_token "a"})
#:dal.kashoo{:client_id "a", :client_secret "b", :refresh_token "a"}
user=>
yea, but the problem I have is that each of those keywords come from different maps, so I'm trying to bring them together by taking a subset of each one and then merge
i.e:
(let [credentials {::kashoo/client-id (:client_id api-key)
::kashoo/client-secret (:client_secret api-key)
::kashoo/refresh-token (:refresh_token bearer-token)}])
select-keys
gives you a subset of a map's keys.
Break things down. Merge the keys you want from various maps. Then qualify them if you need to.
aight, I think I'm done for the night -- it's 1am here
As @deleted-user suggested, it's easy to walk over an entire map and qualify all the keys -- once you have the merged subset of keys you want.
thanks for all your help @seancorfield + @deleted-user
Hi guys, cider jack in today gives this error:
[nREPL] Starting server via /usr/local/bin/lein update-in :dependencies conj \[nrepl\ \"0.4.5\"\] -- update-in :plugins conj \[cider/cider-nrepl\ \"0.19.0-SNAPSHOT\"\] -- repl :headless :host localhost...
error in process sentinel: nrepl-server-sentinel: Could not start nREPL server: Exception in thread "main" java.lang.RuntimeException: Unable to resolve symbol: create in this context, compiling:(/private/var/folders/g_/ldc4hkys4r73yffyf77pcn1c0000gn/T/form-init7254150147707415858.clj:4223:33)
Any idea why this happens? Used to be fine till yesterday so I'm guessing some update somewhere?map is lazy and returns a listy thing (a seq). mapv is eager (consumes it from beginning to end right now) and returns the results in a vector
Sometimes you'll see mapv used not because people actually want the result to be a vector but because they just want an ordered result that computes eagerly
Since you asked for the use case. It's mostly when you need side effect and return value
In some cases, mapv can also be faster. If you are going to use the full result set, mapv creates less intermediate result, and should end up being a little faster. The difference will really only show on massive collections.
Hi! i was searching for a online/link documentation about the ^
symbol used in a def
Anyone know what this symbol :>
means here? https://github.com/metosin/komponentit/blob/01069d1f0c02ccb1209dfde9f504c3b5e3475800/example-src/cljs/example/transitions.cljs#L34
@bronsa do you know any real use case of metadata ? maybe an example in some projects. tia!
It is used to give hints and directives to the Clojure compiler. You can type hint with it for example. You can specify that some Var should be dynamic or constant. That they shouldn't be re-defed on reload.
It is also used for meta info. For example, the doc-string is stored as metadata. That's why it is available at runtime with the doc
function
you could have a def'd version somewhere. (defn more-than-one [c] (> (bounded-count 2 c) 1))
and then (filter more-than-one coll)
seems pretty elegant to me. I don't see anything wrong with your current implementation, provided you don't have any huge (or infinite) lazy seqs going through it.
is there a way to print a dependency tree (similar to lein deps :tree
) using deps.edn?
Geesh, there it is right in the docs and I totally missed it, thanks 😊
is there a way to place a tagged literal into a data structure dynamically?
so in Honeysql you can define a query like :
{:select [#sql/inline 5]}
where #sql/inline
is a predefined tagged literal that has meaning for that library but I don't know how to dynamically insert oneMaybe "how to add a tagged literal to a data structure at run time?" is a better way of asking the question?
can you ignore the tagged literal and show me how you intend to update a datastructure at runtime and then we can figure out how to integrate that with the tagged literal?
The data structure is defined via incoming json, I'm walking over the structure and making several conversions, one conversion I want to make is string of "#sql/inline" to tagged literal of #sql/inline
I think there's more going on in the reader than I assumed, consider the syntax
[#sql/inline 5]
that ends up being (f 5) just because they're next to each other (I think?), seems like I'll have to resolve to the function like you showed and parse at a higher level then per item?I've seen syntax like this too,
[#sql/call [:+ 1 2] "not part of the call"]
Yeah that’s the lookup using the data readers Need to go through the readers map so if the lib changes it in the future it still works
yep the only thing I didn't realize is a tagged literal is like a pair, a function and its argument consider
#inst "2019-06-06"
but #inst
on its own doesn't work, so upstream I need to reconsider how I pass this information maybe something like '{":select" : ["#sql/call [\":+\", \"1\", \"1\"]"]}', then when I walk over the call I have the arguments in the same placeotherwise my walk needs to be context aware, which I think is possible too but not keen on lots of scanning
So #sql/whatever
is going take the string the right and convert it. You should just be able to call read-string if a fn is registered for it. But otherwise you could just run the function that backs whatever
on the string and you'll get the desired thing returned
if you need more than that, tagged literals just invoke a function. Its defined in data_readers.clj in honeysql/resources. And it just calls the function honeysql.types/read-sql-inline
which just wraps the value in a record
Ah I see, I could translate directly to the function call
let me look to see if there's a way to get that. presumably its in a map somewhere but you don't want to hardcode it to a particular function in case it changes in the future
Is there a function which given a seq will return its constructor function?
() => list
[] => vec
#{} => set
{} => hash-map
There is nothing wrong with using the file name core.clj
in your project, but there is, I think, a distinct disadvantage: In stack traces, only the last component of source file names appears, not the full path. It is very common for functions in the clojure.core namespace to have core.clj
as their file name. If you use core.clj
, then your functions will show in stack traces with that file name, too.
aha, sounds important for when debugging an issue but no idea how much of a trouble it would be since I haven't ran into that situation.
@hi135 I am not aware of such a function. There is empty
, which given any collection, will return an empty collection of that same kind. That may not be useful for your purposes, though.
user=> (def make-from (fn [coll] (fn [els] (into (empty coll) els))))
#'user/make-from
user=> ((make-from [1]) '(1 2))
[1 2]
@dpsutton I'm playing around reimplementing map:
(defn map*
[f coll]
(reduce* (fn [acc x] (conj acc (f x))) [] coll))
The problem is that I always return vector
, which means that given a list or a set, I change the type. Passing (empty coll)
to reduce works for sets and vectors, but list is reversed (since conj).and what i'm getting at is that map doesn't preserve the input type. (set? (map identity #{:a :b}))