matrix

2023-05-14T09:42:20.365179Z

Hello everyone me and @hiskennyness made this shadow-web-mx starter (TodoMVC) https://github.com/lidorcg/web-mx-shadow Hope it'll be helpful

🙏 1
kennytilton 2023-05-14T09:49:04.473489Z

Thanks for providing the impetus to get shadow on board as a build option! 🙏 Thx also to @thheller for getting me sorted out on a bad practice (`set!` on a dynamic var still bound to its root value) that was getting in the way of a shadow build. ps. note that the name is indeed web-mx-shadow, not "shadow-web-mx".

2023-05-14T10:28:06.638759Z

(make :name :greeting
      :user-name (cI "John")
      :text (cF (str "Hey, " (mget me :user-name)))
      :dom (cF (div (mget (fasc :greeting me) :text))))
Hey does anyone knows why can't I get to :greeting node?

kennytilton 2023-05-14T12:29:43.746179Z

This is sth I have been planning to fix/clarify: fasc does not include me in its ancestor search. In detail, in your excerpt (thx for that): • :text just mgets me (the :greeting) directly. That is fine; • :dom however calls (fasc ... me) to searches "up" from me, perhaps reasonably thinking it is an inclusive search. But is not, so... • ...`fasc` skips me, the :greeting, and considers first at the parent of me. In CL I have two handy navigation utilities, upper and nearest , nearest being inclusive, upper meaning "above me". Does this help? Please continue if I can clarify further. This is destined for the FAQ. 🙂

kennytilton 2023-05-14T12:46:07.550029Z

Does this docstring look good?

(defn fasc "Search up from `where`, excluding where and following only parent links for `what`."
  [what where & options]
  (apply md/fasc what where options))
Meanwhile, looking at the original CLJ version, I see a much more elaborate version of fasc. I better port that as is -- and it includes an option to say "include me". I have said it before, forgot what a POC state of mind built this version. Time for a solid week of finishing. Working elsewhere on the graceful GCing of MX widgets and resoures they might use. Project Quiesce, on the timesheet.

2023-05-14T13:30:29.067449Z

I wasn't expecting fasc to be me inclusive. I was expecting (make :name :greeting ...) to be the parent of (div (mget (fasc :greeting me) :text)

2023-05-14T13:33:48.715519Z

but for some reason me at div returns this:

#object[cljs.core.Atom {:val {:parent nil, :tag "div", :id "diver", :attr-keys (:id), :kids nil}}]
(I added id to be sure)

kennytilton 2023-05-14T13:52:36.240029Z

OK, I see. So here is the sequence, given

:dom (div (mget (fasc :greeting me) :text))
...where :dom is a property of :greeting, the widget being made: • in (fasc :greeting me), me is the widget :greeting; • fasc searches up from me, the widget :greeting, and does not include that in its search. We agree this search is exclusive; therefore • when fasc moves to its parent, it has already missed the widget :greeting. Mind you, if we had nested widgets named :greeting, we would find the wrong widget :meeting, as the search sailed up to it and matched. Moral: Live by unlimited MX search, and die by it if we miscontrol the search. To me this is the one speed bump when coding MX--slowing down to a crawl when I have to specify navigation.

2023-05-14T13:58:02.659729Z

so div does not create a nested widget? and why the object me that get logged from here:

:dom (div {:id "diver"} (mget (log me "ME") :text))
looks like this:
#object[cljs.core.Atom {:val {:parent nil, :tag "div", :id "diver", :attr-keys (:id), :kids nil}}]
? (I don't have access to :text for it BTW)

kennytilton 2023-05-14T14:01:12.572949Z

Oh, jeez, I am sorry. The problem is completely different!

kennytilton 2023-05-14T14:03:00.767759Z

Web/mx hides all this, so open-coding :dom ..... has issues. Let me look at your code some more.

kennytilton 2023-05-14T14:10:12.484459Z

fasc is part of the MX family mechanism. In family, every model (aka map representing a reactive object), has another model as its :parent. Except the root model!. Also, every model has zero or more children called :kids. web/mx macrology takes care of that for us, so we never see it. But that hierarchy is what we are traversing when we navigate. The good news is you just need a simple with-par around the div. Let me reresh my memory. brb...

kennytilton 2023-05-14T14:14:24.326149Z

Try:

(make :name :greeting
      :user-name (cI "John")
      :text (cF (str "Hey, " (mget me :user-name)))
      :dom (cF (with-par me ;; does the right thing to support fasc
                  (div (mget (fasc :greeting me) :text)))))
So sorry for the false starts on this!

2023-05-14T14:16:40.807139Z

what is the role of with-par cF and make in the construction of the tree?

kennytilton 2023-05-14T14:24:27.723419Z

make is the model factory in the MX API. It does crucial initialization of a standard CLJ map, including setting up control internals in meta; • (with-par me (make... arranges (via a dynamic binging) for the model created by make to have me as its the :parent • macro cF creates a formulaic cell for a model property. The cF mechanism is how one property can be a function of zero or more other MX properties. As a convenience, cF injects the model owning the property as a lexical me , availed to functions that are the "formulas".

kennytilton 2023-05-14T14:34:09.059379Z

Here are the guts of cF and brethren, via a shared internal macro ;

(defmacro c-fn-var [[c] & body]
  `(fn [~c]
     (let [~'me (c-model ~c)
           ~'_cell ~c
           ~'_prop-name (c-prop ~c)
           ~'_cache (c-value ~c)]
       ~@body)))
Note that aside from me, the model owning the property a cF defines, cell functions have access to • _prop-name, the name of the property they occupy; • _cache, the current value of the property being recalculated, in case we are incremental in nature, or sth; and • _cell, the whole cell structure, useful when debugging.

2023-05-14T15:01:35.429129Z

So only with-par create a tree hierarchy and it must be used explicitly. No other construct builds a parent-child rel?

kennytilton 2023-05-14T16:20:27.026149Z

Well, in non-UI MX code, we might see things like:

(make :mx-type :team
   :name "Yankees"
   :kids (cF (binding [*parent* me]
                ; imagine these next selected at random, so generated functionally
                (make :mx-type :player...)
                (make :mx-type :player...)))
The keys then, to making MX family utilities work is maintaining :parent and :kids, perhaps by binding *mx-parent. tl;dr: manage :parent and :kids appropriately, and the MX family suite of utilities will work OK. Btw, :parent should never change, nor should it be formulaic. Aside from a root matrix model, all :kids must have a parent, and that parent must be provided to make , either as an initarg or as the dynamic binding of tiltontec.model.core/*parent*.

kennytilton 2023-05-14T16:21:52.432789Z

In UI code, the web/mx tag macros hide the parent/kids set-up. I hate typing, and noise/boilerplate, is the deep background here.

kennytilton 2023-05-14T16:31:07.527679Z

FWIW, Flutter is super-OO, so unlike HTML which is all DOM nodes and children with a single parent, Flutter/MX as we speak is developing new navigation and even model life-cycle capabilities, so the parent-child pattern will no longe rule. eg, :kids will be just one way to have navigable structure.

2023-05-14T16:32:26.616099Z

Do you think any of that will be back-portable to mx?

2023-05-14T16:39:21.934869Z

Where can I find pure mx (no web) child parent code?

kennytilton 2023-05-14T16:41:39.119079Z

Absolutely. It is a straightforward change. One issue might be whether the API should treat all substructure as "owned". In past versions of MX, I implemented the idea of a :host for substructure, so :kids could be served for tree-modelling, but a model could have other structure, perhaps :parts or a singular :thermostat. Navigation knew to try the :host if the current model's :parent was nil. The easiest way to see pure MX is the test suite. I am afraid I do not have an example of an interesting non-UI MX app, in clojure, anyway. I have a Common Lisp RoboCup client that became quite elaborate, if you would like to see that.

kennytilton 2023-05-14T16:43:42.760199Z

Here is the test suite: https://github.com/kennytilton/matrix/tree/main/cljc/matrix/test/tiltontec/model Most are pretty abstract, but a couple get semantic.

kennytilton 2023-05-14T16:45:17.432319Z

Here is RoboCells : https://github.com/kennytilton/robocells, the Common Lisp RoboCup soccer client that got all input and executed all actions over a UDP socket.

kennytilton 2023-05-14T16:54:21.073969Z

I glossed over sth:

One issue might be whether the API should treat all substructure as "owned". 
Suppose we have a :thermostat property/substructure of a larger :boiler model. That should be "owned" by the boiler, and be disposed of when is the boiler. But what if we did sth weird, gave the :boiler a :balancer property/model, which would be the same instance for all boilers. Not how I would model it, mind you. Just cant think of a better example. But suppose we want a common instance as a property of many instances. Then we would not want to dispose of the :balancer when disposing of a :boiler. [edit: So we would need some way to tag hosted (non-kids) model as owned or not. Prolly by creating the model with some indicator that it should not be owned, possibly by the simple trick of creating it with its own parent, which hosting logic would take as an indicator, own or not.

kennytilton 2023-05-14T17:07:11.884639Z

"Do you think any of that will be back-portable to mx?" I find myself pining for a #?(:cljd ...) option, btw. 🙂 But I can also conform the two closer and closer as I evolve the mx in f/mx. Version 1 was a mad dash to get to a POC in re Dart and Flutter (and CLJD) as a platform. I reached POC quite a while ago, now I have started a hot week of "finishing" which will make gene-swapping easier.

kennytilton 2023-05-14T23:25:00.989359Z

OK if I turn your code example into another bit of doc? Your related questions defined well the issues needing explication.