How about #imm {:a 1}?
Does DCE follow that my immutable map is only used in fnA and my application only calls fnB and therefore doesn't need to bring in the immutable library? Does using a reader impact that?
Yes, that should follow (I will verify if importing the immutable lib has no anti-DCE effects) and the reader should not impact that
I think we need a reader solution so people can put an identity override for clojurescript data_readers . Using a tag will probably confuse JVM Clojure in .cljc files
I like #imm more than the metadata. Will there be a built in predicate to check if I have an immutable map or not?
good consideration, probably we'll add one or maybe piggy back on a core predicate if possible
Ah, good point that #imm is less compatible with jvm. ^imm will at least be ignored on JVM.
can you respond in the thread we were talking? ;)
Sorry, yes. I wasn't sure if I was changing topics/sub problems and distracting π
oh ok
anyway about the reader/tag. it's the other way around:
user=> ^imm {:a 1}
Syntax error compiling at (REPL:0:0).
Unable to resolve symbol: imm in this contextOnline communication is hard π ^:imm, then? Symbols are a reserved list of course π
metadata adds runtime metadata on collection
If i have to #? anyway then I'd want to double check whether they all work equally neatly. It would be a shame though.
#?
?
I mean, what do you mean with #?
I'm not talking about a reader conditional
I like #imm more than the metadata
I think that's where the "matrix" is guiding us, all the other solutions are worseSorry, I mean, If I want to build a library that works on JVM & Squint, if I have to use a reader conditional on all my maps to support squint then I'm not sure how well ^ and # are supported in reader conditionals.
no reader conditionals
just:
(def x #imm {:a 1})
and you define one reader macro for imm in your data_readers.cljwhich is plain identity
How can that be avoided if I want a portable library that's JVM & Squint compatible but relies on immutable maps?
upvote
That requires all my JVM users to set up a data_readers, even if they don't care?
many things already work in squint with the COW data structures btw, you don't have to even use #imm . imm is just a thing you would sometimes need e.g. for big wide state objects with hundreds of keys.
data_readers.clj in a library are propogated to the end user
So every library would include a copy of it including imm?
maybe just a squint JVM compat lib. maybe this is a sign we should go with #squint/imm instead. ah screw this, I'll just think longer about it and when it becomes really necessary
Why I raised this issue, I'd really like to write data literals to get immutable data but perhaps we could just have a function that receives a JS object, but that would screw up keys (those get stringified in JS)
hash-map like function? (Or exactly that function?) It's not a literal, but it's close.
yeah I've already got:
(ns foo (:require [squint.immutable :as imm]))
(imm/hash-map 1 2 3 4)
ok, that's not bad probablywhy use a reader or compile trick when you can just do a function. "just use a function" again and again
I did just blindly assume using a reader was for JVM portability or something.
I could do a macro that turns nested compile time data into a immutable collection though:
(imm/lit {:a 1})well yeah IF you're going to do a reader, then that's one thing to think about since the JVM is going to choke on #imm
but I guess that's what reader conditionals are for
so
(:squint #imm {:a 1} :default {:a 1})
could be a thing, but it's a bit ugly tooIt's a bit annoying to type out a whole map twice. hash-map is a bit easier to swap out without repeating everything.
true... good thinking.
(ns foo
#?(:squint (:refer-clojure :exclude [hash-map]))
#?(:squint [squint.immutable :refer [hash-map]])
;; and there we have hash-map doing the immutable thing on all platformssometimes "do nothing" is the best column in the decision matrix
The hard part is usually identifying the problems/inputs π. I'm not even sure if anyone will want to do this.
Caveat: I'm not a big user of squint and CLJS, just a dabbler.
Wouldn't it be nicer to be able to set an option to default the standard reader for {} to use HAMT ? I'm thinking, maybe not app-global, since you don't want it to affect dependencies or that could break them, but a namespace-level option?
I'm thinking how in Elisp you do:
;;; ... -*- lexical-binding: t -*-
If you want a namespace to use lexical binding.And then inside that namespace, if you don't want the HAMT, you would use (hash-map ...)
And then you could also have a (squint.immutable/hash-map ...) if you wanted it the other way around, like for your namespace to default to non-immutable maps and only occasionally need an immutable map.
Maybe it could follow the same setup as: (set! *warn-on-reflection* true)
Or it could be set as metadata on the namespace maybe?
my expectation would be that you would just HAMT sparingly e.g. for app state and the rest is just regular JS structures, so wholesale namespace probably make sense. So I'm sticking with just literal namespace + functions for now I gues. Perhaps a "lit" macro that takes a data literal and coerces it to the imm. data structure could be nice
Hum... at the point where it gets bundled in, wouldn't you kind of just want to use HAMT everywhere all the time? What's the cons? Is it slower but saves memory?
Oh I guess interop is the biggest downside. So you're thinking people would selectively use it if they need the performance/memory benefits from HAMT ?
@borkdude The squint.immutable/hash-map approach make sense to me for app state since that map is entirely under your control. If you wanted to switch out CoW for HAMT across the board, some kind of global setting makes sense. (That doesn't sound like what you are thinking.)
yeah or perhaps something like a macro which controls immutability (and the realness of keywords and symbols)
(with-cljs (assoc {:a 1} :b 2))Would be cool to have some configuration to enable HAMT by default in all structures or something like that
The reason I suggested namespace-level config is because I don't think you can safely change a COW to a HAMT for code that does interrop or uses the mutable assoc! and company no? So a global switch would potentially break some namespace or your dependencies.
yeah I think a global project config isn't a good fit since {:a 1} won't be a JS object anymore and that breaks the JS interop globally
Yeah, that makes sense. It breaks the current semantics.
My js-land port of tech.resource is now squint-based instead of cherry-based, with the added benefit of actually disposing of resources this time, with a squint-based cljs.test suite to compare to tech.resourceβs operations.
neat, what are you using this for?
Iβve got wasm builds of native libraries, and want to be able to use tech.resource tracking of the pointers to native objects on the js side with the same ergonomics of tech.resource. The next month or two Iβll be publishing a few more libraries that fundamentally depend on this (one that manages pools of workers to emulate jvm parallelism, one that makes core.async.flow models work on squint). All of these are part of the project which Iβll be describing at one of the Friday 1:20pm workshops at https://2026.clojure-conj.org/schedule. In the meantime, an intermediate example: the logic which exists in the following code (which uses my broken earlier cherry version) will, once I get my changes pushed up, be based on this squint version of resource-tracker when run on js and use tech.resource when run on jvm: https://github.com/willcohen/clj-proj/blob/main/src/cljc/net/willcohen/proj/proj.cljc#L777-L800
nice!