matrix

kennytilton 2023-02-04T22:21:24.101639Z

Oh, wow. I recall struggling mightily with the FAB examples because of the animation code. Is this going to make all that more approachable? This is funny because I was just looking for a new example to port and saw a little dice-rolling demo. Then I saw the animation code... 🙀

kennytilton 2023-02-04T22:47:07.195329Z

Next Q: Flutter is already cross-OS, so why would HumbleUI add value? Is there more to it than X-OS?

Benjamin C 2023-02-04T23:14:46.701249Z

Yes, this will make animation much much simpler. Esp. once support lands for dart extensions. https://gskinner.com/ has some interesting work! His github is worth checking out if your looking for fancy looking things to port. :) Well, Clojure on the JVM for one, so support for the existing Clojure library ecosystem. But also I like the way HumbleUI is built. Much nicer to work with than Flutter. Matrix does a whole lot to help that, but it's still Flutter at the end of the day. The app I'm currently building is completely custom in it's theme and interactions, and once you stray from the "material app" too far, it gets a bit painful. It's probably not so bad once you get used to it. :P

kennytilton 2023-02-05T01:52:18.074779Z

"fancy looking things to port" I am a sucker for eye candy! You may have noticed me adding SVG to web-mx--more temptation! 🙂 "...once you stray from the "material app" too far, it gets a bit painful." I keep thinking this is a huge opportunity for CLJD, and Matrix if I can grease the skids for developers. So far, though, I am seeing the code staying rather dense even with MX. Maybe if I roll up some standard patterns as a single custom widget, kinda create an HLL for widgets, the density can be sorted out. I realize now the container widget does exactly that, so we have a precedent. 🤷

chromalchemy 2023-02-05T03:19:29.513289Z

Did you see the latest Flutter topic on hn? https://news.ycombinator.com/item?id=34643291 Some people complaining about boilerplate.. Apparently Flutter Desktop is janky for now also.

chromalchemy 2023-02-05T03:35:04.994249Z

I vote also for HumbleUI, although I dont know if Tonsky is planning his own state system. I am keen to use it because has such a keen sense of visual designer needs. For instance https://tonsky.me/blog/font-size/ . Even if the runtime is not entirely novel, I think his design primatives are likely to be. I feel like we finally got to a place where HTML is wrapped as it’s going to get. You can abstract in clojure, and Matrix makes it reactive. But html and css doesn’t compose easily, and the api’s are inconsistant and brittle. So the foundation for grander composition is shaky. Abstractions are finicky and tend towards brittleness. Instead of flying, you just kind of hover over the trees (mixing metaphors here..) of the standardized DOM forrest. We deal with HTML for a reason. And it is a godsend to have libs like MX to help out. I guess i’m seeking more expressivity though. (maybe I just need to increase my clj and js skills, wrapping components and libs; or use a more clever architecture?). I think both HumbleUI and Membrane have goals of simpler better primitives for UI. As an ambitious designer (who was trying to get into Flash before it died), i am fascinated, even if it there are more limited deployment options. I don’t know about their state stories though. (haven’t had time to really use yet). Membrane values plugability. I don’t think HumbleUI has one yet. At least Flutter isn’t saddled with the Dom, etc.. So Flutter/MX is an exciting step forward. I just want to use the most reactive thing. And if that’s Matrix I’m for plugging it into these other UI frameworks. PS. Keep going with the SVG and that kind of stuff. I’m gonna try it out asap. Making graphics move is real wizardry. Making animation libs more accessible sounds awesome too.

👍🏻 1
Benjamin C 2023-02-05T03:43:28.686349Z

Yep, Matrix + HumbleUI would be stellar. Esp. once HUI is more stable and ESP. if someday it gets mobile support, whether that's by being ported to ClojureDart, or perhaps what's being worked on in #graalvm-mobile.

chromalchemy 2023-02-05T03:59:42.292199Z

PS I had this realization with Hoplon too. That despite suddenly getting a lisp and a pretty easy to use reactive library, I was not delivered to nirvana. I was just wrangling markup for frustrating browser apis. The abstractions i was developing were more like random declarations and AST transformations than pure functional composition. Maybe i will do better with MX (I am planning to port my old todo app) My big hope back then was https://github.com/hoplon/ui , a set of higher-level components built in Hoplon. It was using multimethods and stuff to pull together a lot of design concerns. But it petered out before it got mature. I would bet it must have had serious performance issues too if the reactivity wasn’t fully baked across all those dynamic parameters.

kennytilton 2023-02-06T08:10:21.144539Z

Btw, @smith.adriane, I was exploring https://github.com/phronmophobic/membrane and realized I missed membrane.component, the state management colution. My bad. And Membrane is cool. Love the terminal TodoMVC! Are you familiar with the Common Lisp McCLIM? That, too, is intended to be an abstract UI manager agnostic about rendering engine: https://mcclim.common-lisp.dev/

phronmophobic 2023-02-06T23:44:17.533669Z

> No. It primarily solves application complexity without the crushing weight of boilerplate I think we're in agreement here. Membrane takes a different approach here by reifying "intents". Ideally, the component shouldn't know or care where state is stored. The main idea is that components receive their data via arguments, and event handlers can refer to the identity of those arguments (or parts thereof). The shortest example being the checkbox:

(defui checkbox
  "Checkbox component."
  [{:keys [checked?]}]
  (on
   :mouse-down
   (fn [_]
     [[::toggle $checked?]])
   (ui/checkbox checked?)))
> https://mcclim.common-lisp.dev/ I haven't heard of it. I'll have to check it out!

kennytilton 2023-02-06T23:58:13.639249Z

I was surprised by the activity in the McCLIM project. It seemed moribund in the nineties! That checkbox example is the classic simple case right? What does it look like when a new component appears/disappears when the checkbox is checked? Btw, it sounds like you are saying it is not Membrane's job to connect checked? and $checked?. ie, it is up to a particular implementation to manage that? Or do I misundersatnd? And are you providing a reference implementation on some particular stack? One thing Membrane and Matrix have in common, AFAICT, is that both run the show, if you will. They drive what they wrap. In my case, I wrap specifically HTML/CSS or React or Flutter. They all still do their thing, and Matrix respects how they work, but Matrix is driving things. Membrane as a generic solution is quite ambitious and cool!

phronmophobic 2023-02-07T00:11:01.085989Z

> That checkbox example is the classic simple case right? Yes, the checkbox is about as simple as it gets (for anything that has "state"). > What does it look like when a new component appears/disappears when the checkbox is checked? I'm not sure I understand the crux of the question, but I'll try to answer the next question and maybe that will help. > Btw, it sounds like you are saying it is not Membrane's job to connect checked? and $checked?. ie, it is up to a particular implementation to manage that? Or do I misundersatnd? The implementation for the ::toggle intent is provided by membrane:

(defeffect ::toggle [$bool]
  (dispatch! :update $bool not))
However, this is just a default implementation. Users can either just use all the defaults or they can provide their own versions that reuse some or none of the default implementation. The goal is "make the common case easy and the complex case possible". For most users, the code and result should be what they're familiar with, but they is some decoupling that is really useful for debugging, testing, tooling, etc. > One thing Membrane and Matrix have in common, AFAICT, is that both run the show, if you will. I've been calling the part of the app that "runs the show" the graphics backend. Membrane provides several graphics backends (glfw+skia, javafx, swing, lanterna, webgl). It's not really documented, but as long as you can draw shapes, text, and images and forward user input, then you can create a graphics backend that works with membrane. This is an area that where membrane could be even more simpler, but I don't think anyone notices since it's pretty uncommon to offer any flexibility in this area.

👍 1
phronmophobic 2023-02-07T00:15:50.690319Z

> One thing Membrane and Matrix have in common, AFAICT, is that both run the show, if you will. Also, membrane is very embeddable. If you want to run the show instead of membrane, you can. Some examples: • letting cljfx run the show https://github.com/phronmophobic/reveal-exception • letting portal (or cljfx) run the show https://clojurians.slack.com/archives/C02V9TL2G3V/p1646683039164919?thread_ts=1645320012.790219&cid=C02V9TL2G3V

kennytilton 2023-02-07T00:26:33.959499Z

Sweet. Any plans to do a #clojuredart/ Flutter version. That would cover a lot of ground, but Flutter almost killed me. 🙂

😆 1
phronmophobic 2023-02-07T00:28:22.983989Z

The main backend is based on skia, which is the same graphics library that flutter (and chrome) use so it should be possible to target any of the platforms that flutter targets. (like https://github.com/phronmophobic/grease!)

phronmophobic 2023-02-07T00:35:38.220629Z

I've thought a bit about what the best way to interface with existing UI component libraries, but I'm refusing to publish or release any of those options (at the moment).

phronmophobic 2023-02-07T00:37:14.396419Z

Elmlang used to have strong support for building UIs with shapes, text, and images (the same building primitives that designers use and graphics libraries draw). However, once elm added more HTML support, everyone gave up on using sane primitives.

kennytilton 2023-02-07T01:25:46.686359Z

btw, I get a kick out of the WebGL backend! I think this mention was before you dropped in: https://clojurians.slack.com/archives/CKCBP3QF9/p1675603529677399?thread_ts=1675550827.195329&cid=CKCBP3QF9

😄 1
kennytilton 2023-02-05T13:25:29.677399Z

Aw, jeez. No sooner have I finished Flutter/MX, boom!, time to do Humble/MX! 🤣 I shoulda stuck with Cello. https://github.com/kennytilton/Cello Striving for desktop universality, I built a UI framework that used OpenGL as the renderer. Pushing a button literally moved the button deeper into the coordinate system! But then the Interweb came along and I had to port it all to the Web, but then mobile came along and I had to port it all to RN, but then Flutter and #clojuredart came along... sob! :)

kennytilton 2023-02-05T14:35:32.448369Z

"I dont know if Tonsky is planning his own state system..." This goes back to late 2021 when he was pondering options:

State management
  Reagent and Re-frame were mentioned both positively and negatively.
  SwiftUI only negatively.
  Automerge
Let us pause to contemplate how little respect is given to state management! • FB started ReactJS by saying "We just manage the view, do what you want with state, oh by the way we just designed Flux." The funny thing is that, with Suspense, we are required to let React see all state. • Flutter does not have a state solution. It offers some smaller state capabilities, but they themselves likewise stay out of search for a deep solution. I remember them saying somewhere, "Maybe Redux"? sigh • HumbleUI seems to be an exciting UI solution, and state was an afterthought! • membrane does not have a clear story on state, other than that it seems committed to what got React in trouble: view functions should see only props. https://blog.phronemophobic.com/reusable-ui-components.html#What-Makes-a-Checkbox-Tick tbh, my first thought after seeing the power of Cells was that state management should have been the first thing to work on after von Neumann got programs into memory along with the other data. Done well, it makes a lot of things a lot easier. OK, back to the Web/MX announcement....

chromalchemy 2023-02-05T18:26:45.314969Z

Thanks for considering.. Don’t mean to pile on 😅. I’m very exited to to take on web/MX and flutter/MX as is. Props for your continued and public efforts. Just curious about it’s value proposition when considering other clj UI framework state approaches.

phronmophobic 2023-02-05T18:28:45.836449Z

> membrane does not have a clear story on state, other than that it seems committed to what got React in trouble: view functions should see only props. props is a weird Reactism. The main idea is that membrane components are pure functions (same inputs -> same outputs) that return views (immutable values). I'm curious what the problem with using pure functions might be.

chromalchemy 2023-02-05T18:44:10.799449Z

Some more context on Membrane state for consideration: > While using membrane does not require any specific UI framework, it does provide its own builtin UI framework, membrane.component https://github.com/phronmophobic/membrane/blob/master/docs/tutorial.md#components https://github.com/phronmophobic/membrane/blob/master/src/membrane/component.cljc https://blog.phronemophobic.com/what-is-a-user-interface.html Distinction between incidental/essential , public/private, component/top-level state https://blog.phronemophobic.com/reusable-ui-components.html#Incidental-State @smith.adriane would you see Matrix state management as an optional value-add for a Membrane app, or more overlapping with what you have built?

phronmophobic 2023-02-05T18:46:50.205819Z

I see it as an optional, value-add. You can mix-and-match as needed.

kennytilton 2023-02-05T20:29:52.587769Z

@smith.adriane wrote: "I'm curious what the problem with using pure functions might be" tl;dr I found it hard to code a React exercise that way, because I could not get to the data I needed to decide some widget attribute. Longer version: GUIs by their very nature are highly connected. When we mark a todo as completed, the count in the footer must reflect that. Quite a few other things must change as well. And the TodoMVC example is, in my experience, relatively thin on these interdependencies; real-world UIs are much hairier. Anyway, I see an impedance mismatch applying pure functions to impure, if you will, UIs. That said, in MX GUIs there is a different kind of purity and, even better, it works entirely property-to-property. A formula defines the value for one model (MX-ese for reactive object) property, using other model properties. So there is fine granularity (no view functions, so secondary store) and clear encapsulation, if that is the right word for "see these ten lines of code? Those define this property's value". But! Those formulas are free to navigate to any other model in the app and use any property they like, as long as they do not cycle. Small point: that ^^^ sounds global and it is, but my state lookup utilities branch outward from the asking model/property in search of the data they seek, so there is a natural scoping that resolves duplicate model/property names appropriately in most cases. For the exceptions, well, we get to provide a callback that controls search.

phronmophobic 2023-02-05T20:57:39.301359Z

@hiskennyness, thanks for the detailed reply! I don't think there's anything special about UIs that disqualify using pure functions and immutable values as the main building blocks. I would be happy to elaborate, but since this is the #matrix channel, I'm really here just to follow along what adventures the #matrix library is up to. There's not a ton of UI library authors creating alternatives to react, so it's nice to be able to follow along and learn from alternatives.

kennytilton 2023-02-05T21:20:08.661799Z

I appreciate the consideration 🙏, but there is no need to hold back in #matrix. Except that this stuff is so much fun to think about we might stop getting any work done. 🙂 MX certainly swims against more than one current, and indeed my announcement will tackle that head on: • property-to-property dependencies, not view functions; • in-place state, not a Flux-like secondary store; • global reach, by formulas for other state, by handlers for mutating state; • quality comes from the functional essence of discrete formulas; not really concerned how we impact TDD; and • everything is reactive or made reactive, not just the view. But mostly I want to show code. I do not think I can explain all that. :)

😄 2
phronmophobic 2023-02-05T21:37:41.441209Z

It seems like using cells mainly addresses avoiding recomputing dependent values when inputs haven't changed. For that, membrane leans on memoization. The reason I prefer memoization is that it happens transparently. UI code ends up being just data transformation and you avoid redundant calculations. How do you view the difference between using cells vs pure functions+memoization? Are they solving different problems? > I found it hard to code a React exercise that way I also had troubles with React 😄. I felt like React got in the way of building more complicated apps. I feel like I'm finally able to get back to building the apps that I was interested in building. Here's a demo link that hopefully demonstrates that building UIs with data and pure functions is at least plausible. https://clojurians.slack.com/archives/C02V9TL2G3V/p1646782532013739

kennytilton 2023-02-05T23:24:56.185349Z

@smith.adriane wrote: "It seems like using cells mainly addresses avoiding recomputing dependent values when inputs haven't changed. For that, membrane leans on memoization. " Very astute! Memoization was "step 1" on the path to Cells. But it was insufficient, since a change anywhere in the compute graph caused excessive recalculation up that graph absent an intelligent identification of which changes to which properties required their recalculation -- we had to identify and record property to property dependency. Then, when any property changed, we knew exactly what else to recompute. [Aside -- FB did not even try (guessing) to work off property-to-property dependency; they hit on VDOM being cheap to generate and diff and bet the ranch on that.] At this point in the Cells evolution there was a fun epiphany: oh, this is not "pull"! This is intelligent and efficient "push". Aka, causation. If the RGB value the user entered is invalid, then the background color gets changed to red. Or, "a bad RGB input causes the background to turn red". The efficiency is simply that, if I change from an invalid value to another invalid value, the rule to decide the background color will not even run. The decision "bad or good" still has to run, but nothing else if the decision is the same. MobX the Original, not MST, has the same smarts, • the Cells evolution: https://tilton.medium.com/the-making-of-cells-5ab873d1e6c7 "building UIs with data and pure functions is at least plausible." Oh, god yes! Progammers can do anything! We are beasts! Ivan Sutherland did a CAD system with constraints sixty years ago, in assembler. https://history-computer.com/sketchpad-guide/

phronmophobic 2023-02-06T00:49:03.438869Z

> But it was insufficient, since a change anywhere in the compute graph caused excessive recalculation In my experience, that has not been the case. I think a big part of the equation is that clojure's immutable, persistent data structures do a lot of work to make memoization more effective. Memoization usually does a pretty good job of avoiding redundant calculations. It also excels in scenarios where you're flipping back and forth between states (eg. clicking through tabs on a tabbed pane). Another common scenario is having a subview based on equivalent data show up multiple places in the element tree (the subviews may be derived from entities with different identities).

phronmophobic 2023-02-06T01:02:30.175099Z

I guess my main is question is "Do you consider cells to be primarily a performance optimization?"

kennytilton 2023-02-06T06:52:44.371279Z

Kenny wrote: "But it was insufficient, since a change anywhere in the compute graph caused excessive recalculation" @smith.adriane wrote: "In my experience, that has not been the case." No doubt. In many cases, the "excess" in blind cache invalidation will not be a problem. Our chips our pretty fast. But it is still unnecessary, and sometimes will be a problem. I like to say the same about reactive glitches: I shipped an intense 80kloc enterprise application at a time when Cells was susceptible to glitches. But Cells is going on thirty, and over the decades has seen a lot, and these things can trip us up in particular use cases. In my case, I was coding a RoboCup Simulation League client when glitches broke my app, in brief because the entire application DAG begins from a single state change, the arrival evey 100ms of a complete, new game view. "Do you consider cells to be primarily a performance optimization?" No. It primarily solves application complexity without the crushing weight of boilerplate, aka tediously coding up subscriptions and transactions of a secondary store, a burden that never goes away, since successfull applications never stop evolving. When FB engineers invented the idea of Flux, aka secondary stores, they had been struggling for a long time with partial MVC state solutions. What they saw was that a single, one-way, acyclic data structure with tightly constrained ways of mutating said structure was something they could get right. Yay! But boilerplate. sob. And there is now a disconnect. I am coding up my little widget. I need some state to decided if it should be disabled. Let me stop now and surf through the secondary store to find the subscriptions I need. Note the "in-place" granularity, as well as the unfetterd outreach to the list and state of the individual Todos in the TodoMVC classic. This widget is the little chevron that conveneniently lets the user toggle all todos at once:

(defn toggle-all []
  (div {} {;; 'action' is an ad hoc bit of intermediate state that will be used to decide the
           ;; input HTML checked attribute and will also guide the label onclick handler.
           :action (cF (if (every? td-completed (mx-todo-items me))
                         :uncomplete :complete))}
    (input {:id        "toggle-all"
            :class     "toggle-all"
            ::web-mx/type "checkbox"
            :checked   (cF (= (mget (mx-par me) :action) :uncomplete))})
    (label {:for     "toggle-all"
            :onclick #(let [action (mget me :action)]
                        ;; preventDefault else browser messes with checked, which _we_ handle
                        (event/preventDefault %)
                        (doseq [td (mx-todo-items)]
                          (mset! td :completed (when (= action :complete) (util/now)))))}
      "Mark all as complete")))
The neat thing about Cells/MX is that an application built of models and reactive properties itself constitutes a one-way, acyclic graph. It just does so "in place", as an organic network unifying everything.

emil0r 2023-02-09T15:35:38.960379Z

Too much o_O. Interesting read

Benjamin C 2023-02-04T07:39:26.355319Z

So this looks likes it will be fun: wrapping https://pub.dev/packages/flutter_animate!

chromalchemy 2023-02-05T06:08:07.961409Z

https://blog.gskinner.com/archives/2022/09/introducing-flutter-animate.html > Flutter has a powerful animation framework, but it’s also quite verbose. Let’s say I wanted a button to slide in — I’d have to convert my parent widget to a StatefulWidget, initialize a persistent AnimationController, ensure it’s properly updated and disposed of, use it to drive a CurvedAnimation, and feed that into a SlideTransition wrapping my button. > To make it worse, the necessary code tends to be scattered across 3 or 4 parts of your class, and gets more complex with each additional animated element or effect. This introduces friction and discourages playing with animation. > https://pub.dev/packages/flutter_animate abstracts all of this away: