Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consolidate event handling into Listener type. #245

Merged
merged 11 commits into from
Oct 1, 2021
Merged

Conversation

Laimiux
Copy link
Collaborator

@Laimiux Laimiux commented Sep 30, 2021

What

  • Added Listener type (maybe should call it Callback instead). This replaces () -> Unit and (Event) -> Unit
  • Renamed TransitionCallbackWrapper to TransitionDispatcher.

@@ -0,0 +1,41 @@
package com.instacart.formula

class BoundStream<Message>(
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed from Update to BoundStream

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moving this into #246

/**
* Formula uses Listener callbacks to pass events.
*/
fun interface Listener<Event> : (Event) -> Unit {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still debating what would be the right name for this.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer Listener

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another option would be Handler. I don't have a strong preference, just throwing it out there to see what other people think.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I've also thought about Handler though I didn't like its associations with android.Handler

@@ -0,0 +1,30 @@
package com.instacart.formula

interface TransitionContext {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not yet sure about moving this out.

* and/or [Effects] that should be performed. You can return [Transition.None] if nothing
* should happen.
*/
fun interface Update<State, Event> {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering how to better unify this with the Transition type. Maybe this should be changed to

fun interface Transition<State, Event> {
    sealed class Result {
       /**
         * Stateful transition.
         *
         * @param state New state
         * @param effects Optional effects such as parent callbacks, logging, db writes,
         * network requests, etc.
         */
        data class Stateful<State>(val state: State, override val effects: Effects? = null) : Result<State>()

        /**
         * Only effects are emitted as part of this transition
         *
         * @param effects Effects such as parent callbacks, logging, db writes, network requests, etc.
         */
        data class OnlyEffects(override val effects: Effects) : Result<Nothing>()

        /**
         * Nothing happens in this transition.
         */
        object None : Result<Nothing>() {
            override val effects: Effects? = null
        }
   }

   fun TransitionContext.create(event: Event): Result<State>
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, having an Update that produces a Transition is a bit confusing, would be better to not have that extra name I think.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does Update and Update.Result or Transition and Transition.Result seem better? On one hand, Update corresponds to MVU naming, however many folks like the name Transition (including me).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm moving this change out of this PR.

import com.instacart.formula.Update

@PublishedApi
internal class TransitionDispatcher<State>(
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was called TransitionCallbackWrapper previously

}

// TODO: naming UpdateType as transition seems weird. We need to resolve these two types.
fun <Event> dispatch(transition: Update<State, Event>, event: Event) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: fix this once we resolve naming for Update

children?.forEachValue { it.markAsTerminated() }
}

override fun performTerminationSideEffects() {
children?.forEachValue { it.performTerminationSideEffects() }
updateManager.terminate()
callbacks.disableAll()
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason I'm moving this from markAsTerminated to performTerminationSideEffects is to allow termination side-effects to execute.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Laimiux Laimiux force-pushed the laimonas/naming branch 2 times, most recently from a159563 to c39f88b Compare September 30, 2021 23:00
@carrotkite
Copy link

carrotkite commented Sep 30, 2021

JaCoCo Code Coverage 79.03% ✅

Class Covered Meta Status
com/instacart/formula/internal/Listeners 80% 0%
com/instacart/formula/internal/FormulaManagerImpl 88% 0%
com/instacart/formula/internal/Frame 92% 0%
com/instacart/formula/internal/FormulaContextImpl 50% 0%
com/instacart/formula/internal/ListenerImpl 75% 0%
com/instacart/formula/Evaluation 100% 0%
com/instacart/formula/FormulaContext 47% 0%
com/instacart/formula/StatelessFormula 100% 0%
com/instacart/formula/test/TestListener 0% 0%

Generated by 🚫 Danger

* Note: this class is not a data class because equality is based on instance and not [key].
*/
@PublishedApi
internal class CallbackImpl<State, Event>(internal var key: Any) : Listener<Event> {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: this should be renamed to ListenerImpl or Listener should be renamed to Callback

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add this as a comment to this class?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most likely, I'm going to rename this before merging the PR.

}

val key = JoinedKey(stream.key(), transition::class)
val callback = formulaContext.onEvent(key, transition)
Copy link
Collaborator Author

@Laimiux Laimiux Sep 30, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This now delegates to onEvent to create the Stream listener/callback. This was made possible, by https://github.com/instacart/formula/pull/245/files#r719810293

Copy link
Collaborator

@alexanderbezverhni alexanderbezverhni left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍🏼

*/
inline fun <UIEvent> onEvent(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason we migrated off inline for all functions here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previously, we were creating another function inside of here and cross-inlining the transition function to save an extra object allocation. I've restructured this, so inline isn't needed anymore.

}

/**
* Utility function used to specific
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

INcomplete comment?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

* Note: this class is not a data class because equality is based on instance and not [key].
*/
@PublishedApi
internal class CallbackImpl<State, Event>(internal var key: Any) : Listener<Event> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add this as a comment to this class?

@@ -17,7 +17,7 @@ class EventCallbackFormula : Formula<Unit, String, EventCallbackFormula.Output>
return Evaluation(
output = Output(
state = state,
changeState = context.onEvent { newState ->
changeState = context.onEvent<String> { newState ->
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This actually adds up to the code readability.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean improves code readability?

* and/or [Effects] that should be performed. You can return [Transition.None] if nothing
* should happen.
*/
fun interface Update<State, Event> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, having an Update that produces a Transition is a bit confusing, would be better to not have that extra name I think.

Copy link
Contributor

@mattkranzler5 mattkranzler5 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, I like the changes.

@Laimiux
Copy link
Collaborator Author

Laimiux commented Oct 1, 2021

Hey folks, thank you for your initial review 💯

To reduce the scope of this PR, I've moved some changes such as TransitionContext and Update out (I want to fully flesh these out before merging). In this PR, I focused on the Listener change and update all the docs. Please take a quick look again to see if anything doesn't make sense cc @johnwilde @mattkranzler5 @alexanderbezverhni

@Laimiux Laimiux changed the title Consolidate event handling and naming. Consolidate event handling into Listener type. Oct 1, 2021
Copy link

@jasonostrander jasonostrander left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, just one question.

@@ -17,7 +18,7 @@ class KeyUsingListFormula :

data class ItemRenderModel(
val item: String,
val onDeleteSelected: () -> Unit
val onDeleteSelected: Listener<Unit>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this change mean we need to type our callbacks as Listener in RenderModels, or is this change specific to these formulas?

Copy link
Collaborator Author

@Laimiux Laimiux Oct 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When using onEvent you will need to type to Listener type (or (Event) -> Unit). For now, there still exists callback and eventCallback which support old style.

@Laimiux Laimiux merged commit d726694 into master Oct 1, 2021
@Laimiux Laimiux deleted the laimonas/naming branch October 1, 2021 21:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants