Fork me on GitHub
#hyperfiddle
<
2023-07-29
>
JAtkins04:07:30

Cool thing:

👍 2
🔥 2
JAtkins04:07:22

Electric + electron apps are gonna be fire. I threw this together in ~30m. https://github.com/JJ-Atkinson/electric-starter-app/tree/54c6f63c44cfe214b20efc133e8996653c5803a0

🔥 2
Dustin Getz11:07:30

I get Missing client program manifest any idea why?

Dustin Getz11:07:31

oh, missing package.json

Dustin Getz12:07:36

also missing file-information.edn what is this?

JAtkins14:07:04

Run bin/kondo

Dustin Getz14:07:52

bin/kondo: line 2: clj-kondo: command not found

JAtkins14:07:13

Ah, so clj kondo the linter. Im Surprised you don’t have it. I can put that file here temporarily. I’ll be back with it in 10m

Dustin Getz14:07:45

it's integrated with vscode

Dustin Getz14:07:23

I jsut don't want to guess what your dev setup might be

Dustin Getz14:07:03

i will run `

brew install borkdude/brew/clj-kondo
`

JAtkins14:07:23

Ah, yeah IJ + nix + a local repo with a bunch of project specific tools

JAtkins14:07:44

That should do it

Dustin Getz14:07:58

i have the app partially up now, there are compile time errors such as

Dustin Getz14:07:09

Can you commit and push the version that ran?

JAtkins14:07:27

Weird it was running when I pushed. I’ll restart everything at try again

👍 2
JAtkins15:07:08

Ok give it a pull

JAtkins15:07:24

5f58bc5dd1f6d45b16f8bfce732d295521ebef55

JAtkins15:07:08

I must've renamed the var just before pushing and not got an error, or I didn't see it.

Dustin Getz15:07:33

it builds now but with javascript errors in console, do you have them also?

Dustin Getz15:07:45

main.js:1426 TypeError: add[0].set is not a function

Dustin Getz15:07:15

i also had to add package.json from the starter examples app

thinking-face 2
JAtkins15:07:24

Weird. I don’t know what that js error is - can’t reproduce.

Dustin Getz15:07:47

also i get

core.cljs:3953 Cannot read properties of undefined (reading 'cljs$lang$applyTo')

 in ( contrib.electric-codemirror/codemirror {:parent #object[HTMLDivElement [object HTMLDivElement]]} ) in contrib/electric_codemirror.cljc

👀 2
Dustin Getz15:07:07

when i click on a var to try to trigger "open files"

JAtkins15:07:13

re. package.json yeah that's my fault. I pulled a stupid and didn't add it to git. I'm clearing my node-modules now and restarting

Dustin Getz15:07:25

we might have mismatched package.json

Dustin Getz15:07:35

i dont understand the cm error

Dustin Getz15:07:54

its probably caused by the js error prior, which is in codemirror stack frame

JAtkins15:07:45

ok package.json pushed

JAtkins15:07:17

if that doesn't resolve things I'm not really sure what will

Dustin Getz15:07:23

is nix managing package.json?

Dustin Getz15:07:27

or npm or yarn ?

JAtkins15:07:37

just making sure npm is on the path

Dustin Getz15:07:44

works now !

🎉 2
JAtkins15:07:10

sounds like it was likely related to the package.json mismatch?

JAtkins15:07:27

yeah, my apologies. not sure why it wasn't already added to git but 🤷

Dustin Getz15:07:28

do you know why Kondo doesn't see the vars in the todo-app ns?

JAtkins15:07:01

which ns? that file doesn't exist afaict

Dustin Getz15:07:10

app.todo-list

JAtkins15:07:57

it's a take 5 so the screen isn't just filled with vars

JAtkins16:07:22

It's been a wacky couple months, but I think I've got time again to try out the pathom + electric stuff. will report back soon I hope with something interesting on that front

Dustin Getz16:07:40

looking forward to that!

JAtkins16:07:53

Lol you caught me scrolling Twitter. Btw the reason the defn line wasn’t visible in one of the boxes is that you hadn’t rerun kondo and re loaded the slurped def

JAtkins16:07:17

Kondo has line numbers but those change when you edit the file

Dustin Getz16:07:32

oh did i f up the video

JAtkins16:07:50

Not really just mentioning it in case you didn’t see

Dustin Getz16:07:56

i hadnn't, damn

JAtkins16:07:24

Up to you if ya wanna change it. I only noticed cuz I did the same thing a couple times last night.

Dustin Getz16:07:34

will you put this code in the public domain, i might run with it

JAtkins16:07:03

Which code?

Dustin Getz16:07:19

the whole repo, i have to be careful with licenses

Dustin Getz16:07:24

the UI and source-parser

Dustin Getz16:07:35

we already have something similar I am just dotting is and crossing ts

JAtkins16:07:39

Ah fair enough. Yes this repo is all oss

JAtkins16:07:52

I’ll be explicit in the gh fork

JAtkins16:07:11

ok, added the unlicense

👍 2
JAtkins16:07:22

If it weren't such a dead end time sink, this would be pretty fun to turn into the lighttable code bubbles thing that never was implemented.

Joseph Winston14:07:57

Warning -- New Electric User I'm seeing errors like below. I thought that they were from me using try/catch but it is something different. Ideas?

clj -A:dev -X user/main
Starting Electric compiler and server...
shadow-cljs - server version: 2.20.1 running at 
shadow-cljs - nREPL server started on port 9001
[:dev] Configuring build.
[:dev] Compiling ...
[:dev] Build failure:
------ ERROR -------------------------------------------------------------------
 File: /src/user.cljs:8:3
--------------------------------------------------------------------------------
   5 |     hyperfiddle.electric-dom2))
   6 | 
   7 | (def electric-main
   8 |   (hyperfiddle.electric/boot ; Electric macroexpansion - Clojure to signals compiler
---------^----------------------------------------------------------------------
Encountered error when macroexpanding hyperfiddle.electric/boot.
Unable to resolve symbol: e/*http-request*
{:file "app/dashboard.cljc", :in [e/*http-request*]}
ExceptionInfo: Unable to resolve symbol: e/*http-request*
        hyperfiddle.electric.impl.compiler/analyze-form (compiler.clj:647)
        hyperfiddle.electric.impl.compiler/analyze-form (compiler.clj:632)
        hyperfiddle.electric.impl.compiler/eval30807/analyze--30813/fn--30818/fn--30819 (compiler.clj:700)
        hyperfiddle.electric.impl.compiler/eval30807/analyze--30813/fn--30818 (compiler.clj:699)
        hyperfiddle.electric.impl.compiler/eval30807/analyze--30813 (compiler.clj:688)
        hyperfiddle.electric/boot (electric.cljc:378)
        hyperfiddle.electric/boot (electric.cljc:373)
...

Dustin Getz14:07:18

try wrapping e/http-request in e/server

Joseph Winston14:07:31

Cool -- Will do.

Joseph Winston14:07:04

Something like this from the starter app? If that is correct, then II still have the problem

(let [session-id 
         (e/server (get-in e/*http-request* [:headers "sec-websocket-key"]))
         api-key 
         (e/server (get-in e/*http-request* [:cookies "username" :value]))
         ]
...

Joseph Winston14:07:26

I think that I see the problem in my code. Let me try a few more things...

Joseph Winston14:07:20

Stupid typo in a :require file (misnamed variable).

Dustin Getz16:07:43

Cleaned up CLJ-Kondo var explorer by @jatkin

🎉 12
Reut Sharabani21:07:19

I want to supply a callback to cytoscape and I get an error when clicking (invoking e/fn callback): n.call is not a function My code:

(dom/div (dom/props {:id "cy"
                     :class "cy"})
  (let [container (.getElementById js/document "cy")
        opts (get-cy-opts nodes edges container)]
    (.use cytoscape cy-elk)
    (let [cy (cytoscape (clj->js opts))]
      ;; I guess error is in this callback
      (.on cy "tap" "node" (e/fn [e]
                             (let [node-id (-> e .-target .id)]
                             (println "clicked" node-id)
                             (e/server (println "clicked" node-id)))))))
   nil)
Is e/fn not interchangeable with functions (specifically for callbacks)? How would I update the server that a selection was made? Specifically I want to update my db but I can't access it for updates (`!conn`) on the client afaict. For now it just attempts to print what node was clicked on the client and switch to the server and print there as well.

Dustin Getz21:07:07

yes e/fn and clojure.core/fn are different types

Dustin Getz21:07:13

the quick dirty way forward is to swap an atom in a clojure callback and then watch the atom from electric - this is the side effect way

Dustin Getz21:07:44

the better way is to adapt the event emitter into a missionary flow with m/observe and then use ‘new’ to await the missionary flow from electric - this is the functional way. search m/observe for several examples

Reut Sharabani21:07:48

I thought about an atom side effect but was wondering if there is something simpler. I will search m/observe but I don't know anything about it.

Dustin Getz21:07:40

atom way is 100% ok

Reut Sharabani21:07:20

actually I don't know how to do it. I know how to use an atom on the backend and watch it on the front end, but I don't know how to pass values the other way without an e/server block (which is basically the network). I'll have to think about it.

Dustin Getz21:07:44

you need an atom on the frontend in local scope to mediate the bridge

Dustin Getz21:07:51

callback swaps local atom

Dustin Getz21:07:04

watch atom to get electric reactive value

Dustin Getz21:07:20

now you can use e/server on that value

Reut Sharabani21:07:34

I can watch client atoms? I thought it only works the other way around. Let me mess with it for a bit see if I understand

Dustin Getz21:07:31

(when-some [x (e/watch !mediator)] (e/server (println x)))

Dustin Getz21:07:41

that’s a “callback”

Dustin Getz21:07:12

maybe you need to reset atom to nil after it depends on your use case

Reut Sharabani21:07:54

I understand the other stuff, just the fact you can e/watch on the client was missing to me

👀 2
Dustin Getz21:07:56

it is probably worth reading about the m/observe approach too even if you don’t use it, because you’ll end up there eventually it just might take a couple tries for it to “click”

Reut Sharabani21:07:47

yeah it feels kind of familiar jargon-wise because I used to use rx-java a lot (even contributed an zip operator a long time ago)

Reut Sharabani21:07:53

but I don't know if it's actually the same

Reut Sharabani21:07:25

one day I'll have a big enough commitment to read about missionary 🙂

Dustin Getz21:07:12

oh ok great yeah missionary is like that

Dustin Getz21:07:22

it’s learned best by example

Reut Sharabani22:07:07

atom works btw! I was just missing that I can (e/client (e/watch some-atom)) and not just (e/server (e/watch some-atom))

👍 4
braai engineer22:07:38

If cc/let implies a do, and do is parallel in Electric, what is the execution order of let in Electric? Do the let-clauses execute sequentially or in order of topological dependence if a subsequent clause depends on the one above, or is there an m/sp in there somewhere?

Dustin Getz22:07:46

the let bindings evaluate sequentially when the let boots, the do is implicit only in the let body

👍 2
Dustin Getz22:07:23

any unused let binding will still be evaluated for effect (same as clojure)

Dustin Getz22:07:41

even when we say do is parallel, it still is touched left to right on first render

👍 2
Dustin Getz11:07:10

Your question is great – let may indeed match semantics of do in terms of what happens if an exception is thrown (e.g. pending) in an intermediate let binding – i think the rest of the let bindings will still evaluate

👍 2
braai engineer11:07:37

If one of the let bindings changes, do all the let-clauses evaluate in lexical order?

braai engineer11:07:10

e.g.

(let [a X
      _ (prn "thing")
      b Y ;; e.g. if Y changes
      c Z]
  (+ a b c))

Dustin Getz11:07:22

reactive updates are fine-grained, only the expression whose input changed will rerun

Dustin Getz11:07:46

so put the prn last, if Y changes the prn should not "reboot" because it's argument is constant

Dustin Getz11:07:03

@U09FL65DK is this correct have I made any mistake?

braai engineer11:07:16

so prn won't rerun unless it touches Y?

Dustin Getz11:07:50

prn has invariable arguments (the string literal is constant), wrt updates the topographical ordering is irrelevant

braai engineer11:07:31

so my understanding of Electric today is that the prn will only run on boot (in order of let-clauses) unless prn touches a binding that changed, in which case it will rerun in...unspecified order?

braai engineer11:07:46

e.g. if there are two prns that touch Y

braai engineer11:07:03

(let [a X
      b Y ;; e.g. if Y changes
      _ (prn :first Y) ;; are order of prn effects defined?
      _ (prn :second Y)
      c Z]
  (+ a b c))

Dustin Getz11:07:39

here, ummm i think when Y updates the two prns will be touched in lexical order (left to right) though technically this may be undefined behavior today

Dustin Getz11:07:14

they will absolutely be touched in the same reactive propagation frame, you'll see neither or both, never inconsistent

braai engineer11:07:36

asking because you can imagine clojure peeps expect let to be ordered, so if two effects depend on Y (and only Y), you might expect both effects to run in the order they are defined. I don't have a concrete case, but you could imagine transacting something and then blocking on the tx.

Dustin Getz11:07:36

i think we currently satisfy that

Dustin Getz11:07:29

note it's complicated, if instead of prn we have more Electric components (deeper DAG), inside that DAG may be more latency

xificurC07:07:23

ordering is largely UB (undefined behavior) and subject to change. Today those 2 prns will run in program order. Order of them re-running when Y changes is also UB. If you have a specific case where ordering is necessary we can take a look

Dustin Getz11:07:36

@U09FL65DK i think we intended to lock this down (as the impl currently does the desired thing despite not being formally guaranteed), right? Subject to any larger design changes the future might hold

xificurC11:07:48

we discussed ordering guarantees but that issue is on the backlog. So yes, I don't see any changes happening to that anytime soon

👍 2
braai engineer12:07:15

No specific cases right now - just trying to understand Electric's behaviour. Anytime I need to think about order I just put it in a cc/defn outside e-fn.