This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-07-10
Channels
- # announcements (28)
- # architecture (1)
- # babashka (4)
- # beginners (55)
- # biff (4)
- # calva (11)
- # clerk (1)
- # clj-http (2)
- # clj-kondo (1)
- # clojure (46)
- # clojure-austin (1)
- # clojure-europe (32)
- # clojure-nl (1)
- # clojure-norway (17)
- # clojure-uk (4)
- # clojurescript (5)
- # cursive (6)
- # datomic (2)
- # emacs (2)
- # events (1)
- # fulcro (33)
- # hoplon (4)
- # humbleui (30)
- # hyperfiddle (50)
- # jackdaw (2)
- # jobs (13)
- # joyride (8)
- # lsp (2)
- # malli (2)
- # off-topic (6)
- # re-frame (4)
- # remote-jobs (1)
- # rum (10)
- # shadow-cljs (10)
- # xtdb (7)
I am using the GPT api and electric seems to crash sometimes. I am assuming it is a timing out issue?
Could you share more info to help us diagnose the issue? What does the crash looks like?
I have general question when things are executed in an e/defn
. I have prepared this example
(e/defn my-fn [x y]
(println "executed once when my-fn is mounted into the dag")
(println "executed on mount and whenever x changes" x)
(println "executed on mount and whenever y changes" y)
(e/on-unmount #(println "executed once on unmount"))
(e/on-unmount #(println "executed once on unmount, too")))
Are these statements correct?
If so, is there an order in which things are executed. I read that everything is async.
E.g:
(e/defn my-fn [x]
(println "must run and finish first" x)
(println "must run after above has finished" x))
Is the second example true? If not, how can I enforce this?You could probably wrap them in a (do ...)
statement but then both will run each time.
Electric (do ...)
does execute statements synchronous in order?
That would be fine for the second example because both statements rely on the same value
Could also move the println's out to a Clojure function:
(defn do-both [x y]
(println "executed on mount and whenever x changes" x)
(println "executed on mount and whenever y changes" y))
(e/defn my-fn [x y]
(do-both x y))
In Electric Clojure (do …)
executes statements in parallel.
I understand you want to sequence some effects. What’s your use case?
Oh wow, I did not expect that. What about (e/server (do ...))
?
What happens with a doseq
?
This might explain why I had weird state related stuff happening with checkbox some weeks ago...
How do you force something to run in sequence that does not have symbolic dependency, m/sp
?
e.g.
(do
(d/transact conn ...)
(d/q ... (d/db conn))
@U2DART3HA I am trying to wrap three.js webgl library. I think that electrics RAII features are a perfect fit for cleaning up GPU resources. Some example usecase:
;e.g render after resize. pseudo code
[width height] (stream-of-resize-events)
(.setSize renderer width height)
(.render renderer scene)
Three.js API is essentially imperative whereas Electric Clojure is functional and reactive. Today's short answer is to move your imperative code into clojure land, so it follows Clojure semantics.
;; Anonymous fn reruns on each !my-atom change
((fn [value] (prn "first") (prn "second")) (e/watch !my-atom))
@U051SPP9Z you'd watch the datomic tx log and recompute (d/db conn) which in turn recomputes (d/q db). If you really need to perform d/q just after transact, then move the code to a Clojure function.
doseq
behaves as in Clojure. for
also. They evaluates sequentially. e/for
evaluates in parallel.
Understand that e/for is parallel, but to confirm in (do (a) (b))
, a and b will execute in parallel? I’m guessing in two separate threads?
Yes (a)
and (b)
will execute in parallel. But in the same thread (JavaScript is usu single threaded). If you want to run (a)
or (b)
on a different thread (JVM only) have a look at e/offload
.
isn’t running in the same thread in JS Land synchronous? Or is the order which runs first not determined?
The order of which runs first after the initial run is not determined. Because Electric compiles you program to a graph and maintains it. Let me clarify:
(def !my-atom (atom 0))
(e/def my-value (e/watch !my-atom))
(e/run (do (prn "No dependency")
(prn "One dependency:" my-value)
(prn "Again one dependency: " (inc my-value))))
This program will print immediately:
"No deppendency"
"One dependency 0"
"Again one dependency 1"
These lines will print in order because it’s the first run.
The program says up! So when you (swap! !my-atom inc)
only what depends on !my-atom is recomputed:
"One dependency 1"
"Again one dependency 2"
The order of these two lines is not guaranteed.What does "parallel" mean? Electric is a massively concurrent DAG language, which means we automatically maximize concurrency at every point. (to the extent that we can without breaking Clojure compatibility)
(do (println x) (println y))
has ambiguous order, because x
or y
can be remote, in which case there may be latency when the expression "boots" (runs for the first time). y
can be available first if x
is remote.
In subsequent reactive changes to x
or y
, Electric will maximize concurrency and recompute only the expression whose arguments changed.
If both reactive values are available when the do
expr boots, the println
exprs will compute (run) in lexical (natural) order.
(do (println 1) (println 2))
will print 1 2 in that order (literals are never remote), and then never print again (literals never update).
---
(dom/div (do (dom/text x) (dom/text y)))
will perform point writes in ambiguous order (like println), but with an additional rule: the DOM has a notion of order — an element's children are ordered — so we will ensure that the final result is <div>{$x}{$y}/>
even if y
is written first. Note the do
is implicit, I phrased it explicitly for clarity.
On lexical ordering in the Electric DAG: a DAG node's list of edges are lexically ordered.
Electric provides an undocumented api e/hook
to implement resource effects that have a notion of positional order and lifecycle, like dom elements. println
and other side effects do not have such a notion of positional ordering and therefore do not opt into that API.
---
(let [] (dom/text x) (dom/text y))
– same as above because of implicit do. Thus similarly all other clojure forms that imply do such as when
e/defn
(do (dom/div (dom/text x)) (dom/div (dom/text y)))
will behave as per the above rules, but we must realize that div
is in fact a macro, so the evaluation order has been mucked with to get declarative syntax (i.e., the div
element must be first created so that the text
element can then be attached). This is best understood by reading the source code of dom/element
and dom/text
which are about 10 lines total.
@U053XQP4S @U2DART3HA please confirm I have not made any errors, we also may need to tighten some vocabulary
Specifically we need to clarify our usage of "parallel" vs "concurrent" i think
Dustin thank you for your detailed explanation. Now, I have a much better understand on how electric is working 🙂
@U09K620SG is it a good idea to use the same semantic "do" for a process with no guaranteed execution order? Maybe ado
or pdo
? Just like Clojure has pmap
(which is lazy tho).
This has tripped me up and will probably trip others up too.
yes, it is this way because it cannot reasonably be any other way, this was hard fought knowledge
To be clear: electric's implementation of (do)
is fully backwards compatible with clojure; if you copy paste pre-existing Clojure code into Electric (or call a pre-existing Clojure macro), the electrified expression will return the exact same result as it would in clojure
including running the effects in the exact same order, as demonstrated by (do (println 1) (println 2))
> fully backwards compatible with clojure
almost. (do (assert false) (println 1))
doesn't print in clojure but prints in electric
Recalling now Leo's correction, let us consider:
(def !x (atom true))
(e/run (do (dom/text "a") (assert (e/watch !x) "b") (dom/text "c")))
(swap! !x not)
for clarity, e/run
marks an electric reactive expression as if in a unit test
Should text element "c" be destroyed and removed from the DOM when !x toggles? What about "a"? What happens when !x toggles back?Is (e/server (do-thing) nil)
equivalent to (e/server (e/discard (do-thing)))
?
So excited for incremental compilation ⚡
Which cache does HYPERFIDDLE_ELECTRIC_SERVER_VERSION
bust? Could server not set E-Tag on index.html and/or the JS it serves? On connect check version and if server & client don't match, force client reload?
HYPERFIDDLE_ELECTRIC_SERVER_VERSION
will trigger a client page refresh if it is set and doesn’t match HYPERFIDDLE_ELECTRIC_CLIENT_VERSION
(hardcoded at build time)
About ETAGs.
Websocket client has auto-reconnect.
If a new server version is deployed (e.g. v1 to v2), the websocket client (v1) will be disconnected and will reconnect to sever (v2).
At this point there’s no HTTP request loading HTML or JS. Only the Websocket Upgrade. So no ETAG if I’m not mistaken.
If HYPERFIDDLE_ELECTRIC_SERVER_VERSION
is hardcoded at build time why does it need to be passed in when running uberjar? https://github.com/hyperfiddle/electric-starter-app#deployment
Anything that tries to start the JAR needs to know something about its version which does not make sense to me, even though it can't change the version. Makes it hard to deploy via Dokku in Procfile.
i believe this because today the dag is actually baked into the client, the server is general
perhaps the flag can be removed with minor upgrades to build.clj in the starter app
How would the Datomic tx listener change to support separate Datomic databases per customer, but still stored in the same Datomic System and one app deployed?
I'd have to establish a separate conn per customer when they log in and hold onto it somewhere. I imagine I would have to move !db & !taker into a map per authenticated username / tenant ID?
Could I use the !present
pattern but instead of usernames, put Datomic connections or DBs in there?
How do I create session-specific atoms? I would like to avoid watching a singleton atom with everyone's DB values that triggers a refresh for all customers any time anyone transacts anywhere.
(def !x (atom .)) ; global state (let [!x (atom .)) ; local state
By session-specific I mean auth-scoped so that a Datomic connection can be shared between two user windows, which I guess will mean having a global conns
map keyed on user ID => conn
and then subscribing/unsubscribing from txReportQueue.
I recall there being a limit, i.e. only a single txReportQueue per...connection? Or are Datomic connections relatively cheap? I imagine that each connection acts like own peer with own cache? So to share datom cache you'd probably want to share conn
between Electric sessions with same tenant / user ID?
These are expert level Datomic questions, i'd ask in their support channel
I'ts possible to dynamic call and dispose an electric component?
• Something like that:
◦ 1) Client receives an string representing a component, ex: "ComponentA"
◦ 2) Client looks into registry for "ComponentA"
◦ 3) Client call new
on that Component.
◦ 4) Client can dispose
(unmount) this component in other parts of the code.
mount/unmount are implicit, userland cannot explicitly dispose
Show me some pseudo-code for what business problem you are trying to accomplish
in https://electric.hyperfiddle.net/user.tutorial-lifecycle!Lifecycle note that the blinker is added/removed to the DOM but there is not an explicit disposal, you can also see in the console that we've hooked the mount/unmount lifecycle
I'm thinking of way to integrate with https://golden-layout.com/.
• There are two ways to integrate.
◦ 1) Register components in their own way.
▪︎ the lib will mount/unmount the component in its own way.
◦ 2) Register two callbacks, bindComponent
and unbindComponent
▪︎ here, it's up to you to mount/unmount the component on the dom
pseudo-code for the idea:
(GoldenLayout. dom/node
(fn bind
[container item]
(let [name (.-componentName item)
el (.-rootElement container)
resolved (get registry name)]
(dom/with el
(new resolved))))
(fn unbind
[container]
(let [el (.-rootElement container)]
(dispose el))))
Electric component inside GoldenLayout element?
this is a POC of a fulcro adapter, though in this configuration each instance of the with-electric bridge will get it's own websocket to the server (which works fine)
Hi! Is there emacs users here (with clojure-lsp)? What is your hack to disable Unresolved namespace
:
(ns app
#?(:clj [app.db :as db]))
(e/server (db/foo ...)) ;; Unresolved namespace `db
`If HYPERFIDDLE_ELECTRIC_SERVER_VERSION
is hardcoded at build time why does it need to be passed in when running uberjar? https://github.com/hyperfiddle/electric-starter-app#deployment
Anything that tries to start the JAR needs to know something about its version which does not make sense to me, even though it can't change the version. Makes it hard to deploy via Dokku in Procfile.