Hi @hiskennyness. I know you’re busy polishing Matrix and don’t mean to distract, but I mentioned Matrix in the #hyperfiddle Electric forum in curiosity of how Electrics’s notion of fine-grained reactivity compared to Matrix’s. https://clojurians.slack.com/archives/C7Q9GSHFV/p1681451397542829 I would assume Matrix has an edge for DX on a client, given its pedigree. If so I might like to use them together..? You’ve been so generous about (repeatedly) highlighting why these properties are important. I’m sold and am eager to understand if they are fully manifesting in other progressive ui frameworks (like Membrane, Electric, HumbleUI, etc) that have complementary powers, and why/why not, etc.
Sure is a lot going on these days in both the reactive and UI realms! Yes, Isaw your note in the Electric channel. I like their end-to-end UX, and once had a rough version of that thx to some clever macrology, and it was indeed a joy. I am keeping an eye on that project for sure, and the others you mentioned. I am not sure what a MX/Electric hybrid would look like. Any hybrid might lose the seamlessness that is so cool in Electric. Dunno, I would have to peruse their tutorials to see if MX can add anything. If MX can add anything, one can stare at Web/MX to see how I have blended MX with HTML/CSS. These exercises can take anywhere from a month to two or three in the case of Dart/Flutter, because of their dense OO.
Interesting. I am committed to learning Matrix better to feel the DX out regardless. The new docs and tutorials are very engaging and encouraging, Thank you!!! ps. I’m still always trying to clear runway to play with these cool new techs, but doing too much speculative analysis 👀… instead of just coding into knowledge .
If you didn’t see it, Tonsky posted some notes on his perspective of state management. https://tonsky.notion.site/State-Management-d45ce7c481fa4c0fa1c82accbcbfc166
I took another quick look at the Electric samples. Not sure how MX would fit in. Is there sth specific in MX you wish Electric offered? 🤔
No, not at the moment. I need to get my hands dirty more, and feel out the dx of both (an definitely Membrane too). I was just trying to get oriented to the nature of “fine-grained reactivity” across frameworks. I think that is why I was inviting the discussion with @smith.adriane . I want to use all these frameworks on their merits. But after 20 years of slog, I don’t want to settle for less than maxiumum reactivity and dev ux. And want to understand where principled tradeoffs or scope constraints are happening. Electric offers (an addition to Matrix) > maximum possible granularity at the quanta of a single point in the AST : attribute, expression, whatever… I take that to enable the notion of “Property-based dataflow”, as opposed to the Flux pattern, or some other once-removed store/query-api.
{our messages are crossing, beware! :)] Actually, @chromalchemy, I just noticed the Reagent Interop example: https://electric-examples-app.fly.dev/user.demo-reagent-interop!ReagentInterop But that might exist just because React is such a big target, and Electric does not want to force folks to use its own DOM capabilities. 🤷
I saw where an example of Electric where a function had three print statements, only the second would run because only the second referenced a value that changed. That sounds more granular than a property!
Yes, think dustin has said as much. He said he lost a year trying to build directly on React, (and admitted not looking harder at Hoplon was a “huge miss”). He doesn’t seem to be huge fan atm. But it was very directly demanded i’m sure, at least for people to integrate ui component libs and existing codebases.
> function with three print statements, only the second would run because only the second referenced a value that changed. Yeah, that’s wild. Kind of challenges held notions of idempotence and pure functions and such
One big differentiator with membrane is that it focuses primarily on "what". In other words, where's the data? Do we have the right model? All the frameworks I've looked at seemed to be focused on how and when.
What is maximum reactivity?
That’s the thing. I don’t really know. I defer to the experts. It seems like a broad term. Is one persons meaningful reactivity is another person’s dead end (or even tarpit)? I probably need to interrogate my own experience and intuitions more deeply.
When thinking about granularity, a really common trade-off is between throughput and latency. Since many frameworks focus on how and when, they bake in this decision. I think the ideal solution is to specify the what, have good defaults for providing how/when, and then have ways to choose or provide different implementations when the use case demands it.
It's tough. Discussing UI architecture is really hard because we've done a pretty poor job of defining terms. UI also tends to be heavily coupled (unnecessarily) to the rest of the application. I talked about this in my london clojurians presentation, but most UI frameworks aren't about user interfaces. I think it must be really difficult for users to figure out what's actually happening.
For example, an application may have a database, dependency injection, networking, etc, but that's not really part of the user interface (or at least it shouldn't be coupled to it)
Oh, if dustin was open to Hoplon, maybe there is a fit. Meanwhile, @smith.adriane wrote: "One big differentiator with membrane is that it focuses primarily on "what". In other words, where's the data? Do we have the right model? All the frameworks I've looked at seemed to be focused on how and when." Not to start a famous Abbot & Costello bit 🙂 , but can you give examples of what, how, and when? Maybe a link to a discussion of the distinction. Curious! Agreed on terminology being a lost cause. 🙂
btw, @chromalchemy, got a mini project in mind? I'll be happy to get you started on an exploratory exercise, win web/mx or f/mx.
The talk really doesn't have much specific to membrane, but I had to choose the title of the talk months before I wrote the talk.
Thx, I will check that out later.
@smith.adriane So true, there is so much incidental complexity baked into most ui, it’s hard to parse out what the tradeoffs are. I deeply appreciate your first-principles orientation to help move this forward. I think to me good reactivity points toward a reduction in practical API surface area . An integrated api to deal with ui in a higher level way. Instead of layering multiple api’s (that don’t always compose elegantly) for different concerns. Ex: html + css + xhr pattern for external data fetching + reactive pattern for local data fetching and updates + svg + (js) library apis + cljs/js interop + cljs library apis + clj library apis + database api, etc…. Electric is not trying to boil all of this down, or abstract over all of it (atm). but the radical reduction in LOC hints at the huge value for developer experience. More terse, expressive, declarative, succinct syntax was a big motivation for me to get into Clojure in the first place. Similarly, Matrix is positioned to meaningfully abstract over and integrate browser/flutter client api’s. And i think Membrane also offers some simplification by eschewing the browser for a more pure runtime. etc.
@hiskennyness I will take you up on that. I have a client project where I tried to compute my own state reactions for a small widget, and it quickly turned into 1000 LOC and was buggy when I wanted to add a feature. This is for a web-based ecommerce project that is not using react. It’s not an SPA , so I was hesitant to use cljs. But i ended using it anyway because I am not good at js. So if i’m running cljs, might as well throw in matrix and be off to the races (that fun part I keep hearing about 😉). Unless Matrix it’s too heavy or slow on pageload for this non-SPA case.d
OK, fire away. If MX needs tuning I can tackle that, too. Just need to see a slow use case to tackle. And I have had bad experiences with minification (but that might have been the JS version).
@smith.adriane I am looking towards Membrane to provide first class reactivity in a pure JVM dev environment, like your spreadsheet. Enabling fluid experimental workflows and next level “moldability” and “direct manipulation”, without getting bogged down in too much incidental complexity. (of course to deploy a usable app would be nice too!)
Just some psuedo code examples:
(defn todo-list-item [todo]
(li
{:class (cF (when (td-completed todo)
"completed"))}
...))
If I were to try to explain this snippet. I would say:
• todo is a mutable reference to some data that represents a todo item
• The class of the li will get updated immediately after the todo reference changes
An alternative approach might look something like:
(defn todo-list-item [todo]
(li
{:class (when (:complete? todo)
"completed")}
...))
A brief description:
• todo is an immutable value that represents a todo item
• The class depends on the :complete? key of the todo item
• here, todo-list-item is a pure function that takes an immutable value and returns an immutable value
• when or how is not specified
Obviously, you usually want the todo-list-item to be updated when some todo reference changes, but there are times in dev, debugging, and test where you don't. Additionally, if you have a data description that composes todo-list-item with a reference and know that the completed class depends on the :complete? key of the todo reference, then it's easy to compose a machine that takes care of when and how those updates happen. Further, if you know that you're dealing with 10,000 todo items for some reason, you can compose an alternative machine that batches and schedules the updates. The "when and how" are treated independently of the "what".To be fair, the developer experience for membrane isn't finished yet, but I'm working on it!
Interesting. Thanks for writing up the distinction
Hmm. So the alternative ...
(defn todo-list-item [todo]
(li
{:class (when (:complete? todo)
"completed")}
...))
.... is OK because there is no indication of how it works. It just declares an invariant that somehow must be enforced at runtime as the complete? property changes.
Well, sure, but look what a disaster the React team got into when they tackled the "somehow". I do not know of a UI framework that does not strive for the declarative "what". When I hear of a new UI and check it out, I am confident I will find "declarative"; my only question is how transparent and granular is the "how". That is where the D/X soars, or stalls in the, eg, Reflux boilerplate mud.
So agreed: "what" can be specified in the abstract. Indeed, during this sharing process of mine in which I have tried to work out how to explain MX to folks, I have found myself thinking it might be interesting to specify the abstract "what" necessary for a real U/X.
That said, as someone who works on UIs quite a bit, and has experienced many other frameworks, the important question is "how" we get to "what", because it makes all the difference in the world.Food for thought: back in the seventies, the AI pioneers said they did not care how the biological brain worked, they were going to achieve the "what" of intelligent behavior any way they could. They still are not close, but they were using deeper and deeper neural nets last time I looked. Well, one researcher told me it had become all statistics. That was a decade ago. 🤔
"The "when and how" are treated independently of the "what"." A great example of this is that both MobX and Matrix avoid "glitches" -- transient state inconsistencies -- by always checking if a value used in an expression when property is read. So the calculation of A can read B, kicking off the calculation of B, if it is not yet current. The "what" is that no code ever sees an obsolete value. The "how" is partly the same -- JIT currency assurance -- but partly different: we use completely different ways of deciding if a value is current with the latest change. That was fun to see. 🙂 "it's easy to compose a machine that takes care of when and how those updates happen" sob It took me years to write that code?! sob 🙂
My point with the example:
(defn todo-list-item [todo]
(li
{:class (cF (when (td-completed todo)
"completed"))}
...))
is that this code combines the specification of "what", "when", and "how". My opinion is that the developer should specify the "what", have good defaults for the "when" and "how", and when necessary, have knobs for adjusting the "when" and "how" without making any changes to the specification of the "what".
> "it's easy to compose a machine that takes care of when and how those updates happen"
I wasn't trying to trivialize your work. The reason composing the machine isn't too bad is because of libraries like Matrix that do that part well. My main gripe is requiring the developer to specify the "what", "when", and "how" rather than accepting just a specification of "what" and providing good defaults.