clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
lilactown 2021-03-26T02:16:40.459200Z

(defmacro uh-oh []
  (let [id (gensym "id")
        m {:id id}]
   m))

(uh-oh)
;; Syntax error compiling at (*cider-repl Code/flex:localhost:50966(clj)*:35:7).
;; Unable to resolve symbol: id20798 in this context
any tricks to emit data with a symbol in it from a macro?

lilactown 2021-03-26T02:17:21.459600Z

ah I unqoute then quote it

lilactown 2021-03-26T02:17:53.460200Z

(defmacro uh-oh []
  (let [id (gensym "id")
        m {:id id}]
   `'~m))

(uh-oh)
;; => {:id id20812}

lilactown 2021-03-26T02:19:17.460700Z

ah but that prevents eval of any of m:

(defmacro uh-oh
  [f]
  (let [id (gensym "id")
        m {:id id :f f}]
   `'~m))

(uh-oh (fn [] "foo"))
;; => {:id id20820, :f (fn [] "foo")}

phronmophobic 2021-03-26T02:20:23.461100Z

(defmacro uh-oh []
  (let [id (gensym "id")
        m {:id (list 'quote id)}]
   m))
There's several ways to quote the symbol. I find the (list 'quote my-sym) idiom is easiest for me to follow.

lilactown 2021-03-26T02:43:03.461700Z

aha! thank you

dpsutton 2021-03-26T02:45:43.461900Z

(defmacro foo [] {:id 'id#})`

dpsutton 2021-03-26T02:46:16.462300Z

the backquote is there before the map but it's quite confused the parser

lilactown 2021-03-26T03:06:08.463200Z

see above why backqouting the whole map doesn't quite work

dpsutton 2021-03-26T03:07:49.463400Z

oh i see

imre 2021-03-26T11:43:49.465400Z

Is there any guidance on when one should use simple/qualified keywords?

โž• 1
imre 2021-03-26T11:44:58.465600Z

for example {:my.company/errors [:timeout vs :my.company/timeout]}

imre 2021-03-26T11:45:21.465800Z

Or even (s/keys :req [::foo]) :req being simple

teodorlu 2021-03-26T12:13:44.466100Z

My rules of thumb: 1. qualified keywords for extension mechanisms, so that you can register your own stuff under your own namespace and avoid conflicts 2. simple keywords for internal implementation details The argument against qualified keywords is mostly brevity.

teodorlu 2021-03-26T12:15:02.466300Z

I'd love to see seasoned Clojurians like @alexmiller and @seancorfield reply. Ideally, I believe this should be addressed in a rationale on http://clojure.org.

teodorlu 2021-03-26T12:17:39.466900Z

Perhaps here:

imre 2021-03-26T12:47:42.467100Z

Yeah, I was searching for something like that at first

imre 2021-03-26T12:47:58.467300Z

perhaps this is something for ask clojure

โž• 3
p-himik 2021-03-26T12:53:12.467500Z

IMHO the higher the probability of name clashing, the more reasons there are to use namespaces. In a controlled environment, like the confines of my own project, I almost exclusively use plain keywords. It also makes reusability much smoother if some entities are similar, like

(defn name-with-description [{:keys [name description]}]
  [:span {:title description} name])
The above can be used everywhere on entities that have :name and optionally :description. But in an uncontrolled environment, like an API, I think namespaced keywords make more sense, especially if it's something extendable.

๐Ÿ‘ 1
teodorlu 2021-03-26T13:01:58.467800Z

> perhaps this is something for ask clojure I'd give that questions as many upvotes as I can.

teodorlu 2021-03-26T14:11:18.468600Z

Unfortunately, I'm unable to provide more than a single upvote!

2021-03-26T14:11:39.468800Z

Itโ€™s a question of: 1. scoping 2. use case

2021-03-26T14:12:15.469Z

if the scope is the argument to a single function, then an unqualified keyword does no harm and is shorter to type

๐Ÿ‘ 1
2021-03-26T14:12:49.469500Z

if the use case is, โ€œkeys in a hashmapโ€ you always want namespaced keys

2021-03-26T14:13:13.469700Z

if the use case is values in a hashmap, you might want unqualified keywords

1
2021-03-26T14:13:25.469900Z

e.g. {:http/method :post} is fine

2021-03-26T14:13:57.470100Z

:post in this context is somewhat universal and is never intended as the key in a map, so it cannot clash

2021-03-26T14:15:52.470700Z

On the other hand, if youโ€™re developing a new library with new terms, you might consider a namespace qualifier. {:my/mode :my.mode/fast}

2021-03-26T14:16:35.471600Z

^ does little harm and is slightly easier to use tooling to search for

2021-03-26T14:17:12.472200Z

but the upside is comparatively marginal if :my.mode/fast cannot clash because itโ€™s always a value

aratare 2021-03-26T14:21:33.475Z

Hi there. So Iโ€™m playing around with spec and plumaticโ€™s schema at the moment and Iโ€™m not sure how to approach this. In Schema, you can have (s/set-fn-validation! true) run and any function schema I define thereafter will be checked automatically. I notice that in spec I can do (stest/instrument) but it only works if I call it after the spec has been defined. Is there a way I can turn instrumentation on first and define specs later? Alternatively, what is the most standard workflow to use spec when deving?

borkdude 2021-03-26T14:22:32.475600Z

@rextruong one way is to hook this in your (dev) component / integrant-like system, so when you restart it, the specs are instrumented

aratare 2021-03-26T14:25:21.476Z

So every time I define a new function spec Iโ€™ll need to restart my dev component you mean?

borkdude 2021-03-26T14:25:35.476200Z

yeah

borkdude 2021-03-26T14:25:44.476400Z

similar to routes and a web server I guess

aratare 2021-03-26T14:27:08.476600Z

Ah I see. Thatโ€™s indeed one way to do it.

aratare 2021-03-26T14:27:30.476800Z

Actually I may adopt that. Thanks ๐Ÿ™‚

kirill.salykin 2021-03-26T14:58:05.478700Z

Hi there is overloaded java method

serviceConfiguration(Consumer<S3Configuration.Builder> serviceConfiguration) 
serviceConfiguration(S3Configuration serviceConfiguration) 
how I can type hint param so correct method is picked? I am trying this - but no luck
(.serviceConfiguration ^S3Configuration (-> (S3Configuration/builder)
                                                             (.pathStyleAccessEnabled true)
                                                             (.build)))
https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3BaseClientBuilder.html please help thanks

imre 2021-03-26T15:00:52.478800Z

Thanks for the input!

kenny 2021-03-26T15:43:22.479200Z

A big downside to unqualified is the impact on refactors. We have a large code base. If someone uses an extremely common keyword (e.g., :type), you need to sort through hundreds of uses. It is very likely you'll miss one and potentially break the code in a very nuanced way (yay optional keys).

โž• 1
simongray 2021-03-26T15:46:47.479400Z

True, and editors like Cursive and probably other setups too allow refactoring namespaced keywords like vars

โœ”๏ธ 2
p-himik 2021-03-26T15:49:39.479600Z

The second overload of (.serviceConfiguration ...) that you need receives not one but two aguments - this and serviceConfiguration. You're providing only serviceConfiguration, thus replacing this with it.

kirill.salykin 2021-03-26T15:50:53.480Z

my bad it is a bit trickier

(cond-> (S3Client/builder)

             (and access-key secret-key)
             (.credentialsProvider (StaticCredentialsProvider/create
                                    (AwsBasicCredentials/create access-key
                                                                secret-key)))


             endpoint
             (-> (.serviceConfiguration ^S3Configuration (-> (S3Configuration/builder)
                                                             (.pathStyleAccessEnabled true)
                                                             (.build)))
                 (.endpointOverride (URI. endpoint)))

             true
             (.build))

kirill.salykin 2021-03-26T15:51:03.480200Z

so this seems be provided

kirill.salykin 2021-03-26T15:52:32.480400Z

and the error i recieve indicates that it expects Consumer

borkdude 2021-03-26T15:54:41.480600Z

I always wonder why tools.deps does :git/url but then :sha unqualified

โž• 2
alexmiller 2021-03-26T15:55:43.482200Z

The original idea was to have at least one key qualified so we could determine procurer type, but not the others for conciseness

alexmiller 2021-03-26T15:56:16.483Z

In retrospect, that was dumb

p-himik 2021-03-26T15:56:46.483400Z

Hmm, right. No idea why, sorry.

kirill.salykin 2021-03-26T15:56:54.483700Z

thanks

alexmiller 2021-03-26T15:56:55.483900Z

May allow both in the future

borkdude 2021-03-26T16:01:28.484100Z

Right now I'm designing a task runner "DSL". First I had:

{:task/type :shell (or some other task dispatch key)
 :task/args ...
 ... other task specific opts}
but this was getting too verbose. Since type + args were always there, I could just go with hiccup style:
[:shell { optional opts map } ... args ...]
But because every task can have some general options like :description I didn't want to put those in the task options, so I went with:
^{:description ...} [:shell { optional opts map } ... args ...]
Kind of like docstring meta. But that's also a bit weird maybe. So I'm considering:
[:shell {:task/description ...  other opts } ... args ...]
now where :task/* are general task opts which can never conflict with the other task specific opts because of the namespace... :thinking_face: .

๐Ÿ‘ 1
borkdude 2021-03-26T16:04:22.484300Z

Full example:

{:tasks {:clean [:shell "rm" "-rf" "target"]
         :uberjar [:shell "clojure" "-X:uberjar" ":jar" "foo.jar"]
         :all [:do {:task/description "Execute all steps"}
               [:clean]
               [:uberjar]]}}

๐Ÿ‘ 1
borkdude 2021-03-26T16:10:13.485100Z

It still feels a bit wrong though, to put the :task/description in that spot.

borkdude 2021-03-26T16:11:02.485300Z

well maybe it's ok, since in HTML you also put "id" etc on random HTML elements of different types

teodorlu 2021-03-26T16:21:18.485500Z

So you'd want to avoid conflicts between :clean, :shell, :uberjar, :do and the like? Some options come predefined (shell, clean, do), others are defined by the user (clean, uberjar)?

borkdude 2021-03-26T16:22:26.485700Z

Avoid conflicts between options passed to the tasks, e.g.:

[:shell {:task/description ... :shell/opts {...}}]
I don't necessarily want to namespace every single keyword

borkdude 2021-03-26T16:23:25.485900Z

yes, there is also the issue of user defined clashing with built-ins like :shell, but I don't like [:task/shell ....] [:task/babashka ....] etc, I think

teodorlu 2021-03-26T16:23:42.486100Z

Mhm, it gets too verbose quickly.

borkdude 2021-03-26T16:23:46.486300Z

instead users can choose namespaced keywords

borkdude 2021-03-26T16:24:21.486500Z

if there are clashes, or just choose a different name. The user-defined task will always be chosen first, so if they override, they just make something inaccessible for themselves, which is not the end of the world

๐Ÿ‘ 1
teodorlu 2021-03-26T16:24:31.486700Z

> instead users can choose namespaced keywords That might make sense, especially if one is referring more to the predefined symbols than ones own additions

borkdude 2021-03-26T16:25:06.487Z

it's a similar problem with overriding vars with local symbols in clojure

teodorlu 2021-03-26T16:25:16.487200Z

yeah, I was thinking the same thing.

teodorlu 2021-03-26T16:25:23.487400Z

I really like Clojure's namespacing mechanism.

teodorlu 2021-03-26T16:25:55.487600Z

And as long as you allow the user to override definitions, you won't break users if you add new symbols to the default environment

borkdude 2021-03-26T16:26:05.487800Z

exactly

โœ”๏ธ 1
borkdude 2021-03-26T16:28:29.488100Z

maybe I should add a way to always refer to the built-in, e.g. :task/shell will always refer to the built-in, no matter if you overrode (?) it with :shell, like clojure.core/name always refers to the built-in var

lassemaatta 2021-03-26T16:28:43.488300Z

(fyi: there's always https://github.com/cognitect-labs/aws-api which I found really nice to use)

teodorlu 2021-03-26T16:28:46.488700Z

I've make a mistake of "namespacing everything with too much cruft" before myself. I had just learned about namespaced keywords. I was designing a data interface with EDN, so I just namespaced everything. How did people like it? It was a hassle. Didn't help that I just used ::key-name in my Clojure file, so there were multiple layers in the namespace as well. I don't want to do that mistake again.

teodorlu 2021-03-26T16:29:13.489Z

> maybe I should add a way to always refer to the built-in I like that idea.

teodorlu 2021-03-26T16:29:52.490300Z

Could make reusing parts of task runner specifications less problematic.

wilkerlucio 2021-03-26T16:32:18.493900Z

I think using qualified keywords add a lot of leverage in your codebase, as mentioned before, its great for refactoring, but also for understanding the code base, a find usages in a qualified keyword can give you an accurate view about what that property means across the system (and how its being used). For enumerations is also great, in editors like Cursive, instead of going to look up in a documentation about what are the options, if you have something like :my.task.type/foo and :my.task.type/bar, on typing :my.task.type, you can see the options right there

borkdude 2021-03-26T16:33:05.494100Z

note that dots in the name part of keywords are not officially supported (if you have :my.task.type as a standalone keyword)

imre 2021-03-26T16:33:34.494800Z

I think he meant typing the namespace part will bring up auto-complete

wilkerlucio 2021-03-26T16:33:35.495100Z

I mean't more as a prefix for auto-complete, not as an actual keyword (the last one)

borkdude 2021-03-26T16:33:43.495300Z

right, my bad

teodorlu 2021-03-26T16:34:25.495500Z

@wilkerlucio I'm guessing you're speaking from experience? I'd love to hear examples / instances where qualified keywords have enabled nice workflows.

teodorlu 2021-03-26T16:34:44.495700Z

and also where you'd consider them not necessary.

wilkerlucio 2021-03-26T16:40:13.002700Z

I do a lot of fully qualified keywords in Pathom for example, and in Pathom I use a more hard-core attribute driven philosophy (and Pathom itself is an expression of this idea), so for example, in the planner (https://github.com/wilkerlucio/pathom3/blob/master/src/main/com/wsscode/pathom3/connect/planner.cljc) or runner (https://github.com/wilkerlucio/pathom3/blob/master/src/main/com/wsscode/pathom3/connect/runner.cljc) you can see a lot of keyword definitions at start. those keywords are mostly used inside the same namespace, but are not limited to it. so when I need get back on feet in some complex part of the code that I haven't touched in a while, I can see which keywords participate in the process, and by following their usages I can quickly remember all the places in which its used. I believe that having consistent property names (close the same way we regard our functions, as an independent being) enables consistent re-usage of these same keywords, and them their semantics can flow over the system

๐Ÿ’ฏ 1
wilkerlucio 2021-03-26T16:40:38.003700Z

about short keywords, I do use them as well, but I try to avoid most of the time

๐Ÿ‘ 1
teodorlu 2021-03-26T16:41:17.004100Z

Thanks for explaining!

wilkerlucio 2021-03-26T16:41:45.004600Z

hope it makes sense, I'm deep in this rabbit hole :P

teodorlu 2021-03-26T16:41:50.004800Z

haha

teodorlu 2021-03-26T16:43:08.005Z

(>def ::node-id "docstring" pos-int?)
i'm guessing does: 1. Document the "attribute" 2. "establish it" (don't use not-established qualified namespaces) 3. specs the value ?

wilkerlucio 2021-03-26T16:44:20.005900Z

correct, I'm using Guardrails, which is some syntax on top of spec, this doc is purely for the code reader (you can't access it at runtime)

๐Ÿ‘ 1
teodorlu 2021-03-26T16:45:34.006200Z

but you Tony Kay decied to deprecate >def? from https://github.com/fulcrologic/guardrails/blob/develop/src/main/com/fulcrologic/guardrails/core.cljc#L749-L755.

wilkerlucio 2021-03-26T16:49:01.007600Z

I wasn't aware of that, not sure why its on deprecate list (@tony.kay maintains Guardrails), gotta understand why

๐Ÿ‘ 1
๐Ÿคž 1
teodorlu 2021-03-26T16:51:44.008100Z

For the record, I've spent enough time guessing what "name" can actually be for JSON documents in codebases to be curious about a better way.

teodorlu 2021-03-26T16:52:43.008300Z

@imre sorry for derailing your thread ๐Ÿ˜…

imre 2021-03-26T16:53:35.008500Z

This is the sort of discussion I was hoping for ๐Ÿ˜„

๐Ÿ˜Š 1
imre 2021-03-26T16:54:04.008800Z

I might link this thread into the askclojure once it's available in the log

๐Ÿ‘ 1
wilkerlucio 2021-03-26T17:02:48.012700Z

@teodorlu on your "name", issue, that's a great case to start seeing it, unqualified names always require some context to understand, but a lot of times this context is implicit, and then its up for the reader to interpret it. being fare, its easy in a lot of cases, but the worst is when you are confident, but wrong at the same time. this is the kind of problem qualified keywords can eliminate, if they are big enough (same considerations as for functions), you can always be confident about their meaning, no need to know the context

๐Ÿ‘ 2
wilkerlucio 2021-03-26T17:04:00.014900Z

but its a hard battle, JSON dominates everything, and I find unlikely that the industry is changing in that direction anytime soon, so for those wanting this path, there is a lot of "naming expansion" to be done while interacting with external sources of data, but I believe in Clojure world we are much closer, and we could have a nice ecosystem of qualified names here

๐Ÿ‘ 2
borkdude 2021-03-26T18:24:01.015600Z

FWIW JSON totally accepts "foo/bar" keys

borkdude 2021-03-26T18:24:42.015900Z

GraphQL however, maybe not, I found their support for these kinds of things lacking in some respects when I checked it out some years ago. A step back from RDF.

โ˜๏ธ 2
๐Ÿ˜ญ 1
2021-03-26T19:18:11.016600Z

The whole "hard-core attribute driven philosophy" makes a ton of sense to me when I hear people talk about it but then I do tend to struggle with how exactly to implement it when I sit down to code something. Thus I can never get enough of these kinds of discussions ๐Ÿ™‚

โž• 1
kirill.salykin 2021-03-26T19:22:06.016800Z

aws-api rather limited in its possibilities anyway, Iโ€™d like to understand how to type hint param so clojure can be proper method

vemv 2021-03-26T20:01:56.017600Z

maybe I'd remove -> and use simple chains (or let). -> can introduce some uncertainty if you aren't intimately familiar with it as a second trick, you can println the class of the object being built. maybe it's not what you think it is and as a last resource, you could use java's reflection api directly instead of clojure interop syntax

kirill.salykin 2021-03-26T20:02:40.017800Z

ok, thanks

rgm 2021-03-26T20:34:20.018Z

My general conventions here are informed by a lot of re-frame use and Clojurescript having weaker support for namespace aliasing: 1. Internal to a namespace: unqualified generally 2. If itโ€™s being shared out (eg. a re-frame handler key) then it gets namespaced. 3. Auto-namespacing is only to be used for internal reference to a global thing (eg. the re-frame database โ€ฆ (assoc db ::data-belonging-to-this-ns ,,,) โ€ฆ ugly, but it helps to track down its provenance in tools like re-frame-10x 4. No auto-namespacing ever for anything intended to be shared. It breaks grep and :: looks too much like : when Iโ€™m tired. I know things like clojure-lsp and cursive can overcome this but I like to keep lowest-common-denominator outside-the-editor tools like grep working OK.

rgm 2021-03-26T20:37:56.018200Z

(I also would put in a big upvote for related guidance on the idea that code namespaces and keyword namespaces donโ€™t necessarily need to be conflated โ€ฆ when should they be kept in sync and when is it wiser to let them drift. IMO the conflation of the two leads to some pretty ugly and un-readable namespaced keywords. Sometimes I find a domain concept namespace hierarchy on keywords helpful and this doesnโ€™t necessarily match up cleanly to the location of code files in a directory tree).

wilkerlucio 2021-03-26T21:54:31.020200Z

Im really looking forward to https://clojure.atlassian.net/plugins/servlet/mobile?originPath=%2Fbrowse%2FCLJ-2123#prompt?redirectUri=%2Fbrowse%2FCLJ-2123, that will reduce the annoying part of big names, by making it easier to alias, specially for the pure domain cases

๐Ÿ’ฏ 4