matrix

2024-03-12T07:32:35.604659Z

Hello 👋 I'm looking for an example of "props passing" in matrix. Specifically the coordination of arbitrary number of children under one parent. In the todomvc example it should be the todo list passing each child an id (or the entire todo data) so it'll know which todo it's his, but I couldn't pinpoint the exact place this data passing takes place.

kennytilton 2024-03-12T13:17:44.972099Z

Props passing is quite rare, and it is not even props passing! Let's get that last bit out of the way first. "props passing" is an ongoing stream, not just a one time function call where parameters get consumed and we be done. In props passing, the reactive framework promises to call a special view function whenever a prop changes. This is why we have functions generating view functions instead of widgets -- so the framework has something to call! That, by the way, is why they end up with gross reactive granularity: the framework does not really know in detail what depends on what, so it has to invoke an entire view function just to change the color of the text of one field when sth changes. Matrix allows individual properties to depend on individual other properties, so it knows exactly what to update. That said, where we wrap React or any framework with view functions -- including Flutter -- we accept the reality of view functions, so we generate those and call setState when we see a change, but the dependency tracking remains specific to properties. What we do not do is use the lifecycle method that says "do not re-render me", because we do not call setState unless rendering is required. How do we get away with not passing props to subcomponents? Subcomponents are free to look around the entire app to find any info they need. The navigation utilities support the "looking around", and then simply reading another property established the dependency transparently. Just recently I have started thinking of this idea: MX apps constitute an information bus, for both reading state and controlling the U/X, say by disabling an item. It begins by organizing widgets as a hierarchy, then wiring up connections on the fly via auto-dependency tracking. That said, when generating widgets we do use functions, of course, but they are not special creatures like view functions. These are just normal functions responsible for returning actual widgets. Where they take parameters, the parameters simply are used by the functions to build the desired widget. Now it gets interesting. If we look at TodoMVC namespace example.demo.todoMVC.items, we see a todo instance as a parameter to most functions. The functions are building the UI items, each responsible for managing one todo. These functions get called just once! To build the item. The function code then generates cells that read properties of the todo, establishing dependencies on those individual properies. The function runs only once, but produces a "living" widget that will react to changes elsewhere -- wherever the properties used state from the app-wide information bus. Basically, view functions are a regrettable hack to work around a failure of imagination (as to how to manage dependencies property-wise). Does that help?

kennytilton 2024-03-12T13:21:41.811399Z

One thing I left out is that a function generating a widget can create cells with formulas that track todo state (a "prop") because the formulas are first class functions that close over the function parameters. But only the cell formulas re-run, not the widget-generating function.

2024-03-12T15:46:49.352759Z

Well I regret using the term "passing props" as it derailed the question a bit. I was specifically asking about spawning "children" to handle a collection of something. If I understand correctly you just use a cF on a collection returning whatever you need. So for example if I'd like (for some unimaginable reason) to have a button to spawn a new todomvc to the screen: I'd create some counter, use cF to map over it to create new todomvc matrix object.

kennytilton 2024-03-12T16:02:21.438519Z

Oh, sure. In MX I like :kids over :children because I do not type well, but it is as you say: a formula reads a property that holds a sequence and builds a new child for each element. When I see a problem with rebuilding a whole new list of derived objects just because one gets added or removed, I do sth akin to React and the key property, but more sophisticated/verbose: I have a workhorse :kid-values property that looks at the original source collection and re-decides a complete new collection of kid-values. Then the heavier list of, say, display widgets has formula that looks at the _cache of existing widgets and checks if any of them live on. Ones no longer needed get tossed, and for new ones we pass the relevant kid-value to a kid-factory to produce the new widget. Lemme see if I can find an example.

kennytilton 2024-03-12T16:06:23.384259Z

Nothing in my recent Flutter work. Lemme know if you need an example, I can prolly sth in my Web/MX code.

kennytilton 2024-03-12T16:17:53.243669Z

From the CLJC version of my AskHN Who's Hiring app: https://github.com/kennytilton/matrix/blob/c5a9e8f671a9dc96947079299e3561b5e24ae877/cljc/whoshiring/src/whoshiring/job_list.cljs#L150 hth!

kennytilton 2024-03-12T18:24:51.655289Z

This example is from https://github.com/kennytilton/flutter-mx/blob/main/examples/learn/counter/counter_fmx.cljd

(defn hand-of-cards []
  (row {:mainAxisAlignment m/MainAxisAlignment.spaceEvenly}
    {:name     :hand
     :value (cI (mk-hand))}
    (mapv (fn [i]
            (playing-card i))
      (h-dealt (mav :value)))))
This one does not worry about recreating widgets as the "dealt" property changes.

2024-03-12T18:57:32.889109Z

Yes here you just pass around indices so each playing-card will know where to find its data, but if you were to change dealt all the widgets would be recreated which will be sub-optimal performance-wise but more importantly will cause the lost of internal state (cells). Am I correct?

kennytilton 2024-03-12T20:14:53.076769Z

Right, this is a mindless hack that evolved gradually out of the standard Flutter counter app. And the performance penalty for under a thousand dealt cards would be scant. 🙂 Check the other example with the kids factory for better performance than the ReactJS key hack would offer.

2024-03-13T10:56:36.911139Z

ATM I'm less consernd with performance and more with predictability and "correctness" (and I super relate to the term "hack" here, it does feel like a hack). So unintended loss of state (because I didn't really changed any kid but rather added or removed one) is the main concern for me here. (are there any other implecations I'm missing?)

kennytilton 2024-03-13T12:05:28.895299Z

Well, there is no loss of state. If you want one collection to mirror another collections, as when we want one TodoDisplayItem for eash Todo, then use the :kid-factory pattern. Is that what you consider a hack? Back in Common Lisp where we could use OO, we had a FamilyValues class that bundled up the :kids-factory. That does what React is doing behind the scenes with the key hack. So the hack, if that is what you meant, is there, you just do not see it. Mind you, any time you just have a collection, you can add/remove items without a problem. The only performance/state issue is when we want to mirror one collection with a second collection of widgets which are expensive and/or stateful.