barneycarroll / fxio

Break out of the lifecycle loop with steps!
MIT License
7 stars 2 forks source link

Remove IO instead yield FX.attrs? #1

Closed JAForbes closed 3 years ago

JAForbes commented 3 years ago

What if you could go:

const {
  duration = 600,
  easing   = 'ease-in-out',
} = yield FX.attrs

yield m.request(...yada)

const {
  a,
  b
} = yield FX.attrs

Effectively lets you grab fresh attrs at any point, and then completey remove IO

barneycarroll commented 3 years ago

Well the way I’m thinking about it is that you have FX which is independent of IO and it’s a linguistic exercise in breaking the cycle. Sure we could get a better vnode but I feel a need to get bloody minded about it for the sake of 'separating concerns'. FX are idempotent, IO is pure. You get top level significance this way: different objects for different purposes.

JAForbes commented 3 years ago

Fair enough.

Can you explain how attrs are refreshed currently? Is the IO.attrs object replaced after each yield?

So effectively a fully qualified reference is never stale?

barneycarroll commented 3 years ago

Yeah, IO is updated throughout:

https://github.com/barneycarroll/fxio/blob/131fb9014ef7a2fc680f6f67358e76faa76fcf38/sequencer.js#L75-L84

I did originally have FX yields evaluating to the IO object for convenient as-you-go destructuring:

function * PluginAdapter({enter, update, teardown}, {attrs: {input, …config}}){
  const {dom} = yield enter

  const plugin = Plugin(dom, {…config, input})

  while(input = (yield update)?.attrs?.input){
    plugin.method(input)
  }

  yield teardown

  plugin.destroy()
}

This way you could destructure config from initial input, grab the dom as it becomes available on FX.enter … until you get to FX.update: there’s no elegant way of yielding new IO values while simultaneously providing a clear signal for end of input — which is the priority.

The default advice would be to try to destructure the FX parameter at all times but leave IO intact: this gives a heads up indication of the temporal shape of the sequence, while leaving IO temporality as a discrete concern. We end up with the updating DOM plugin adapter looking like this:

function * PluginAdapter({enter, update, teardown}, IO){
  yield enter

  const plugin = Plugin(IO.dom, IO.attrs)

  while(yield update){
    plugin.method(IO.attrs.input)
  }

  yield teardown

  plugin.destroy()
}

This heads-up commitment to the FX ‘shape’ of a component was kinda there with the Hooks pattern in that you have imports at the top of the module giving an indication of complexity, it’s just very limited in semantic benefit because useEffect is the broad brush which describes the entire remit of component FX complexity.