-
Notifications
You must be signed in to change notification settings - Fork 1.8k
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
ConflatedBroadcastChannel as ReadWriteProperty #245
Comments
It is interesting. Can you share your project (or maybe some smaller playground project if cannot) via github to see it in a larger context? |
I have a similar issue, also.
This code is weak, I can lost an update during the first |
Sorry, but I cannot share the project. Anyway it is not used a lot (yet) as we are really not sure if it is good design or not. The idea is to achieve generic observer pattern. It is inspired of what we can do with JavaFx and tornadofx: class MyClass {
val valueProperty = SimpleIntegerProperty(0)
var value by valueProperty
}
fun usage(obj: MyClass) {
// get current (latest) value
val currentValue = obj.value
// subscribe
obj.valueProperty.addListener { _, _, newValue -> println("new value: $newValue") }
} And with a coroutines version I proposed in my first post, one could do: fun usage(obj: MyClass) {
// get current (latest) value
val currentValue = obj.value
// subscribe
launch {
obj.openValueSubscription().consumeEach { newValue -> println("new value: $newValue") }
}
} Actually I'm more asking:
|
Regarding Having something like the reactive
|
Yes, but there is not a lot to do. I don't see how I can do more than throwing an exception. To achieve it one would simply implement But anyway, for this usage the broadcast wouldn't normally be closed, unless we know that We indeed used But there are no technical problem. From a technical point of view it does work as expected, and we don't have any issue. I'm really asking what you think about this design. And, if it appears it is a good design propose to implements |
Your |
I agree. That's why an
Indeed, if the value changes many times quickly the listener may not receive all values but only the latest one. But it is usually not a problem, as (in most cases*) we only care about the most recent value. *: This is not the case of the light example. |
Sorry, I missed it. |
I think it is a perfectly valid opinion. Actually I'm really not sure if it is a good idea either. One alternative could be: class MyClass {
private val broadcast = ConflatedBroadcastChannel(0)
fun setValue(newValue: Int) {
broadcast.sendBlocking(newValue)
}
fun openValueSubscription() = broadcast.openSubscription()
} With this latest example, the only way to get a value is to subscribe with @fvasco, Do you think this latest pattern is preferable to my first example? Or would you propose something different? |
Hi @jcornaz, My use case requires an interface like this interface ObservableVariable<T> {
var value: T
fun openValueSubscription(): SubscriptionReceiveChannel<T>
} in such case delegate a variable to the We can choose to implement First choice is pretty simple and safe. What are the downside of the second choice. Finally we should choose a good practice for a common case: class MyClass(observableVariable: ObservableValue<Int>) {
var double: Int
init {
double = observableVariable.value * 2
launch {
observableVariable.openValueSubscription().consumeEach {
double = it * 2
}
}
}
} a simple solution should be: class MyClass(observableVariable: ObservableValue<Int>) {
var double: Int by observableVariable.mapValue { it * 2 }
}
Can you compare my considerations with you experience, please? |
I have to say we came up with an interface like the one you proposed too. Our version of the Then I'd like to say, I really like the naming you used ( I personaly like the option of making About the possibility to be closed, I would say like you "with great power comes great responsibility". I think it fine to say that's the responsibility to the client code to have a correct usage of it. If one close a property and then try to update it, it should throw an exception. I totally agree with your point. But what else can we do?
Either I don't understand your point, or it is incorrect. Finally, as you proposed, I would love to have class MyClass(observableVariable: ObservableValue<Int>) {
val doubleObservable: ObservableValue<Int> = observableVariable.map { it * 2 }
val sumObservable: ObservableValue<Int> = combine(observableVariable, double) { x, y -> x + y }
val double: Double by doubleObservable
} Note: for your first usage example it is not necessary to do So globally I'd say your proposition is going in a good direction (in my opinion). We already have something similar implemented in our code base (with two operators |
Well, really nice.
You are right, I try to explain it better adding interface SubscribableChannel<E> { // like ReceiveBroadcastChannel
fun openSubscription(): SubscriptionReceiveChannel<E>
}
interface BroadcastChannel<E> : SendChannel<E>, SubscribableChannel<E> {
}
interface ConflatedSubscribableChannel<E> : SubscribableChannel<E> {
val value: E
}
interface ConflatedBroadcastChannel<E> : BroadcastChannel<E>, ConflatedSubscribableChannel<E> {
override var value: E
}
Yes, whould be nice to preserve both behaviors. |
Thanks @fvasco, I like the interfaces you propose. I would only propose these changes:
And I would either: Make Or provide: operator fun <T> ConflatedSubscribable<T>.getValue(thisRef: Any, property: KProperty<*>): T = value
operator fun <T> ConflatedBroadcastChannel<T>.setValue(thisRef: Any, property: KProperty<*>, value: T) { this.value = value } |
I consider better extending the interface (your alternative). |
I notify this post regarding the "replay channel" (https://discuss.kotlinlang.org/t/replay-channel/6933): the |
So far I like what I see here. You are welcome to send pull request with those interfaces. I also see that abstraction for cold streams (see #254) could implement the same interfaces and it should provide a lighter-weight implementation of those interfaces than |
Hi @jcornaz, |
Hi @fvasco Just one thing, may I suggest to use this naming scheme: I prefer this naming scheme for two reasons: First, |
Hi @jcornaz, Otherwise open a draft pull request to pin up some comments. |
@fvasco Yes, I've open a draft PR. let's move this discussion there. |
Hello,
In our code base, we found useful to define the folowing extension functions:
They allow us to use the following pattern:
May I ask what do you think about this ?
Do you think it is an anti-pattern ?
ConflatedBroadcastChannel
implementReadWriteProperty
?The text was updated successfully, but these errors were encountered: