matrix

chromalchemy 2022-03-16T15:14:52.278029Z

Hi @hiskennyness I met you in the Hoplon chat years ago. I just rediscovered your Matrix libraries and documentation from the Reddit link a few days ago. I started stateful front-end dev with Hoplon, and went quite far with it as a cljs/js newbie. I was drawn to the idea that cells can be powerful state mechanism for UI (that I already intuitively new, from spreadsheets!). I didn’t have a lot of experience with Databases and data-access patterns, so the idea of working with (reactive) values directly seemed perfect. I stalled out on practicing front end about the same time Hoplon community went quiet. I hesitated to make the transition to Reagent/Re-frame, partly because of Hoplon/Javelin evangelism/comparisons, but I always wondered if I was “doing it wrong”, because that’s where the mindshare was. Now I code hiccup sometimes for static HTML, but I wonder If I “am doing it wrong” again. I have kept an eye on progressive Clojure UI frameworks: Odoyle Rules, Fulcro, Hyperfiddle, Biff + HTMX (server-size-driven), and now Membrane and HumbleUI (and more…). These span a lot of different concerns, but I am keen to anchor to a UI abstraction that isn’t fundamentally riddled with artificial splits caused by incidental complexity (having grown up with the HTML/CSS/DOM divide schizophrenia). I am especially interested in first-class notions of reactivity, and curious about how Hyperfiddle seems to outline a need to abstract over the entire network distributed computation (using Missionary concurrency) to get reactive UI right.. How would you characterize Matrix with regard to other progressive Clojure attempts to provide a foundation for functional UI (Fulcro, Hyperfiddle)? What distinguishes Cells from some of these contemporary approaches? How does matrix it compare to Reagent/Re-Frame? I understand Matrix is down on Flux. How is Re-Frame different? Is Matrix very similar to Reagent, or more conceptually separated from the React dependency? And more specifically, how would you characterize Matrix with regard to Hoplon (since they share common DNA)? My understanding is that Hoplon is pretty mature, if dormant. Is Matrix-cljs production ready also? Does Matrix address more/different concerns than Hoplon+Javelin? Thanks! And I appreciate the thoughtful write-ups. They give a lot of context to the usability of the paradigm.

kennytilton 2022-03-16T15:35:33.509839Z

Hey, @chromalchemy! I'll have to answer in bits and pieces since those queries cover a lot of ground. Let's start with Matrix v Hoplon/Javelin (HJ). The big one there is that HJ works by code inspection, This has a couple of consequences. First, dependency tracking cannot "see" inside function calls. And in fact one cannot even bury a dependency use inside an arbitrary other function. Matrix, like many reactive engines (REs) works off the call stack, a special var binding, so it has no problem dynamically detecting usage of one cell by another. Second, code inspection, when it sees (if A B C) has to create dependencies on each of A, B, and C. With Matrix and some other REs, dynamic again comes to the rescue: dependencies are "forgotten" when a formula gets kicked off, and re-established iff a dynamic access is made. So in this case we would have dependencies [A B} or [A C}. This saves firing the rule unnecessarily. Not a big deal in most cases! The bigger problem to me is that, with HJ, I cannot wrap a dependency in some useful processing inside a function-- I have to reference it in my formula and then pass that to a function. In trivial rules this is trivial, but then we scale to a serious app and it can be a drag. I know, that is where Cells started until I discovered special variables!

kennytilton 2022-03-16T15:37:48.301029Z

I should add that my recollection of HJ is both fuzzy and perhaps out of date. And to add a little, Cells might be more expressive: it has three kinds of laziness, "anonymous" cells that can sit inside a formula, ephemeral cells for handling events cleanly, prolly more.

kennytilton 2022-03-16T16:04:50.914969Z

Regarding "I understand Matrix is down on Flux. How is Re-Frame different?" You know, I heard rumors that re-frame predates Flux and may even have been its inspiration. Lispers do good work! And re-frame is vastly better than its copycats. But they clump together on the "state separation" axis. It sounds as if you found https://github.com/kennytilton/matrix/wiki/introduction#in-place-state-management-flux-need-not-apply Open the "In depth: A deeper look at the problem with Flux" section for more, including an argument that the Flux pattern, while meant to simplify, was the wrong way to go and actually makes things harder than necessary for developers: "With a separate store, a developer working on a UI component now must work out how to get the state they need from that store. When they want a component event handler to alter the state seen by other UI components, they have to work out what abstract action/event will leave the store with the state where the other components will find it. They may even have to architect new store schema and new reducers when extending functionality." So re-frame/Flux do create an artifact that is more manageable than helter-skelter MVC ("Yay!"), but it is too much of a hassle for the benefit. Matrix, MobX, and others are existence proofs that a separate store is not needed to make state and asynchronous events manageable. More accurately, with Matrix we code locally and the "uni-directional store" is an emergent property we can ignore. (The punch line here is that someone looked at MobX and said, "Great! We can do a Flux based on MobX internals!!" Enter MST: https://mobx-state-tree.js.org/intro/welcome "MobX-State-Tree (also known as MST) is a state container system built on MobX, a functional reactive state library." Priceless.) btw on that last, I spent a couple of days looking for a friendly charting library so I could display the emergent DAG of a running Matrix app, but it looked like more of a heavy lift than could be justified for what would be just a fun exercise. It would have no diagnostic value because, again, with Matrix we code locally and the RE takes care of the DAG.

kennytilton 2022-03-16T16:36:05.619169Z

"I am keen to anchor to a UI abstraction that isn’t fundamentally riddled with artificial splits caused by incidental complexity " Ah, I might be preaching to the choir. Every time I see the phrase "separation of concerns" I read "artificial splits caused by incidental complexity". 🙂 And one of my favorite expressions is "divide and conquer", so I get it. Someone challenged me to argue for the "separate store" approach to see if I grokked it fairly. My answer was, "Easy. I would have done the same thing if I had not accidentally come at it from a different angle. But I would have turned back when the boilerplate appeared." The "accidentally" bit is here: https://tilton.medium.com/the-making-of-cells-5ab873d1e6c7 I think that ^^^ is a vital place to start for many of your concerns. It even gives HJ a shout-out! The one thing it does not make explicit is that Cells's evolution has been driven at every turn by the need to turn out an application. The one exception is lazy cells, which for a long time existed only to shut up a naysayer who said eager would not work for CAD. 🙂 I was not doing CAD, but I took an afternoon to knock off lazy. I used it years later to minimize I/O in my algebra app so past student work got pulled in only as needed for a certain dashboard. I just had that loading in lazy cells so the I/O was not done until some part of the program asked for it, and then it was done reliably. Yay.

kennytilton 2022-03-16T16:40:34.663799Z

Regarding Odoyle Rules, all I know is that people always tell me, "Hey, you could do a rules engine with Cells!". I heard this first at a Lisp conference from a fan of LISA, David Young's rules engine. I think we are back in the zone where I will do sth like that when it comes up in some other work I am doing. I am sure it will be fun!

kennytilton 2022-03-16T17:06:21.998369Z

I looked at Fulcro briefly, and someone I respect loves Fulcro, but it sure is big. I am greatly interested in #hyperfiddle and have jumped on the beta waiting list. I once did sth similar papering over two running Lisp apps, where one acted like a server and the other could embed expressions in its own code that got sent over a socket to the "server", with the expression evaluating to the response passed back over the socket. I did this purely so I could use the AllegroCL IDE to complete my work! Actually, I just remembered connecting Cells to a database, but that was cheating: it was in Common Lisp and the database was Franz's persistent CLOS AllegroStrore, which involved the AMOP as did Cells at the time, so I just did multiple inheritance with AStore and bingo, the GUI followed the DB. I have a great story on that to share over beers.

kennytilton 2022-03-16T17:18:02.344679Z

And of course there is always the not very fine-grained Postgres notify. But with a bit of work and pseudo-graphdb we could get more granularity, I think. Again, driven by need.

kennytilton 2022-03-16T17:24:13.133459Z

btw, Reddit link? I do not Reddit much. r/clojure or sth? ...Got it.

kennytilton 2022-03-16T17:35:59.597069Z

Regarding Matrix vs Reagent: as an exercise I implemented my JS hack https://kennytilton.github.io/whoishiring/ in both Reagent and re-frame. I definitely felt more at home in Reagent. But Reagent suffers from glitches. Kind of a deal-breaker there. Also, it involves React.js. I think the handwriting is on the wall for that one.