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
Add support for async EventListener DSL #35
Add support for async EventListener DSL #35
Conversation
Thanks @pambrose for the great suggestion. My first experiment to enable async event consumption clearly lacked beauty. Your PR is much cleaner. The only concern I'd like to raise is the limitation to existing event types. In the simulations we are doing, we typically create lots of other custom types which we can consume very selectively. E.g. class ToolEvent(
val tool: Tool,
val processId: UUID,
val start: TickTime,
val end: TickTime
) : Event(end)
val toolEvents = collect<ToolEvent>() // which will be of type List<ToolEvent> Actually, in some simulations we exclusively monitor the custom events and do not mind about the built-in event types, which can for performance sometimes even be suppressed via https://www.kalasim.org/advanced/#continuous-simulation So, I wonder if the entire event API should support/follow a reified minimalistic approach for in-sync and async listeners? addEventListener<ToolEvent>{ print(it) }
addAsyncEventListener<ToolEvent>(myOptionalScope){ print(it) } Or maybe even just Clearly, the event API should follow what's best-practise in kotlin, so that new users feel at home right very quickly. How do you feel about that? |
Ah, very interesting. I had not considered custom types. Let me rework it a bit. |
Hi Holger, I played around with using generics and I can see 4 ways of dealing with it. I wanted to see what you think. addAsyncEventListener<ComponentStateChangeEvent>{ event: ComponentStateChangeEvent ->
println("onComponentStateChangeEvent: $event")
} addEventListener(
asyncEventListener<ComponentStateChangeEvent> { event: ComponentStateChangeEvent ->
println("onComponentStateChangeEvent: $event")
}
) addEventListener(
asyncEventListener<ComponentStateChangeEvent> {
onEvent { event: ComponentStateChangeEvent ->
println("onComponentStateChangeEvent: $event")
}
}
) addEventListener(
asyncEventListener {
onEvent<ComponentStateChangeEvent> { event: Event ->
println("onComponentStateChangeEvent: $event")
}
}
) Pros and cons that come to mind are: #1 is the most terse, but it creates a new method that overlaps with the existing #2 is almost as terse, preserves the existing #3 allows for multiple #4 allows for mixing multiple types of #3 and #4 are likely providing unnecessary flexibility that would not be used, but I wanted to at least mentioned them in case you thought they might be valuable in actual usage of the API. #1 and #2 look clean to me and I like not breaking code, so my favorite is probably #2, but I will happily defer to whichever you judge to be best. |
I was just playing with doing a synchronous listener for #2 and I think I like offering both options using the same
and
|
My favourite would be clearly (1) with quite a margin, as it's most concise and very readable. I don't mind the breaking change, since we/I have pointed on multiple occasions in the documentation (release notes, changelog, etc.) that the API is not yet stable. Starting with the first major release, API stability will be for sure something to weigh carefully against cosmetical improvements. |
…es a lambda event handler.
I added an implementation of #1 and I like it. The only catch is the eventListeners value has to be public because addEventListener() is inlined and cannot reference private values. Also, I did not include async in the method name because I am assuming one would not want a synchronous version messing with the timing. Let me know if that is a bad assumption. |
I've slightly reworked the implementation, managed to make the listeners private again, simplified the docs, and added a test
So the debug print (to be removed for sure) in
I would have a second related question. Would the coroutine Channel API allow to add a second consumer as shown in the commented code in the mentioned test implementation? Or can a flow be consumed only once? |
No description provided.