(defprotocol IFoo
(dest [this]))
(deftype Foo [^:volatile-mutable dst]
IFoo
(dest [this]
(locking this
(set! dst 123))))
;; Works ok
(deftype Foo [^:volatile-mutable dst]
IFoo
(dest [this]
(let [dest (locking this
(set! dst 123))]
dest)))
;; Unhandled clojure.lang.Compiler$CompilerException
;; Caused by java.lang.IllegalArgumentException
;; Cannot assign to non-mutable: dst
would this be a Clojure bug?I'd guess it's probably due to not allowing closing over mutables (https://clojure.atlassian.net/browse/CLJ-274)
or do you need the syntax (set! (.dst this) 123)
for this?
user=> (doc set!)
-------------------------
set!
(set! var-symbol expr)
(set! (. instance-expr instanceFieldName-symbol) expr)
(set! (. Classname-symbol staticFieldName-symbol) expr)
Special Form
Used to set thread-local-bound vars, Java object instance
fields, and Java class static fields.
Please see <http://clojure.org/vars#set>
Used to set thread-local-bound vars, Java object instance
fields, and Java class static fields.
From the link, "When the first operand is a symbol, it must resolve to a global var."
The advantage of clojure test for all the things is: - outputs (editor integration, tap, junit, cli exit code) - selection (metadata) - tooling (parallels, pretty diffs) These are (mostly, excepting perhaps editor integration) generic to generative, unit and integration tests I think? I think that's what you're getting at, but I'm not sure. It's worth making sure we all know what we're talking about.
(.dst this)
seems to work, thanks @alexmiller!
For other curious people, this is the generative test runner used by clojure https://github.com/clojure/test.generative/blob/master/src/main/clojure/clojure/test/generative/runner.clj
I'd summarize it by saying it's using different keys on vars to locate tests to clojure.test, and uses those as different inputs.
So is it just an accident of implementation that it happens to work sometimes with the (set! symbol expr)
variant, i.e. when used inside of a deftype to mutate fields of the type instances?
Because as far as I can tell it does work in core.rrb-vector across dozens if not hundreds of uses.
It’s the fact that it is wrapped in locking
, which has an internal try block
If that is the Q
why does (set! (.dst this) 123)
work then?
I assume it's some difference in compilation but I didn't look any more closely at it
I guess it doesn't have to close over the mutable in that case, just invokes via interop?
Yeah I guess so? Seems accidental
I mean, this is exactly the sort of place where you should be able to do this kind of thing
only locals are closed over, the let + locking combination causes the try
to be lifted into a fn while just locking doesn't cause let
puts the try
block in a different compilation context
not trivial to fix cause changing the compilation of mutable locals (e.g. transforming foo
to (.foo this)
) is a potentially breaking change when closing over is done intentionally (capturing value vs capturing reference)
the hoisting patch that compiles hoisted functions as methods would fix that
The auto hoisting is the source of a number of oddities
afaict that hoisting is a workaround for a compiled bug wrt locals (not sure if a bug or intentional compilation choice), but alternatives are possibile - - t.e.jvm iirc doesn't need that