hyperfiddle

Nikolai 2025-02-11T17:41:33.175899Z

Hello, I have experimented with implementing a counter in Electric 3 as:

(e/defn Counter [p]
 (case (e/Task (m/sleep 100))
   (e/amb p (Counter (inc p)))))

(dom/div (dom/text (Counter 0)))
It works, but is eating all the memory after some time. Is there a solution for this, concerning freeing somehow the resources? What am I doing wrong? Thanks for any insights!

Dustin Getz (Hyperfiddle) 2025-02-11T17:42:22.319709Z

ha yes that is building a collection like [1 2 3 4 5 ...], and then racing the elements into a single dom/text. use (dom/text (pr-str (e/as-vec (Counter 0)))) to see the whole collection

Dustin Getz (Hyperfiddle) 2025-02-11T17:42:57.543169Z

have you seen https://electric.hyperfiddle.net/tutorial/counter

Nikolai 2025-02-11T17:55:48.765989Z

Thanks, Dustin! Yes, it is a great implementation and tutorial. Actually, I would like to have something similar to Missionary flow clock implementation, but doing with Electric (e/amb), growing and freeing in time.

(m/ap (loop [] (m/amb nil (do (m/? (m/sleep 100)) (recur))))) 
Does it make sense?

Dustin Getz (Hyperfiddle) 2025-02-11T17:57:32.056569Z

you can use e/input to use that from Electric

Dustin Getz (Hyperfiddle) 2025-02-11T17:58:06.571239Z

I can probably come up with a native electric version if i have free time later

Nikolai 2025-02-11T18:00:57.689049Z

Yes, I have the implementation with (e/input), but thinking about native Electric exactly!

Dustin Getz (Hyperfiddle) 2025-02-11T18:02:01.799319Z

e/with-cycle

Dustin Getz (Hyperfiddle) 2025-02-11T18:02:37.385459Z

(e/with-cycle [n 0] (+ n (e/Task (m/sleep 100 1))))

🙏 1
Dustin Getz (Hyperfiddle) 2025-02-11T18:03:58.159359Z

not sure if the sleep needs a causal dependency on n

Nikolai 2025-02-11T18:26:37.869849Z

Thanks! As I understand, the implementation with (e/with-cycle) forces the use of an atom, compared to aka "forking flow" with (e/Task) and self referencing? I would also like to explore these direction of the implementation, if possible and reasonable.

Dustin Getz (Hyperfiddle) 2025-02-11T18:30:48.183019Z

We would encourage you not to think of with-cycle as stateful, we consider this a pure functional pattern. The fact that it happens to use an atom internally is because the stateful implementation is equivalent to the fastest possible implementation if we were to reify this as a special form inside the electric compiler

đź’Ż 1
Dustin Getz (Hyperfiddle) 2025-02-11T18:31:48.859459Z

There is state everywhere in electric, every let binding and every function argument is memoized

Dustin Getz (Hyperfiddle) 2025-02-11T18:33:41.138779Z

Another example is how we use atoms to loop up a value in lexical scope, in apps like https://electric.hyperfiddle.net/tutorial/system_properties

🚀 1
Dustin Getz (Hyperfiddle) 2025-02-11T18:34:28.304409Z

in haskell dataflow this can be accomplished with recursive let bindings, and when electric eventually gets that feature, we too can eliminate the explicit atom. But the state will still be present, it is just hidden inside the let 's memo buffer

Dustin Getz (Hyperfiddle) 2025-02-11T19:55:59.772809Z

played a bit more, basically one day perhaps the latter can be syntactically valid, i think it is well defined

(dom/text
  (e/with-cycle [n 0]
    (+ n (e/Task (m/sleep 100 1)))))

(let [n (+ (e/Some (e/amb n 0)) (e/Task (m/sleep 100 1)))] ; recursive binding, i.e. a dataflow loop
  (dom/text n))

đź‘€ 1
Dustin Getz (Hyperfiddle) 2025-02-11T19:57:04.215409Z

(e/Some x) is a made up function which takes the first non-e/amb value from x

Adam Clements 2025-02-11T23:39:38.204019Z

What am I doing wrong?

(e/defn Button [label f]
  (dom/button (dom/props {:class "bg-red-600 text-white rounded px-4 py-1 m-2"
                          :href  "#"
                          :type  "button"})
              (dom/text label)
              (dom/On "click" f nil)))

(e/defn SimpleInput [m-atom k]
  (form/Input (k @m-atom)
    :parse (fn [x] (swap! m-atom assoc k x))
    :class "rounded py-1 px-2 mx-2 shadow border"))

(e/defn Systems []
  (e/server
    (let [current-user* (-> (f/coll db "users") (f/doc "test") f/->atom)
          current-user  (e/watch current-user*)]
      (e/client
        (dom/div
          (dom/props {:class "m-4 space-y-4"})
                 
          (Avatar current-user)
                 
          (let [result (atom {:firstname (get current-user "firstname")
                              :lastname  (get current-user "lastname")})]
            (dom/form
              (style "p-4 border rounded")
              (dom/label (dom/text "First Name:"))
              (SimpleInput result :firstname)
              (dom/text "Last Name:")
              (SimpleInput result :lastname)
                     
              (Button "Save Changes"
                (e/fn []
                  (e/client (js/alert (str "Saving " @result)))
                  (e/server (-> db (f/coll "users")
                              (f/doc "test")
                              (f/assoc! "firstname" (:firstname @result)))))))))))))
On the last line - if I pass a normal clojure function there, it works and runs in the browser, trying to pass an e/fn which does something on the server, clicking the button does nothing now and I can’t figure out what I should be doing differently. can dom/On take an e/fn?u (One thing that does work really nicely though is the reactivity of Firestore, stuff on the page just magically updating when it changes in the db) I realise I should probably be using forms3, but couldn’t figure out how you were meant to style your form (given the visual component is implemented in the same function as the value storage - I get the input rendered as a side effect when trying to put all the components of my form in a map!) and put your own Save/reset buttons etc

xificurC 2025-02-12T08:29:38.101799Z

https://electric.hyperfiddle.net/tutorial/token_explainer to understand how one should use dom/On. The next tutorials cover the current status of forms

Dustin Getz (Hyperfiddle) 2025-02-12T18:26:43.694249Z

> can dom/On take an e/fn No, there is a brief explainer why under "simple free text input" in https://electric.hyperfiddle.net/tutorial/system_properties

Dustin Getz (Hyperfiddle) 2025-02-12T18:27:27.166069Z

Electric v2 did this (allowing "electric callbacks" to process events and potentially switch to the server) but it had many many problems, which led us to the e/Token pattern that @xifi linked above

Dustin Getz (Hyperfiddle) 2025-02-12T18:30:52.927409Z

you can provide a SimpleButton component like this if you really want to think in electric callbacks - but it won't scale to the sophisticated "enterprise" patterns (table pickers and such like in https://electric.hyperfiddle.net/blog/y20250112_data_browser/(:branches)/(9))

Dustin Getz (Hyperfiddle) 2025-02-12T18:36:53.180559Z

here is a https://gist.github.com/dustingetz/b0e7f92ba753e61122a2d4a136bd93dc using MyButton

Dustin Getz (Hyperfiddle) 2025-02-12T18:42:44.081739Z

I realise I should probably be using forms3, but couldn’t figure out how you were meant to style your formyeah i do not think we have "unbundled" the form yet for full layout control (button placement etc) - you didn't do anything wrong. fwiw hyperfiddle.forms-* is very bleeding edge and is intended to express Complicated Enterprise Forms such as this attached. The pattern may not be worth it for less complicated forms, and the pattern is definitely not "finished"

Adam Clements 2025-02-12T19:08:03.603749Z

I mean the pattern I'm intuitively trying to recreate is the formik one more or less, where the form state is a simple map, the form renders based on that state map and the inputs reach in and update the values, the commit function takes the full map and returns a map with any errors etc, which then get simply rendered, and pending states can likewise be managed by just associng the pending state into the map at the start of the commit function

Dustin Getz (Hyperfiddle) 2025-02-12T19:10:58.345129Z

https://electric.hyperfiddle.net/tutorial/form_list is pretty close to that i think? form state as map L15, toggle failure checkbox off to see that committing to one form updates both

Dustin Getz (Hyperfiddle) 2025-02-12T19:13:12.138109Z

here's an unpublished demo with validation at field and form level, w/ working retry state - (edit: fixed link) https://electric.hyperfiddle.net/staffly.staffly!Staffly/:restrict-staff-from-venue/17592186045437/'restrict'

Adam Clements 2025-02-12T19:24:26.165919Z

Not really, that stores the latest value in a map site, but it doesn't style the full state of the form. I.e. does a specific field have validation errors, is it dirty etc, which in formik you'd then use when rendering your form if you wanted to show eg that you've edited a field since last save or have a custom field error or list of field error renderings. The forms3 way all that seems tied into the component implementation so I don't see how you'd decouple it to do anything vaguely custom, and it's very opinionated at it stands. Guess it depends whether you're going for a headless philosophy where the workings are decoupled from the rendering or bootstrap batteries included, everything looks and works the same but you're on your own if you want to do anything different

Dustin Getz (Hyperfiddle) 2025-02-12T19:26:53.468069Z

example of custom : https://electric.hyperfiddle.net/tutorial/todomvc

Dustin Getz (Hyperfiddle) 2025-02-12T19:27:01.470479Z

yes, it uses the forms patterns

Dustin Getz (Hyperfiddle) 2025-02-12T19:27:34.994899Z

(not claiming it's perfect or finished, as i said this is all very bleeding edge, you are basically expected to either read the source or hire us)

Dustin Getz (Hyperfiddle) 2025-02-12T20:20:30.182939Z

oops, above form demo url was broken, fixed link - https://electric.hyperfiddle.net/staffly.staffly!Staffly/:restrict-staff-from-venue/17592186045437/'restrict'