Can I check I've understood the token explainer tutorial correctly?
https://electric.hyperfiddle.net/tutorial/token_explainer
What I believe is:
• On L15, t and err are signals. Initially both nil
• As soon as button is clicked, t becomes non-nil, which changes button styles thanks to lines 16 & 17. However err remains nil for the time being.
• t becoming non-nil is what causes the when-some to succeed and mounts the (let ...) branch on L 20, which is what makes the call to the server.
• As soon as (t) or (t res) are called, t becomes nil, again changing styles thanks to lines 16 & 17
• If (t res) is called, err is set to the value of res and this propagates back to L18 to set :aria-invalid.
Is that correct? Thanks for answering my ELI5 questions - it's just taking me a little while to wrap my head around this pattern 😅
it is correct. Do you have any feedback on the explainer, or the API?
Thanks for confirming! Explainer was good. Maybe I'd suggest just a few of the bullets could be reworded for clarity:
• (edited bullet): When t becomes non-nil, (when-some [t ...] succeeds and the when-body is mounted. Mounting the when-body makes the call to the server followed by calling (t) or (t res) , either of which set t to nil, disabling the button (lines 16 and 17) to prevent future events, i.e. "backpressure the user", who is the source of events.
• (new bullet): In the error case, calling (t res) sets the value of the err signal on L18 to non-nil, changing, turning the button pink.
• ...
• (edited bullet:) Invoke the token as a function (t ...) to spend the token, which will turn it nil, which will unmount the when-body and remove the disabled state from the button so it can receive the next event.
I really appreciate these tutorials you and the team put together. I'm following Dustin's advice of typing out every tutorial on my machine to make sure I really understand it, otherwise it's too easy to skim over and kid myself that I understand.
Token API seems a bit more verbose than v2 but I'm happy to trust what you say in the tutorial that it's powerful and will grow on me 👍
Thank you for the feedback
Re token API I'd say one can implement the old dom/on API with tokens but not vice versa. E.g. the forms abstractions require tokens
I’ve added an underlining option to my Emacs minor mode. See thread.
I can fix this to an extent.
Here’s where I’ve got to with the DirTree example:
I”m not coloring Electric calls themselves, but — not shown in that example — I am recursively coloring their args.
It needs an understanding of all Electric forms that make bindings (`e/defn`, let, e/for. e/fn, …), and it needs to understand destructuring.
Some way to go, and it won’t understand any new binding forms that I don’t teach it. So any userland macros that create bindings will be an issue. Unless I come up with an extension mechanism.
An example with non-trivial args in an Electric call:
Improved example, changing a to x:
Ohh. I made a package for this same thing previously, just didn't do the work to open-source it. Nice to see that you went a step further 🙂
@hkjels Oh, cool. How far did you get with it?
Alternative installation for Emacs 30+
(use-package nomis-electric-clojure
:vc (:url ""
:rev "newest")
:ensure t)
might be worth putting in the README
> Alternative installation for Emacs 30+ Thanks! I’ve added that.
Actually, I see now that it's just part of my clojure config, so not very far 😄
(use-package clojure-ts-mode
:defer t
:commands (clojure-ts-mode)
:mode (("\\.clj\\(?:c\\|s\\)?\\'" . clojure-ts-mode)
("\\.edn\\'" . clojure-ts-mode))
:hook ((clojure-ts-mode . cider-mode)
(clojure-ts-mode . jarchive-mode)
(clojure-ts-mode . eglot-ensure)
(clojure-ts-mode . clojure-highlight-db-ident-keyword)
(clojure-ts-mode . clojure-add-electric-keywords))
:config
(defun clojure-setup-electric-prettify ()
"Prettify e/client and e/server symbols."
(setq prettify-symbols-alist
'(("e/client" . "λ")
("e/server" . "⚙"))))
(defface clojure-db-ident-keyword-face
'((t (:foreground "#ffa500")))
"Custom face for the keyword following :db/ident.")
(defface clojure-e-client-face
'((t (:background "#fafafa")))
"Face for e/client sexp.")
(defface clojure-e-server-face
'((t (:background "#f3f3f3")))
"Face for e/server sexp.")
(defun clojure-highlight-electric-blocks (limit)
"Highlight e/client and e/server blocks up to LIMIT."
(while (re-search-forward "(e/\\(client\\|server\\)" limit t)
(let ((sexp-end (find-sexp-end)))
(when sexp-end
(put-text-property (match-beginning 0) sexp-end
'face (if (equal (match-string 1) "client"
(e-client-face)
(e-server-face))))))))
(defun clojure-highlight-db-ident-keyword ()
(font-lock-add-keywords nil
'((":db/ident[[:space:]]+\\(:[^[:space:]\n]+\\)"
1 'clojure-db-ident-keyword-face prepend))))
(defun clojure-add-electric-keywords ()
(font-lock-add-keywords nil '((clojure-highlight-electric-blocks))))
(add-to-list 'org-structure-template-alist '("clj" . "src clojure"))
(add-to-list 'clojure-align-binding-forms "e/server")
(add-to-list 'clojure-align-binding-forms "e/client")
(add-to-list 'clojure-align-binding-forms "e/on-unmount")
(put-clojure-indent 'e/defn :defn)
(put-clojure-indent 'e/def :def)
(put-clojure-indent 'compojure.core/DELETE :defn)
(put-clojure-indent 'compojure.core/GET :defn)
(put-clojure-indent 'compojure.core/POST :defn)
(put-clojure-indent 'compojure.core/PUT :defn)
(put-clojure-indent 'compojure.core/context :defn)
(put-clojure-indent 'clojure.test.check.properties/for-all :defn)
(setq clojure-ts-indent-style 'fixed
clojure-indent-style 'always-indent
clojure-indent-keyword-style 'always-indent
clojure-enable-indent-specs nil
clojure-align-forms-automatically t))Looks like nomis is doing quite a bit more
Sometimes the background colours can get in the way, so this is a nice alternative.
It’s easy to switch between backgrounds and underlining.
I’ve just realised that this is all rather broken. For example the h and s on line 28 above shouldn’t be colored.
I don’t think it’s easy to fix.
Grrrr!
just wanted to say this is a really cool project and i hope it works out!
Thanks. I may be able to fix it to an extent. Just trying something now.
Could Electric be used for syncing multiplayer Clojure code editors at the s-expr level, where S-exprs are like DOM tree? CRDT pain etc etc. i.e. Paredit-like point effects. Real motivation is LLM integration as you type to stream to model, and see its proposed edits without constantly diffing. But also, multiplayer Clojure 🙂
i think CRDT is the right structure for this, "conflict free" is what collaborative editors want (electric can still manage the network connection, the CRDT libs should support network-free usage)
Even if we ignored conflicts, I guess Electric would have to know the AST on both client and server ahead of time? Since writing code is modifying the AST, i.e. more analogous to compiler step than runtime, but then back to diffing or CRDTs.