I’m deeply confused about this architecture. Why d...
# compose
m
I’m deeply confused about this architecture. Why does a
Change
need both an
Applier
and a
SlotWriter
? The explanation below makes it sound like it’d only need an
Applier
, since each is to be applied by it once all
@Composable
functions have run. Shouldn’t the
Applier
then take care of flushing changes to the slot table via a
SlotWriter
or whatever?
j
Applier realizes the diff against the backing node tree. It is public API. It knows nothing about the slot table which is implementation detail.
It also only runs once the recomposition is complete and successful
If you're writing a UI toolkit on top of Compose you implement Applier to create your node tree.
s
Applier
is only for node operations,
SlotWriter
is for writing composition things. You can think about it as
SlotWriter
making internal edits and
Applier
is making user facing (node related) edits.
m
@jw @shikasd Thanks for having a look at my question. So the chain of events is not: 1. A
Composer
composes
@Composable
functions. 2. The
Composer
updates the corresponding
Composition
based on what it recorded. 3. The
Composition
materialises a tree (eg a
LayoutNode
tree) from itself. But rather: 1. A
Composer
composes
@Composable
functions. 2. The Composer submits updates to… ◦ The corresponding
Composition
to be executed by a
SlotWriter
. ◦ The tree materialised from the
Composition
to be executed by an
Applier
. And the submitted updates are either scheduled or executed immediately?
s
Yeah, mostly executed immediately, unless you are running pausable composition
m
@shikasd It seems that the runtime composes
Composition
objects, and a
Composition
composes
@Composable
functions via a
Composer
.
Copy code
private fun applyChangesInLocked(changes: Changes) {
    // ...
        trace(traceName) {
            val rememberManager = pendingPausedComposition?.rememberManager ?: rememberManager
            applier.onBeginChanges()

            changes.execute(slotStorage, applier, rememberManager, composer.errorContext)

            applier.onEndChanges()
        }
    // ...
}
The
Composer
submits changes to the
Composition
, which schedules or executes them immediately. I’m guessing I can only materialise one tree from a
Composition
. The
Composition
then seems to execute the changes by passing them its slot table and the tree materialised from it while being composed. Have I got everything right?
image.png
Or maybe the Composer… • Either submits changes to the Composition, which schedules them • Or makes them immediately to the Composition slot table and the tree materialised from it? Which is it?
I hope you won’t mind the use of ChatGPT, I typically don’t rely on it, but I have no choice when it comes to Compose. This explanation seems to contradict the
changes.execute(slotStorage, applier, rememberManager, composer.errorContext)
statement found in
Composition.applyChanges
.
Change
instances seem to run in the ‘apply phase’ and make changes to the slot table and the derived tree via the passed
slotStorage
and
applier
. Genuinely what on earth is going on? Could you please tell me the actual order of events?
I cannot tell if it’s hallucinating or telling the truth. Is the way Compose works that 1. a Composer submits changes to its Composition, and 2. the Composition makes the submitted changes to its slot table and the derived tree later, namely in the ‘apply phase’?
Between a rock and a hard place; an incoherent book full of vague explanations and a hallucinating probabilistic text generator. Please help
s
Yes, that is correct
m
@shikasd I was expecting to hear back tomorrow since it’s the weekend, but I appreciate the expedited response; I hope you’re having a good one! 1. A
Composer
submits changes to its
Composition
2. The
Composition
eventually relays those changes to ◦ Its slots, which constitute a serial representation of the composed tree, ◦ And trees to be kept in sync, each via its
Applier
So in summary, this is the order of events, and • A
Composition
is agnostic about the implementation detail of each materialised tree. • A
CompositionImpl
drives one
Applier
, but there’s no technical limit to how many an arbitrary implementation of
Composition
can drive. • What it means for a materialised tree to be kept in sync with the
Composition
is decided by its
Applier
, to which the
Composition
simply forwards all new changes. These define the contract. Have I got everything right? Also if it’s more than what you can verify during the weekend, I can absolutely wait. Your help has been valuable the whole time, and I don’t mean to rush you in any way
👌 1
(Fantastic, thank you!)