Morn!
Morn!
Morn!
God morgen!
Morn!
Morn
God morgen!
mrn
Morn!
morn!
Denne overrasket meg:
(def ret (def x 33))
ret
;; clj -> #'x
;; cljs -> 33men jeg kan gjøre #'x i både clojure og clojurescript!
Så (symbol (defn g [])) returnerer `g på clj og krasjer på cljs!
synes det var snodig.
cljs har ikke vars
har du en referanse på det?
skulle gjerne visst hva som faktisk er forskjellig. det jeg har observert til nå er at returtypen til def og defn er ulik.
Fra den siste:
> • def produces ordinary JS variables
>
etter litt tukling ser det ut som alle de interessante runtime-greiene man kan gjøre med vars mangler. alter-var-root mangler, intern mangler. ns-publics kan ikke ta *ns* som argument.
Også fra din siste:
> Vars
> • not reified at runtime
Since I could read most of this exchange, I thought I'd jump in. (Note that reading is a long way from being able to write sensibly, so I will stay with English. Please respond in Norwegian: I need the practice).
I knew some of the above, but it was interesting to look into more of it.
In Clojure, Var has "frames". This is a stack (implemented as a linked list) of java.lang.ThreadLocal objects, wrapping a set of bindings. Every time you use (binding [...] ...) with a "dynamic" var then it pushes the new bindings onto that stack. Those associations are now returned instead of the original var values (and they're potentially unique for each thread). Leaving the binding block restores the previous frame, removing the ThreadLocal values when there are no frames left.
i.e. there is a lot of infrastructure to make the contents of dynamic vars behave correctly in a multithreaded environment.
The root of a var is just the value before any frames shadow it. So alter-var-root is changing the initial value underneath all of that multithreading structure. When you don't have any threads, then it just changes the value in there.
ClojureScript has no threads, so none of this is needed. Hence, dynamic vars do something different, and there is no need for alter-var-root. Instead of that last one, you just use set! in ClojureScript. (Clojure can also use set! if it's a dynamic var and you're in a thread where you bound the value. This is likely not a very safe practice!)
If you really want to intern something at runtime, like:
(intern 'user 'x 42)
Then in ClojureScript you can always just add it to the namespace using the goog.object api e.g.
(require '[goog.object :as gobject])
(gobject/add (find-ns-obj 'cljs.user) "x" 42)
This doesn't create a Var like Clojure does, but ClojureScript namespaces don't use vars. Instead, it just attaches the name to the value. Unfortunately, the repl hates this. It has an internal map of everything that was defed and this bypasses it. So you can get this new value of x, but you'll get a warning when you do:
cljs.user=> (gobject/add (find-ns-obj 'cljs.user) "x" 42)
nil
cljs.user=> x
WARNING: Use of undeclared Var cljs.user/x at line 1 <cljs repl>
42
(This is why I said "`intern` at runtime". The warning is from the repl, not the runtime)Takk for et interessante kaninhullet (Did I say that right?)
Testa det på mino her nå, som oppfører seg likt som JVM Clojure 🙂
;; def returns the Var, and the REPL echoes it
(def ret (def x 33)) ;; => #'user/ret
;; ret's root really is the inner Var
@(var ret) ;; => #'user/x
(var? @(var ret)) ;; => true
;; reading the symbol auto-derefs through the ns env
ret ;; => 33
(var? ret) ;; => false
;; lexical bindings skip the auto-deref
(let [r (def q 5)] r) ;; => #'user/q
(let [r (def q 5)] (var? r)) ;; => true
;; tree-walker positions keep the Var
(var? (do (def w 6))) ;; => true
(var? (if true (def w2 7))) ;; => true
;; BC-compiled fn bodies return the value instead
((fn [] (def inner 9))) ;; => 9
((fn [] (var? (def inner2 9)))) ;; => false
;; the auto-deref exists so referred names stay callable
(var? println) ;; => false
(println "still callable") ;; => still callableHaha, “x is undeclared, but it’s value is 42, since you insist”. (My Norwegian isn’t there.)
> Takk for et interessante kaninhullet Nesten! Du mangler bare riktige endinger. Helt riktig hadde vært: "Takk for et interessant kaninhull". (kaninhull uten et fordi det er i bestemt form "et kaninhull", og interessant uten e fordi det er i entall, ikke flere.
Thank you for the great explanation, @quoll! The impact of single threaded JS / multi-threaded JVM makes a lot of sense, and I had no idea one could do the equivalent of altering vars in Clojurescript. ☺️ gratitude
What about node SCI regarding vars, @borkdude?
Can you give me the full context so I don't have to mine this thread for the relevant info
SCI has real vars also in CLJS. Vars in CLJS are a compile-time fiction
Tbh, I don’t dare summarize the context. 😃 This is the most relevant part: https://clojurians.slack.com/archives/C061XGG1W/p1787684538408449?thread_ts=1787658603.138149&cid=C061XGG1W
The context was that CLJS doesn't really have vars. I described how vars in Clojure handle values in a threading context, while CLJS doesn't really have threads. Instead, a Var in CLJS is a simple structure wrapping a value (or… wrapping a thunk that returns the value), but that when code accesses a value that has been defed it just looks up the value saved in the environment. The “environment” in this context is a goog.object which contains keys of namespace names (OK, it's fragments of the names… I’m simplifying) and values that are more objects. Traverse down the object tree to find the object that contains your namespace’s bound vars. Alternatively, just ask for (find-ns-obj 'your.namespace). The namespace works like a string-keyed map, where the keys are strings and the values are the values they are mapped to. No vars appear in this path at all.
(sorry if that was hard to read. I wrote it on my phone)
These are just nested JavaScript objects but after advanced compilation they're gone. Squint uses the same trick to facilitate live objects during REPL/dev time