ReactiveX / RxJava

RxJava – Reactive Extensions for the JVM – a library for composing asynchronous and event-based programs using observable sequences for the Java VM.
Apache License 2.0
47.91k stars 7.6k forks source link

2.x: emitting nulls from Observable.create() #4492

Closed marcin-kozinski closed 8 years ago

marcin-kozinski commented 8 years ago

It is not possible right now to create an observable that emits nulls using Observable.create(). Emitter's javadoc states @param value the value to signal, not null and in fact when I tried calling emitter.onNext(null) it is checked at runtime and results in a NullPointerException.

Can I get any insight as to why this decision has been taken? And why only in this place, as I believe it is still possible to emit null via for example Observable.just()?

It is a fairly common pattern when you want an observable that emits multiple "events" that don't carry any data. Usually then you create an Observable<Void> and emit nulls in onNext(). For example this is what I tried to write to get an observable of clicks on a view in Android (while @JakeWharton 's RxBinding doesn't have a 2.x release):

public static Observable<Void> clicks(final View view) {
    return Observable.create(emitter -> {
        MainThreadDisposable.verifyMainThread();

        view.setOnClickListener(ignored -> emitter.onNext(null)); // causes NullPointerException at runtime

        emitter.setDisposable(new MainThreadDisposable() {
            @Override
            protected void onDispose() {
                view.setOnClickListener(null);
            }
        });
    });
}

Should I use a different way of creating such observable? Or is there a different pattern altogether for this use case in 2.x?

akarnokd commented 8 years ago

We adopted the Reactive-Streams style of value management which forbids using nulls in the sequences. Use Object instead and emit anything:

public static Observable<Object> clicks(final View view) {
    return Observable.create(emitter -> {
        MainThreadDisposable.verifyMainThread();

        view.setOnClickListener(ignored -> emitter.onNext(ignored)); // works fine

        emitter.setDisposable(new MainThreadDisposable() {
            @Override
            protected void onDispose() {
                view.setOnClickListener(null);
            }
        });
    });
}
vanniktech commented 8 years ago

How about providing a class in RxJava itself which basically provides that ignored value class?

akarnokd commented 8 years ago

Java has a fantastic built-in type and instance that you can ignore at your will: (Integer)1.

VictorAlbertos commented 8 years ago

I agree with @vanniktech. I think that providing a RxJava type which express this intent will lead to more consistent apis.

Because @akarnokd you have suggested to use an Integer/Object, but I was thinking in creating my own Type, like a wrapper for Void, something like Result<Void>.

So I suggest that we should create a new RxJava Type or at least set from now on an official way to deal with this situation.

JakeWharton commented 8 years ago

I do not think this is RxJava's responsibility. Kotlin has Unit and with Java you have just Object and literally anything. If the point of these Observables is that the value doesn't matter why are we so concerned about having a standard value?

On Wed, Sep 7, 2016, 8:42 AM Víctor Albertos notifications@github.com wrote:

I agree with @vanniktech https://github.com/vanniktech. I think that providing a RxJava type which express this intent will lead to more consistent apis.

Because @akarnokd https://github.com/akarnokd you have suggested to use an Integer/Object, but I was thinking in creating my own Type, like a wrapper for Void, something like Result.

So I suggest that we should create a new RxJava Type or at least set from now on an official way to deal with this situation.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/ReactiveX/RxJava/issues/4492#issuecomment-245268270, or mute the thread https://github.com/notifications/unsubscribe-auth/AAEEETSQ0R4yOL3GwG_2_JzcjvECh7UAks5qnrE5gaJpZM4J2wZ9 .

VictorAlbertos commented 8 years ago

Precisely, to make completely clear that the value doesn't matter without needing to check the docs.

@JakeWharton, what are you planning to do regarding this matter for RxBinding? Will be Observable<Object>instead of Observable<Void>?

JakeWharton commented 8 years ago

Probably

On Wed, Sep 7, 2016 at 8:52 AM Víctor Albertos notifications@github.com wrote:

Precisely, to make completely clear that the value doesn't matter without needing to check the docs.

@JakeWharton https://github.com/JakeWharton, what are you planning to do regarding this matter for RxBinding? Will be Observableinstead of Observable?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/ReactiveX/RxJava/issues/4492#issuecomment-245270767, or mute the thread https://github.com/notifications/unsubscribe-auth/AAEEET9WHRtHix8WIR8iS8Luxw0hcKLVks5qnrOYgaJpZM4J2wZ9 .

pakoito commented 8 years ago

Not worth adding to the RxJava 2 branch, but I suspect I'll end up using something similar to this in my client libraries:

public final class Ignore {
  private final Ignore(){}

  public static final Ignore GET = new Ignore();
}

Just because I want to encode the behaviour as a type and it always succeeds equality checks as Void did, even if the cost is a tiny bit higher than using any other constant.

VictorAlbertos commented 8 years ago

I think more users are going to feel this gap too, and they will eventually create his own type. Indeed, I'm thinking to create something like that for my own libraries and client code.

But it would be better if someone with enought reputation/popularity -like @JakeWharton or @akarnokd - created a library to fill this gap.

akarnokd commented 8 years ago

I'm a bit fed up with all these reactive types because each new one shamed out of me doubles the work I have to do for free and overtime. There are infinite amount of base reactive types possible with all sorts of property combinations, no-valued types, multi-valued onNext, with or without backpressure, multiple onErrors, etc.

JakeWharton commented 8 years ago

JetBrains (extremely reputable and popular) already created a library called kotlin-runtime that has Unit and Unit.INSTANCE!

On Wed, Sep 7, 2016 at 10:46 AM Víctor Albertos notifications@github.com wrote:

I think more users are going to feel this gap too, and they will eventually create his own type. Indeed, I'm thinking to create something like that for my own libraries and client code.

But it would be better if someone with enought reputation/popularity -like @JakeWharton https://github.com/JakeWharton or @akarnokd https://github.com/akarnokd - created a library to fill this gap.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/ReactiveX/RxJava/issues/4492#issuecomment-245304166, or mute the thread https://github.com/notifications/unsubscribe-auth/AAEEEdw4ecjPDnYItzPPH7YUUy8S-FUtks5qns5PgaJpZM4J2wZ9 .

abersnaze commented 8 years ago

@akarnokd I don't think the Ignore class was intended to be new reactive type. I think it was intended to be non null replacement for the Void type so code that uses Observable<Void> could be changed to Observable<Ignore> when using 2.x.

akarnokd commented 8 years ago

It's the same problem as with Pairs, Tuples and function combinators; we don't use them in the library and would just clutter the namespaces for projects which use something else anyway. You can use your own 'ignored' type as you see fit.

public enum MyIgnored {
   PLEASE_IGNORE_ANY_VALUE_OF_ME;
}
vietj commented 8 years ago

I understand how a stream of null values can be emitted using a type that represents null.

I don't understand how a stream of values that might have null instance emitted should be handled without loosing type safety as Java does not handle union types.

akarnokd commented 8 years ago

@vietj Use Optional<T>.

vietj commented 8 years ago

@akarnokd seems the best alternative indeed