This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-02-16
Channels
- # announcements (18)
- # architecture (12)
- # babashka (20)
- # beginners (32)
- # biff (21)
- # calva (81)
- # clerk (6)
- # clj-kondo (16)
- # clj-otel (5)
- # cljsrn (8)
- # clojure (94)
- # clojure-austin (1)
- # clojure-australia (1)
- # clojure-europe (68)
- # clojure-nl (2)
- # clojure-norway (6)
- # clojure-uk (2)
- # clojurescript (13)
- # conjure (1)
- # core-logic (1)
- # cursive (7)
- # data-science (2)
- # datahike (3)
- # datomic (12)
- # emacs (33)
- # etaoin (1)
- # fulcro (8)
- # graalvm (2)
- # graphql (1)
- # honeysql (1)
- # hyperfiddle (97)
- # improve-getting-started (40)
- # jobs (2)
- # jobs-discuss (12)
- # lsp (9)
- # membrane (6)
- # nbb (2)
- # off-topic (16)
- # portal (6)
- # re-frame (2)
- # reagent (3)
- # releases (2)
- # remote-jobs (1)
- # tools-deps (7)
- # xtdb (38)
I'm more on frontend currently but been watching this since months. Now that it is in public, i want to ask how to use tailwindcss for styling the ui?
Install tailwind (follow their readme)
Then add classes to your dom elements:
(dom/div (dom/props {::dom/class ["bg-teal"]}))
I think you can even use https://github.com/thheller/shadow-css
Latest i try to configure for its jit feature so e.g. only used classes sent to browser but still not satisfied with it. Hooking with shadow-cljs build pipeline would be awesome. Has some one put ground works on this matter?
You want to use its JIT feature to scan your cljc code? To prune unused classes? What do you mean “only sent to the browser”? Do you also use tailwind classes on the server?
About only sent to browser, i mean for development and production it works as exactly as how it works with node react js (e.g. vite) project.
I think wether this is doable: https://twitter.com/simonswiss/status/1625675040267051010?t=2hHb0DZzXUtzU7tmCp5ftQ&s=09
Understood. It should just work out of the box. A basic tailwind config would look like:
module.exports = {
content: ["./src/**/*.{clj, cljs, cljc}"]
}
For a prod build you would scan the output javascript file rather than your sources.
While it’s possible, it’s not clear if it’s Electric Clojure’s job to run the tailwind executable, like Remix does.
What do you think?Hi! Electric is interesting. I'm trying to evaluate this from a REPL:
(e/client 123)
;; clojure.lang.ExceptionInfo: Invalid p/client in Clojure code block (use from Electric code only)
;; ...
Is there a quick way I can evaluate electric code from a REPL? (Sorry if this is obvious from some docs I haven't read yet)the client/server macros require you to have a clojurescript browser repl working, which is tricky (and i actually think we might have broken that recently, i am not sure if our tests pass in the cljs repl right now)
Actually no - there is a special test entrypoint for Electric Clojure that has loopback client/server configuration, photon_test.cljc uses this iirc
Does that mean hyperfiddle/rcf provides "magic/helpers/context" required to "choose where to send the expression for evaluation"? Whereas when I simply evaluate expressions, I don't provide that context?
here - https://github.com/hyperfiddle/electric/blob/master/src-docs/user/photon/photon_1_lang.cljc
e/run (p/run) is how you run an Electric expression at the repl, since the program is reactive it will stay running, until you kill the program through the dispose callback
ah, right. I was wondering about that part. Why I needed all the other stuff apart from p/run
.
It works naked, rcf/tap becomes println outside of (tests)
note the println 1 2 3
in response to the swap!
so here you are allowed to
(e/def x (e/watch !x))
outside of an async context because x itself becomes an async thing (signal?), but when you actually want to do something with it (print it), you need to provide an async context for that printing with p/run
,
(p/run (println x))
?if you browse into the source, e/run essentially calls the Electric compiler and then boots the reactive program
e/def today doesn't actually do anything; it saves off the quoted program (s-exprs) as metadata for the compiler to deal with later when e/run is called
ok, that makes sense. I'm probably asking lots of stupid questions here, never really worked with async code. One more question if you don't mind.
(def dispose (e/run (rcf/tap (e/client 123123))))
;; prints 123123
(def dispose (e/run (e/client (js/alert "stuff is happening"))))
;; Syntax error compiling at (src/shadow/user.cljc:13:14).
;; No such namespace: js
Can you help me understand why the second form is failing? I thought since I was able to evaluate client code for the first form, I should be able to for the second form too?to try to answer my own question, I'm guessing I'm missing setup. I've been reading the example app, which https://github.com/hyperfiddle/electric-starter-app/blob/b347af77b7c32788acd117777f9d0a1e256d875e/src/user.cljs#L7-L22.
e/run
runs both the server and client parts on the same peer, in this case you're starting it from the clj REPL so on the JVM
You can also build a small entrypoint and let shadow recompile on save. You get similar instant feedback
I'm using the electric-starter-app
example, and I already have recompiles on save.
Is it possible for me to evaluate things in async environment setup by user.cljs
? I see that there's a var reactor
, can I use that?
Electric compiles your whole programs, there's no way to "poke holes" into it. Can you give an example of what you'd like to evaluate?
this is really helpful for me, by the way, I think I'm getting closer to what I'm not understanding. > Electric compiles your whole programs, there's no way to "poke holes" into it. I suppose this is what I'm not understanding.
there's otherwise no way to get information into the frontend. Does that sound right?
OK, your example is standard cljs code, so all you need for that to evaluate is a cljs repl
If you add the js/alert into your app then yes, the program will recompile and you'll see the alert
By whole programs I mean that electric does full program analysis. Evaluating (e/defn ...) in the REPL doesn't do much, it just saves the sexp for the compiler to analyze it later. So evaling an e/defn in the REPL won't change your running program, you have to save the file for the app to recompile. Does that make sense?
yes, I think so. I assumed it worked more like a clojure.core/defn
, redefining something tangible I could "just touch"
I think I'm getting "hit by" not really getting when code is being run compile time, (like the e/defn
) and when code is executed runtime (like (dom/on "keydown" (e/fn [e] ,,,))
). Because inside the keydown event function I can add code that gets run on the frontend.
Standard clojure rules apply otherwise, the electric function will run when you call it. A keydown handler will run when you press a key ;)
@U3X7174KS see https://github.com/hyperfiddle/electric-starter-app/blob/main/src/user.cljs#L1
^:dev/always
is a shadow-cljs directive to rebuild this file whenever shadow runs
@U3X7174KS you want to do a zoom call? Any of us are happy to give you a jump start. It also helps us figure out what FAQs people have and know what docs to write next
I appreciate the offer, but a bit of async Q/A was exactly what I needed right now. Plus, it's soon midnight here in GMT+1!
On docs: I think the talks you've given are a great motivation for why electric needs to exist. I want this to succeed. But then I'm asking myself ... just how can I use this to do something specific, where choosing Electic makes sense? Something like the https://reagent-project.github.io/ or the https://day8.github.io/re-frame/re-frame/, where I'm guided through tasks that get gradually more complex, and there's motivation for why I would want to do the things that appear in the guide. Perhaps that could tackle some of the questions I had above. What does it mean that it's a compiler? That it's all async? How does that give Electric leverage, and what kinds of limitations does it bring? (my kneejerk reaction right now, I might be wrong!)
ty for the feedback!
Have you looked at the demos in the main repo?
in main repo, run clj -A:dev -X user/main
, demo code is in https://github.com/hyperfiddle/electric/tree/master/src-docs/user
The reagent intro ends with todomvc, you can compare that to https://github.com/hyperfiddle/electric/blob/master/src-docs/user/demo_5_todomvc.cljc. Disregarding empty lines and diagnostics they come out largely the same LOC. So what's different?
• less moving parts. No useEffect
, useRef
, ratoms. Larger codebases would also start using useState
, useMemo
, useContext
and whatnot. Electric is just reactive clojure, not much new to learn.
• the electric version has realtime sync. Open 2 tabs and see a change in 1 tab show up in the other. No additional code required. Now think how would you extend the reagent demo to do the same
◦ write the backend part separately
◦ pick a transport protocol
◦ update the reagent code to handle changes flowing in from the backend
▪︎ and this wouldn't be the same pretty code because now you need message handlers that mutate your UI or state.
All of this could easily triple the code size and introduce so much coordination you now need to write, debug and maintain yourself! The toll on your reasoning of the code is huge too, because now you have to keep all these other things in your head when making a change.
As of 967f03df
, I appear to be getting OutOfMemoryError Java heap space
when running clj -A:dev -X user/main
. Linux on a 16 GB thinkpad laptop. Happy to provide more details.
> Have you looked at the demos in the main repo?
I hadn't before you mentioned them. I've spent a bit of time with them now, and it's helpful. I find user.demo-4-webview
especially interesting, model, view and controller on one page, all reactive.
@U3X7174KS I believe that's a compile-time error, -Xss2m fixes it for us (the compiler uses a lot of stack - presumably will be less when we optimize it soon). Please confirm that -Xmx1g is needed as well? That I have not seen before
@U09K620SG this is weird - I'm seeing a bit of different behavior.
Observations:
1. When I've already been able to compile the app once, I'm not able to reproduce any compile errors. But when I clj -A:dev -X user/main
after a clean git clone
or after I've removed untracked files (`git clean -fxd`), I see errors.
2. But after git clean -fxd
, I see errors. (I need to run git clean -fxd
between each time I test).
a. Neither opt: StackoverflowError
.
b. Only -Xss2m
: OutOfMemoryError Java heap space
c. Only -Xmx1G
: Working
d. Both -Xss2m
and -Xmx1G
: Working.
So on my system, -Xss2m
doesn't appear to have any effect other than giving me different errors, whereas -Xmx1G
appears to solve eveything.
Thanks, we will discuss as a team - please keep me posted if you encounter more issues
Trying to follow along in this thread about how to use the REPL. Not quite getting it yet, but will keep trying. 😃 Btw, the link to photon_1_lang,clj above doesn't work. It's renamed: https://github.com/hyperfiddle/electric/blob/master/src-docs/user/electric/electric_1_lang.cljc (For anyone else finding this thread).
@U0ETXRFEW i have a moment now do u want to zoom?
Does a huddle here work for you? I can zoom too, but just easier with huddle, and anyone can join easier.
dming
okay we kinda have to rethink our options now with multiplayer canvas as a stock component 😼
I notice in the todo app we have TodoCreate
(e/defn TodoCreate []
(e/client
(InputSubmit. (e/fn [v]
(e/server
(e/discard
(d/transact! !conn [{:task/description v
:task/status :active}])))))))
e/server
makes sense and so does e/transact
but why e/discard
The value from the e/server gets transferred to the client and the return value of the transaction is not serializable. e/discard
just returns nil
oh cool, so i kinda understnd -- do you mean serializable like if i wanted to inline the result of the call
(d/transact! ...) returns like a connection or something as the return value, which can't cross from server to client
you'll get a warning in the console "unserializable reference error" - it is harmless
returning nil will send nil instead of trying (and failing) to send the reference, resolving the warning
for example, (e/client (println (e/server (type 1)))) will try to send a java Class type to the client and print it at the browser console, which will fail to serialize and you'll get an "unserializable reference transfer" warning
no problem 🙂 Standard clojure evaluation rules apply, so (e/server 10)
returns 10 to the client. In order to "return" it has to cross a wire in standard web app setup, so the value gets serialized (with transit). This means unserializable data is problematic, e.g. (e/server (Object.))
. In this case you get a warning and the block will return nil
. To signal you don't care about a return value you can use e/discard
, which will return nil
without the warning.
in case you missed it, first zoom group onboarding tomorrow 3pm ET - two slots left https://clojurians.slack.com/archives/C7Q9GSHFV/p1676577250341209?thread_ts=1676472352.651429&cid=C7Q9GSHFV
Someone in my network noted the similarities between Electric Clojure and http://opalang.org/. Was there any inspiration from that project?
We designed electric from first principles. We've seen Opa a long time ago IIRC. Electric is fundamentally different. We've seen other languages with client
and server
markers, but as far as we can tell, they are all doing RPC or message passing. Electric is based on a functional effect system.
looking at their https://github.com/MLstate/opalang/wiki/Hello%2C-chat they use a Network
to broadcast messages between the clients. The clients then use an "on-message" callback to handle incoming messages. This is done in user_update
that directly manipulates the DOM.
Compare to our https://github.com/hyperfiddle/electric/blob/master/src-docs/user/demo_4_chat.cljc where we imitate a DB with an atom (could have used e.g. datascript as well) and attach a single impure operation to the inupt - transact on enter. Notice there's 0 ceremony around this, the app is declared and reactivity takes care of fulfilling that declaration.
The execution model is different (messages vs. reactivity) and therefore the code is written differently as well (impure callbacks vs. pure reactive code).
https://en.wikipedia.org/wiki/Multitier_programming has a list of languages
AFAIK none in that list have the key streaming network planner aspect, which is the critical thing that makes it work, but maybe am missing something, we didn't look too closely