[WIP] Imperative hook delegation and functional hook APIs through StateHooks #39
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
What Changed
Currently, all the code lives in a test to act as a POC. The basic approach is to add a new
StateHook
concept (somewhat akin to Kotlin coroutinesStateFlow
) for encapsulating values published by a single arity,Unit
return sync hook:The typing is important here because we can only arbitrarily tap hooks that we know what to return with. This leaves
Unit
(effectivelyvoid
),BailResult
, andT
. Leaving outBailResult
(as bail hooks aren't likely to be used to represent state) &T
(waterfall hooks are a little more probable, but still not likely to be used to represent state).I attempted to solve imperative hook APIs first, but found that the use case laid out in #23 somewhat requires some functional APIs to
flatMap
nested hooks;flatMap
just meaning mapping incoming hook values into other hooks to unwrap as part of aStateHook
. All the functional APIs are meant to act as reactive chunks to compose hooks, i.e. they all return new hooks that wrap the parent hooks, with some unique logic to power such functionality. The current functional APIs implemented in this POC:filter
: only propagate hook values that satisfy the predicatemap
: transform incoming hook valueflatMap
: transform incoming hook value into another hook and unwrapflatten
:flatMap
where identify is the incoming hook valueThe test code has a few example tests, as well as edge case tests. It'd be easiest to look at these tests to understand how they should be used:
simple state hook
: Demonstrates ability to delegate to aStateHook
for state representation in a variable. Also demonstrates "replay-ability" ofStateHook
s to ensure new taps are called with the state value, even if a new value isn't published.map state hook
: Demonstrates ability tomap
hook values to encapsulate arbitrary information as stateflatmap state hook
: Demonstrates ability toflatMap
nested hooksThe other tests are a bit more pervasive edge case tests to ensure the API works for all use cases. Feel free to take a look, but it gets a messy quick.
With that, there is still a complexity around nullable hook values and nullable hooks.
flatMap
vsflatMapNullable
exists strictly to ensure we respect the incoming hook value nullability with respect to the new hooks type parameters, nullable or not. Kotlin allows us to represent both in an overloaded generic, but the JVM is unable to represent such distinction, hence the separate methods. I am somewhat of the mind to create a new sealed class to encapsulate the return type ofStateHook
s, to represent "empty" vs a stateful value. This'd allownull
to strictly represent a state value, at least for the hook API. Delegation could still usenull
to represent an empty state, as we wouldn't want to pollute the convenience API with more complexity.Why
Solving for #23 & #37