Using rxjava-2.1.4 and I'm running into an issue with the methods that deal with the empty condition of a Maybe. Could they be changed to allow changing the type?
from:
public abstract class Maybe<T> implements MaybeSource<T> {
public final Maybe<T> defaultIfEmpty(T defaultItem)
public final Maybe<T> switchIfEmpty(MaybeSource<? extends T> other)
public final Single<T> switchIfEmpty(SingleSource<? extends T> other)
}
to:
public abstract class Maybe<T> implements MaybeSource<T> {
public final Maybe<R> defaultIfEmpty(R defaultItem)
public final Maybe<R> switchIfEmpty(MaybeSource<? extends R> other)
public final Single<R> switchIfEmpty(SingleSource<? extends R> other)
}
It doesn't work. When the source is not empty, the item output is of type T but the downstream expects R and results in a ClassCastException somewhere in the chain below.
There is a three argument flatMap that let's you change the type on all 3 signal types consistently.
I guess if you know for sure that the Maybe is empty, it will be safe to do a cast.
Maybe<String> maybe = Maybe.just(1)
.filter(ignored -> false)
.cast(String.class)
.defaultIfEmpty("foo");
@slisaasquatch If you know your source is empty, you should be using Completable.
What about a three arg flatMapSingle and flatMapCompletable. Well I
only need a two args like an if/else but pass through errors. For now I'll
use something like flatMap(onSuccess, (error) -> Maybe.error(error), onEmpty).toSingle()
What about a three arg flatMapSingle and flatMapCompletable.
They don't exist and the support for a proposed Maybe.flatMapSingle via #6154 is weak at the moment. So if you find that toSingle too expensive, you have to take #6154 by source, turn it into a MaybeTransformer and work with that.
Just finished switching the code to use the three arg flatMap. It was a bit unintuitive to use. A lot of switching from Maybe to Single and back again.
public Single<Thing> createThing(String initialValue) {
return Things.find(initialValue)
.flatMap((existingThing) -> {
return Maybe.error(new Exception("Thing already exists")); // could be Single.error()
},
(error) -> Maybe.error(error), // unnecessary rewrapping of error
() -> {
return Thing.create(initialValue).toMaybe(); // unnecessary toMaybe()
})
.toSingle(); // unnecessary toSingle()
}
Not sure how that example would need to change the type via defaultIfEmpty but you can have a shortcut:
Things.find(initialValue)
.map((Function<List<Thing>, Thing>)existingThings -> {
throw new Exception("Thing already exists");
})
.switchIfEmpty(Single.fromCallable(() -> Thing.create(initialValue)))
;
The map will do a type conversion in the signature but never emit a value to be mistaken as it crashes with the exception. That exception will "pass over" switchIfEmpty which now can produce the single item if the original source was empty.
I also feel that a two arg map(onSuccess, onComplete) or flatMap*(onSuccess, onComplete) would be more clear for the readers.
Or I guess you could use Optional.
```java
Things.find(initialValue)
.map(Optional::of)
.toSingle(Optional.empty())
.flatMap(existingThing -> {
if (existingThing.isPresent()) {
return Single.error(new Exception("Thing already exists"));
} else {
return Things.create(initialValue);
}
});
I think it's a little complex for an if/else.
So how about this?
```java
Things.find(initialValue)
.map(existingThing -> Single.error(new Exception("Thing already exists")))
.toSingle(Single.defer(() -> Thing.create(initialValue)))
.flatMap(s -> s);
I also feel that a two arg map(onSuccess, onComplete) or flatMap*(onSuccess, onComplete) would be more clear for the readers.
As would a method on Thing (i.e., tryCreate() that failed if the Thing already exists.
tryCreate() is essentially the method that I'm trying to write.
I think the problem I'm having can be summed up as. switchIfEmpty, map, flatMap work well for one or the other state of the Maybe but I want to handle with both states in one operator.
The closest operator to meet my needs is flatMap(onSuccess, onError, onComplete).
[Edited] Woops, there is flatMap(onSuccess, onError, onComplete) for Maybe and Observable. Please discard that part of my comment 馃槥
From my point of view, we should not try to treat Maybe as a special case. As Observable could have the same states: not empty, empty, have an error. And we do not have special operators to work with all this states for Observable in one operator (flatMap(onSuccess, onError, onComplete)).
So if we are going to add flatMap(onSuccess, onError, onComplete) kind of operators to Maybe we should do similar for Observable.
I can see that flatMap(onSuccess, onError, onComplete) could be easily workarounded using existing operators.
Original proposal of
public abstract class Maybe<T> implements MaybeSource<T> {
public final Maybe<R> defaultIfEmpty(R defaultItem)
public final Maybe<R> switchIfEmpty(MaybeSource<? extends R> other)
public final Single<R> switchIfEmpty(SingleSource<? extends R> other)
}
Could be easily done using Completable:
Maybe.xxx().ignoreElement().toSingleDefault(R defaultItem)
Maybe.xxx().ignoreElement().andThen(MaybeSource<? extends R> other)
Maybe.xxx().ignoreElement().andThen(SingleSource<? extends R> other)
Also I haven't seen any operators which are providing conditional processing with multiple actions.
Addition of such methods could lead to even bigger expansion of the library.
They are different types expressly for the purpose of having different method on each. Not all methods make sense for all types. Where the methods do the same thing they should have the same name.
I also think of Rx as functional a language more than a library. I think of Single and Maybe as async variables and not as a collection like Flowable and Observable. So I think it makes sense to support an operator like <condition> ? <true value> : <false value> on these scalar types.
I'm closing this issue due to inactivity. If you have further input on the issue, don't hesitate to reopen this issue or post a new one.
Most helpful comment
Or I guess you could use
Optional.```java
Things.find(initialValue)
.map(Optional::of)
.toSingle(Optional.empty())
.flatMap(existingThing -> {
if (existingThing.isPresent()) {
return Single.error(new Exception("Thing already exists"));
} else {
return Things.create(initialValue);
}
});