"Is Matrix-cljs production ready also?" Matrix/cells is quite mature, having been used for 25 years, the last twenty without much change. The port to CLJC did let me clean up one benign internal hiccup. That said, I plan to add some safeguards to throw errors sooner on bad inputs, and report problems better, so folks other than its author can debug efficiently. But first I am doing some tutorial doc, and reference doc come to think of it. mxWeb is just a thin wrapper around MDN-standard HTML/CSS/XHR. I think a JS pro could find a number of things to tidy up, and look forward to someone like that getting involved. And it may leak resources, but Matrix has lifecycle support for models/cells shuffling off this mortal coil so we have a place to hook anything like that. I have started on a wrapper for ReactNative since that seems to be the best native game in town, pending the arrival of ClojureDart. Again, mxRN is just a thin (200LOC!) wrapper around RN, so it just needs a shakedown cruise on a serious app.
Getting back to hyperfiddle, I think that is just a larger scale solution, and quite exciting at that. The dependency "Datomic peer API or other Datomic-like immutable database supporting entity semantics and speculative transactions" seems significant but worth it if the rest pans out. I am on board with paying a price for profound productivity wins, and I think hyperfiddle is lined up for such a win. OTOH, if you look at what Matrix does with network requests, it too smooths out the seams: an async network request gets wrapped up as just another reactive thing. Some view element has a formula that spawns an XHR, and has another view property formula reactively watching the response. I did a couple of tests where so-called "synaptic cells" (think "anonymous function") inside a cell could then do things like: • not produce any response until all N XHRs were completed; • produce a response from the first of N XHRs to complete; and • execute a chain of XHRs where each depended on the prior XHR. All that ^^ was done declaratively without JS promises or anything. It works because Matrix was developed in the UI context, with our beloved user randomly and asynchronously generating mouse clicks and key clicks. The network is just another source of async input. Hyperfiddle seems to go further, with nested expressions evaluated on the server, but in a Matrix app the granularity of reactivity is as fine as it can get, the individual property. So having a property dedicated to a network interaction is something we would do anyway. I'll give a less hand-wavy answer after I learn a bit more about HF.
Final thought: there is indeed no free lunch, and Matrix indeed has an "artificial split". To leverage reactivity, in Matrix we need to create a layer of reactive Matrix models that work like something we really want to use. eg, XHRs are not reactive off the shelf, so we have to create what I call a proxy XHR that is reactive: https://github.com/kennytilton/matrix/blob/b8d383483d8f488059a86cc8e613bfb0b1bea287/cljs/mxxhr/src/tiltontec/mxxhr/core.cljc#L209 To minimize the split, we should make the proxy look as much like the real thing as possible. eg, mxWeb DIV macro parameters map to MDN DIV attributes and children directly, then an additional/optional parameter is available for reactive properties. There is no need for this similarity, but it does make reactive libraries more approachable. The good news is that "wrapping" things like XHR is technically straightforward, and just needs to be done once. mxWeb is fairly substantial LOC-wise because it is wrapping HTML and CSS, but most things we want to be reactive can be handled with a couple hundred lines of code. And wrapping is optional, until one realizes how effective and fun it is to program this way. 🙂
Thank you for all the insights and cool takes. All of your thoughtfulness gives me greater confidence to try the library out.
It might even be point of least resistance for me with my fond Hoplon experience. I do remember some of the advanced users were struggling with some subtleties of debouncing and with-tpl comprehension pools. So it’s good to hear that these characteristics have a story.
My experience of cells was coming up with ad-hoc data structures organically, but feeling like I was missing something by not having a normalized structure to serialize or leverage different query languages. I used Specter inside formula cells to surgically access data, which seemed to make the lenses idea radically powerful. Not sure of the performance characteristics, but it seemed to have decent scale. But on both sides of breaking apart the data, then specifying arbitrary paths through it, there is an open ability to make an arbitrary database, that is probably runtime specific. I guess that’s the point, to make data specific to the runtime (UI)! Where do you factor the tension to be able to produce or interface with a normalized schema, for standardized data access, from like a Datalog database? Part of my journey has been basically gradually re-inventing a database, for ad-hoc data needs as they arise. (Re)discovering the value of indexing (joins) to support graph-like queries across data structures. And basically using generic Clojure data access as a query language. I’m not sure wether this this is good or bad. Good in the sense of discovery, empowerment and customization. Or bad, as in a costly tax on on just doing UI.
Also on a more practical note. Could Matrix html-tags as cell work with htmx powered html attributes? https://htmx.org/
Could I start small and embed some matrix in an application with an existing page render / reload mechanism? Like a mount html function? In Hoplon, you would “blow away” whatever was already rendered. Is there a recommended way to “hydrate” an existing DOM with matrix?
I'll take an easy one first: " In Hoplon, you would “blow away” whatever was already rendered. Is there a recommended way to “hydrate” an existing DOM with matrix? (edited) " Matrix knows exactly what changed, and whacks the DOM accordingly. If a state change does no more than make the rule for the enabled attribute of a button come up with false instead of true, the only thing mxWeb does is set the disabled attribute on that button. This was like the second thing we had to do with Cells. The application was a Mac OS 9 GUI and 100mhz was not fast enough for "redraw the whole thing each time". Funny you should ask, one of my next tasks is to reinstate the mxweb-trace mechanism so we can watch the console to see the action. Here again, not a vital diagnostic: mxWeb draws us into handling every DOM element as an independent reactive model (object) with every DOM attribute and its children with their own formulas, so it is just how mxWeb apps always work. Oh, and Matrix optimizes away the reactive mechanism wherever it sees a formula did not in fact depend on sth else, so there is no needless work even to determine if some chunk of the DOM is invariant.
Can I mix Matrix nodes with pre-rendered html? What are the constraints of initialization? What if I wanted to make 2 separate DOM nodes reactive in the same context? Would I need to mount both nodes separately? Does Matrix need to take over the entire DOM? Would pre-rendered html need to be mirrored in Matrix code, when adding reactivity? I guess Im speaking of the notion of “hydrating” static elements into a reactive runtime.
Still responding selectively, Matrix takes a target DOM node and renders into that, and does not care what else is going on around it. So not, it does need to run the whole show. Not sure if I understand "2 separate DOM nodes reactive in the same context", surprisingly that should not be a problem. There would be one "data-pulse", a 64-bit reactive clock if you will, and that would rule both Matrix trees. If I understood you. One object in one tree could easily depend on or provide to an object in a separate tree.
I'll catch up with other points later.
@chagas.vis has joined the channel
"What are the constraints of initialization?" Not sure what you mean, but I think the answer is none. An app starts as a single instance with a formula for children, recursively growing as Matrix internals automatically kick off every formula and "observe" every cell to execute any necessary side-effects of initial values. So there is a nice irony here: the app structure springs to life through eager evaluation "pulling" objects and values into existence, then thereafter works by "push" as imperative assignment to cell inputs triggers dataflow.