-
Notifications
You must be signed in to change notification settings - Fork 10
Functional MX
A new branch, functional-mx
, holds the product of eleven days of refactoring. The goal was to make f/mx
widget factories such as material-app
and scaffold
into functions instead of macros. The original reason for this was that the macros expand into quite a bit of code for each widget coded.
A benefit, we noticed, was that the two maps accepted by widgets, one for native Flutter attributes, one for custom application attributes, no longer needed to be literal maps. We could then have a custom widget which could be tailored by other widgets, which would merge their inputs with a custom widget's. In a word, widget composition. This was never anything we aspired to, but it seemed a natural win.
In the past, macros had seemed necessary. Matrix children must be created knowing their parents, because MX make
brings a model to life before returning. This so-called "awakening" entails forcing the evaluation of all formulaic cells, and these may need to navigate to other models to get the information they need. Navigation often passes through the parent. But if we build a Flutter component with (center (text "Hi, Mom."))
, the natural evaluation order would try to make
the text
before making the center
.
Fortunately, macros let us expand the above snippet into:
(make 'center
:kids (cFkids (make 'text
:content (cF "Hi, Mom."))))
Now the manufacture of the text
widget is deferred until the center
widget has been created, because cFkids
itself defers the creation of the children. When those children are created, the parent center
can be passed to the text
constructor. If the text
needed to navigate, it could: (text (mget (fmu :counter) :value))
.
The overarching goal is to eliminate boilerplate, the noise that slows down coding and makes code harder to understand and modify.
It occurred to us that all factories such as scaffold
and center
could be more like Cells. They could be functions that defer their product by returning "factory" functions which would be called JIT to actually create a widget. These functions would not run until the parent existed, and would be passed the parent as their only parameter. (Other effective parameters would come from lexical scope and by navigating to the Matrix for additional state, as usual.)
The branch functional-mx
holds the result of that effort. In brief:
- The line count of apps is 20% or so of what it was with macros;
- The resulting binary size of
release
builds was marginally different; - It became necessary, in cases where a code built some widgets conditionally, it was necessary to add
(cFkids...)
as a wrapper; - Text widgets especially often required an
(as-content....)
wrapper where the text was computed; and - Some extra type hinting became necessary because inferencing cues were lost.
For now, functional-mx
remains a possibility, but the loss of transparency seemed greater than the sum of the supporting source changes described. The binding of me
, a vital MX cornerstone, became less certain. MX suddenly felt unintuitive.
At the same time, the exercise showed us how to reduce the macro code bloat. We will explore that starting next, and expect to achieve much of the savings of functional widgets.
That said, functional-mx
was close enough. We will keep it in mind, and perhaps find a role fore it, or even adopt it altogether some day.
.