This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-03-26
Channels
- # announcements (3)
- # architecture (53)
- # babashka (6)
- # beginners (101)
- # bitcoin (3)
- # calva (4)
- # cider (3)
- # clara (7)
- # cljdoc (2)
- # cljsrn (14)
- # clojure (104)
- # clojure-europe (96)
- # clojure-germany (21)
- # clojure-nl (6)
- # clojure-serbia (3)
- # clojure-spain (1)
- # clojure-uk (13)
- # clojuredesign-podcast (4)
- # clojurescript (14)
- # cursive (5)
- # data-science (19)
- # datomic (16)
- # emacs (15)
- # fulcro (33)
- # graalvm (5)
- # honeysql (3)
- # instaparse (2)
- # jobs (3)
- # lsp (82)
- # malli (2)
- # off-topic (11)
- # pedestal (4)
- # polylith (62)
- # practicalli (4)
- # shadow-cljs (56)
- # tools-deps (53)
- # vim (17)
- # xtdb (53)
(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?(defmacro uh-oh []
(let [id (gensym "id")
m {:id id}]
`'~m))
(uh-oh)
;; => {:id id20812}
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")}
(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.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.
I'd love to see seasoned Clojurians like @U064X3EF3 and @U04V70XH6 reply. Ideally, I believe this should be addressed in a rationale on http://clojure.org.
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.> perhaps this is something for ask clojure I'd give that questions as many upvotes as I can.
here ya go https://ask.clojure.org/index.php/10380/when-to-use-simple-qualified-keywords
if the scope is the argument to a single function, then an unqualified keyword does no harm and is shorter to type
:post
in this context is somewhat universal and is never intended as the key in a map, so it cannot clash
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}
but the upside is comparatively marginal if :my.mode/fast
cannot clash because it’s always a value
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).
True, and editors like Cursive and probably other setups too allow refactoring namespaced keywords like vars
The original idea was to have at least one key qualified so we could determine procurer type, but not the others for conciseness
In retrospect, that was dumb
May allow both in the future
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: .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]]}}
well maybe it's ok, since in HTML you also put "id"
etc on random HTML elements of different types
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)?
Avoid conflicts between options passed to the tasks, e.g.:
[:shell {:task/description ... :shell/opts {...}}]
I don't necessarily want to namespace every single keywordyes, 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
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
> instead users can choose namespaced keywords That might make sense, especially if one is referring more to the predefined symbols than ones own additions
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
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
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.
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
note that dots in the name part of keywords are not officially supported (if you have :my.task.type
as a standalone keyword)
I mean't more as a prefix for auto-complete, not as an actual keyword (the last one)
@U066U8JQJ I'm guessing you're speaking from experience? I'd love to hear examples / instances where qualified keywords have enabled nice workflows.
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
about short keywords, I do use them as well, but I try to avoid most of the time
hope it makes sense, I'm deep in this rabbit hole :P
(>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
?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)
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.
I wasn't aware of that, not sure why its on deprecate list (@U0CKQ19AQ maintains Guardrails), gotta understand why
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.
@U08BJGV6E sorry for derailing your thread 😅
@U3X7174KS 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
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
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.
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 🙂
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.
(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).
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
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?
@rextruong one way is to hook this in your (dev) component / integrant-like system, so when you restart it, the specs are instrumented
So every time I define a new function spec I’ll need to restart my dev component you mean?
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
thanksThe 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.
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))
so this
seems be provided
and the error i recieve indicates that it expects Consumer
thanks
(fyi: there's always https://github.com/cognitect-labs/aws-api which I found really nice to use)
aws-api rather limited in its possibilities anyway, I’d like to understand how to type hint param so clojure can be proper method
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
ok, thanks