This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-11-29
Channels
- # adventofcode (1)
- # announcements (2)
- # beginners (163)
- # biff (3)
- # calva (19)
- # cider (56)
- # cljs-dev (5)
- # clojure (43)
- # clojure-belgium (2)
- # clojure-europe (47)
- # clojure-norway (32)
- # clojure-uk (2)
- # clojurescript (24)
- # datomic (5)
- # events (1)
- # fulcro (2)
- # hoplon (11)
- # hyperfiddle (12)
- # jobs (1)
- # lsp (15)
- # malli (7)
- # music (1)
- # polylith (2)
- # re-frame (7)
- # reagent (7)
- # shadow-cljs (25)
- # specter (9)
- # squint (16)
- # xtdb (5)
I found an interesting case of a “closure over field names” in deftype
not being interpreted correctly when placed inside of locking
and using set!
.
I’ll put the full example in a thread.
The high-level part is just this though:
;;; Compiler error:
;;; Cannot assign to non-mutable: x
(deftype ChangerWithLock [^:volatile-mutable x]
ChangingThing
(getVal [this]
(locking change-lock
(set! x (inc x)))
x))
x
isn’t recognized as a mutable field when the locking
is there - it is fine otherwise. Need to use something like (set! (.-x this) ...)
instead.Here it is:
(definterface ChangingThing
(getVal []))
(deftype ChangerNoLock [^:volatile-mutable x]
ChangingThing
(getVal [this]
(set! x (inc x))
x))
(let [^ChangingThing changer (->ChangerNoLock 1)]
[(.getVal changer)
(.getVal changer)
(.getVal changer)])
;;= [2 3 4]
(def change-lock (Object.))
;;; Compiler error:
;;; Cannot assign to non-mutable: x
(deftype ChangerWithLock [^:volatile-mutable x]
ChangingThing
(getVal [this]
(locking change-lock
(set! x (inc x)))
x))
;;; Do this instead
(deftype ChangerWithLock [^:volatile-mutable x]
ChangingThing
(getVal [this]
(locking change-lock
(set! (.-x this) (inc (.-x this))))
(.-x this)))
(let [^ChangingThing changer (->ChangerWithLock 1)]
[(.getVal changer)
(.getVal changer)
(.getVal changer)])
;;= [2 3 4]
I looked a bit at the bytecode, but didn’t dig enough to really understand it fully.
I’m wondering if it tends to just not be a good idea to access mutable fields as a “closure” within deftype
and instead do the field access interop everywhere.
Seems to related to: • https://ask.clojure.org/index.php/3340/cannot-close-over-mutable-fields-in-deftype • https://www.juxt.pro/blog/mutable-deftypes
Looks like the compiler can't figure out that x
is a deftype field when the setter is within a try-catch block. But, weirdly enough, sometimes it can.
;; This works
(deftype ChangerWithLock [^:volatile-mutable x]
Object
(toString [this]
(try (set! x (inc x))
(str x)
(catch Exception _))))
;; This doesn't
(deftype ChangerWithLock [^:volatile-mutable x]
Object
(toString [this]
(try (set! x (inc x))
(catch Exception _))
(str x)))
would clojure be better if macros could splice themselves? Like reader conditional unquote splicing but happening at eval time like if @ could be called in the middle of a normal form, like suggested here: https://stackoverflow.com/questions/44052107/clojure-unquote-slicing-outside-of-syntax-quote happening at macro-expansion time or evaluation-time, not sure is there some invariant that this violates which is useful? can implement it for a random scope something like (reduce [acc el] (concat acc ((if (splice? el) identity list) el)) form)
when writing macros and feel the need to do something like that i usually use a loop one () outer and build the list normally
But if it was in the language you could do it in arbitrary scopes without needing to be in macro scope
I think I asked the question wrong as thinking about it I don't really mean macros, they're just an example of something that would usefully splice itself
there are languages that can do this (`unpack` in lua, for example) but i don't think it's necessary in clojure? if you have a list and you want to splice something, you can use syntax-quote to make it happen: '(1 2 3 @(4 5 6))
is equivalent to (1 2 3 ~@(4 5 6))`
But syntax quote pushes you into reader conditional time, the most restrictive possibility
Doesn't let you eg write a macro that can be called inline in a let binding vector to add bindings
like creating dynamic bindings? that seems hard to parse and follow
i think python has this with *
(`*args`) and javascript has this with ...
(`[...obj]`)
give that we have macros, i don't see the need, but i know i've wanted them in the past
oh, this is just a version of "multiple return values", which we accomplish with destructuring
Basically I'd say the main need/use is to be able to compose your macro (from the inside of?) Another macro, eg. The first example is let
I think it's always doable (kind of) by rewriting the outer macro, or moving your macro /outside/ the outer one, but then you need to rewrite some existing macro, or refer to specific positions inside the (not-your-macro) from outside. For let you can just wrap another let /outside/ not inside for the example I gave
yeah, i get it, and i can seem some arguments for it
So what's the change you're suggesting? Does it make it more complicated to write a macro that doesn't need this feature? How much more complex does it make the implementation of macroexpand
? Regardless of bugs does it add to the cognitive weight of writing a macro? Clojure has a strong emphisis on composing simple elements together, and making something as fundamental macroexpansion more complex might just not be worth the trade-off? I guess it depends on "better" at what?
I think it does nothing semantically, unless there's something that yields an unsplice element, and that's handled by the compiler not you
i think the suggestion is that at read time (not even macro time), if a bare ~@
is encountered, it would be evaluated and inserted into the surrounding form
If someone returns one where it's not expected I guess you could get an interesting explosion
so (let [a 1 b 2 ~@[c 3]] (prn c))
would work
Not read time, because read time wouldn't let you write macros that yield something that wants to be unsplice I think
lol that's why this is a suggestion
you're right that it currently doesn't work like that
yeah ... so the reader has to be able to return more than one result? ... or we need a new datatype that means "splice these things in"?
Is there anything more upto date than Swagger1st that creates a server from an OpenAPI specification.
I haven't tried it myself but there's https://github.com/juxt/apex. Seems to be pre-alpha and haven't been touched in 3 years so I'm not sure if it's any more up to date.