This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-01-17
Channels
- # announcements (24)
- # babashka (22)
- # beginners (49)
- # cider (16)
- # clj-kondo (8)
- # cljsrn (4)
- # clojure (87)
- # clojure-australia (7)
- # clojure-europe (44)
- # clojure-nl (4)
- # clojure-sweden (7)
- # clojure-uk (24)
- # clojurescript (5)
- # core-async (7)
- # cryogen (8)
- # cursive (22)
- # data-oriented-programming (2)
- # datomic (1)
- # emacs (6)
- # events (4)
- # fulcro (11)
- # google-cloud (1)
- # introduce-yourself (1)
- # java (8)
- # jobs (3)
- # lsp (10)
- # observability (1)
- # off-topic (12)
- # polylith (12)
- # re-frame (6)
- # reitit (36)
- # remote-jobs (1)
- # ring (4)
- # ring-swagger (1)
- # rum (4)
- # schema (1)
- # shadow-cljs (18)
- # sql (56)
- # tools-deps (33)
You have to be careful when using ::
, normally it is done to avoid key conflicts, but you need to be sure that it is not something that others will depend on. So like use it only for things for which the namespace is meant to write/read from as an internal detail, and not to be persisted or serialized.
@didibus what's the harm there? you serialize a form that had ::foo
and :your-ns/foo
gets written to disk
yeah it provides some potential for you to break things by moving the code, thats basically it.
Your namespace name is not stable, so it's easy to break consumers or future you. That's why 1.11 introduced lightweight aliases, so that you can make a stable namespace alias and use ::alias/foo
over ::foo
for your keywords.
In your example, a year later, the namespace might have moved/been renamed too: my-ns
, or maybe the code accessing ::foo
was moved to a separate ns like your-ns-impl
. And now suddenly you load this file you'd save to disk, and (::foo (edn/read-string (slurp "my-file.edn")))
returns nil, because the file contains :your-ns/foo
, but ::foo
now returns :my-ns/foo
or :your-ns-impl/foo
"That's why 1.11 introduced lightweight aliases" -- no, it was not introduced because "namespace names are not stable".
That was definitely one reason, at least I remember asking about this problem and Alex saying that this is why they are looking into lightweight aliases
It was introduced because qualified keywords are not related to namespaces and people were doing all sorts of workarounds for the lack of lightweight aliases.
That seems like just a different way to say the same thing?
The issue is coupling keywords to namespaces. And ::
does that. One of the problem this coupling causes is what I mentioned.
Because of this coupling and its problem, people were doing all sorts of workarounds.
Don't mix ::foo
with ::quux/foo
::foo
couples you to (the current) namespace. ::quux/foo
uses an alias that can point at anything.
Agree with that. I go a step further, and also just say to be careful if using ::foo
, its easy to introduce breakage or corrupt data using it.
I think ::foo
fits into the "scope" discussion -- ::foo
is naturally scoped to just the enclosing namespace. It's unique and almost "private".
If I'm sharing a qualified keyword across namespaces, I generally pick a meaningly qualifier, not just whatever random namespace it happens to be in.
Like, in halt-when
, it uses ::halt
-- and I'd consider that a private implementation detail. I wouldn't expect anyone to use :clojure.core/halt
or have an alias to clojure.core
and do something like ::core/halt
.
Ya, completely agree. That's what I meant, ::foo
should be for things that are internal details. Like implementation details or pseudo-private as you say. Should also be temporary, as-in, not survive an app restart or be persisted/etc.
The thing is, it is common for people to have a (ns my-app.user)
and then start using ::name
, ::address
, ::password
, which then gets stored in the DB, to file, passed to payloads to other services, etc. Sometimes they started with :name
and :address
, but when they add Spec realize, oh it wants namespaced keywords, and the simple fix is ::
.
And that's fine, but the issue is that later people don't realize that refactorings can break this data.
Sometimes even simple things, like a library might throw an ex-info with data as {::type :invalid-input}
, the user of the library will write some try/catch and hard-code :the-cool-lib/type
in there to check the type of the exception. Later the library author might refactor things again so that this is thrown from a different namespace, not realizing they will break consumers.
For those reasons, I feel the producer and consumer code of ::
needs to be pretty self-contained, or dare I say, encapsulated š
I agree, with the caveat that ::foo
and ::quux/foo
are very different.
@didibus I think we're in agreement here: ::foo
should be considered "private" to a namespace or at least "encapsulated", whereas ::quux/foo
is just shorthand for :some.quux.thing/foo
where the alias is some local name you've chosen to make your code less verbose.
(format ""
"bob"
"A567"
"EUR")
https://andersmurphy.com/2019/01/15/clojure-string-interpolation.htmlalso https://github.com/clojure/core.incubator
(let [name "bob"
item-id "A567"
currency "EUR"]
(<< ""))
there's also clojure.pprint/cl-format
or for the simplest cases, str
with occasionally nested pr-str
if you need the alternate printing behavior
Is there something built into clojure which you could use as a priority queue? Or are my options to use a sorted map ignoring the values, use sorted set, or to use java interop with java.util.PriorityQueue?
the sorted set / map would delete items if the priority is equal to an existing item. I'd just use the java one via interop.
ok...was somehow expecting there to be something lying around, or some way of using existing primitives to get the job done. Thanks @noisesmith - that answers the question.
there are libraries on github offering clojure priority queues, but I can't vouch for any of them
oh and I was going to use unique things as the sorted items...so no collisions would be possible, but still...I'll probably drop back to the trusted and true java impl like suggested
yeah when the behavior desired is explicitly side effecting, the cross over to immutable data, then a second one back to the mutable world via atom etc. are just two sources of errors IMHO
hi all can someone please help me to generate swagger ui for my API in clojure
what have you done so far? there are libraries for generating swagger - eg. https://github.com/metosin/ring-swagger
[reitit.swagger :as swagger] [reitit.swagger-ui :as swagger-ui] ["" {:no-doc true} ["/swagger.json" {:get (swagger/create-swagger-handler)}] ["/api-docs/*" {:get (swagger-ui/create-swagger-ui-handler)}]]
i only need to generate swagger ui of API so i can create same API in node js
Clojure APIs have no type definitions, so there is nothing to use to generate Swagger APIs
But if you want to use a spec language to specify the shape of your APIs that works as both something you can use in Clojure and to generate Swagger APIs, you should look at https://github.com/metosin/malli#swagger2
There's also https://github.com/metosin/schema-tools if you prefer using Plumatic Schema or https://github.com/metosin/spec-tools if you prefer using Clojure Spec
@mbjarland There is this library with immutable priority queues: https://github.com/clojure/data.priority-map
How would you replace Factory design patterns in functional programming?
interface ThingFactory {
Thing makeThing();
}
...
void work(ThingFactory factory) {
var thing = factory.makeThing();
thing.whatever();
}
(defn work [factory]
(let [thing (factory)]
(.whatever thing)))
you can also use the exact pattern as is (where the producer method gets a special name) with traits/typeclasses/protocols depending on your FP language
You can probably get a long way by just using generic data structures for input too. Taking a map as input is fine. Taking a sequence of maps as input is fine. Then you can just use Clojure to manipulate those data structures :)
See for instance XTDB start-node. Input is just a map! https://docs.xtdb.com/clients/clojure/#_start_node
Higher-order functions are also a form of factory. Picture it as a function that creates a customized constructor for an object.
(defn vending-machine-factory [params]
(fn [] (str "Giving out a " params "!"))
(let [coke-vending-machine (vending-machine-factory "coke")]
(println (coke-vending-machine))
>> Giving out a coke!