You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I really would love to see this because of 2 reasons:
There is no good (async & composable) Future type in the Java standard library if you have to target < Java8. Even with CompleteableFuture available this uses executors for the continuations. For an RxJava integration using an Rx Scheduler is a better match - we can then receive Observable events and Future continuations on the same scheduler.
The alternative is using an AsyncSubject. The problem with AsyncSubject is that the user only sees an Observable in the API and thereby won't know that it emits only a single item. He also won't know if it's hot (the function call which returns the Observable will immediatly start an operation without the need for Subscribe() or not (needs Subscribe()). Therefore I think the ability to model such single return-value async operations with Futures makes an API better understandable.
Rx.net has seamless integration between observable and task.
Yes, but RxJava hasn't. An integration/interfacing with Bolts could be an option instead of separate implementation, but it would probably require som changes to Bolts as well. Bolts, unlike RxJava, rely on Executors.
However without async await syntax, tasks/futures do not provide much advantage
Over what? Observables? Simpler syntax and easier learning curve would be one. Right now I'm using Observables for Scalar data (HTTP responses) because that's the only thing Retrofit supports, but it's been a challenge to sift through the wiki finding just the methods I need for these kind of tasks. continueWith=flatMap? Not intuitive. I simply feel I'm using the wrong tool for the job.
What makes the use of flatMap not intuitive on an Observable that has a single value versus multiple values?
First and foremost the naming. "FlatMap" doesn't tell me anything. What's "unflat" about a Scalar value? What part of the operation involves mapping of any kind? Promise.then is much more descriptive to me.
But I realize it might be my background coming from Tasks in C# to Promises in Javascript and then Observables that might be "polluting my mind". I also might have misunderstood this isse. Maybe you could elaborate, since you're the OP?
flatMap in RxJava is selectMany in C#. Since you mention you know Tasks form C# you should read this http://blogs.msdn.com/b/pfxteam/archive/2013/04/03/tasks-monads-and-linq.aspx. Technically speaking the Task API is a so-called co-monad, but since they are continuations, Task can also be formulated as a monad. In Rx .NET we had manySelect for a while that corresponds to continueWith.
If there is no need to flatten then it would be map:
Future<R> Future.map(Func1<T, R> mapper)
If it is purely a side-effect, then the naming is up in the air but in RxJava thus far it is doOn* method names to signal the side-effects, so something like doOnSuccess/doOnError on a scalar Future and just pass the value through:
I think the issue here is that flatmap is a description what happens in general whereas then or continueWith describe it in a more concrete way for the Future type.
Some time ago I had no experience with FP programming, so flatmap didn't tell me anything. However continueWith is more self-describing so I could understand it and it's easier to discover in an API documentation. I like it. But as I later learned that then/continueWith are equivalent to flatmap so now I understand it too. Maybe the same applies to other users?
Why not expose both function signatures? Don't think it would be a problem and I think it would make the familarity higher for OO as well as FP programmers.
However I thought the more general question was whether RxJava would get a Task/Future like structure at all? I would still like to see it. Bolts looks good, but having a single library for all kinds of asynchronous things and with the possibility for interaction between structures (futures to observables and the other way around, sharing of schedulers, ...) would be even nicer.
Can someone explain what this Future/Task should do, or more specifically, what is the underlying issue we want to solve that Observable or Async can't do (efficiency?)?
Represent request/response of a single value. It is purely a design thing, not functional. Many, many people would prefer to represent scalar values such as IO request/response (think HTTP, REST, RPC) as a Future/Task but be able to merge them into Observable streams. It is one of the most common questions I get.
Rx.Net has it because .Net already has Task that works nicely with Observable since they were built for each other. The Java Future is unusable in this sense and CompletableFuture in Java 8 can be made to work but it's entire API design is incredibly different and they are always "hot".
@benjchristensen I meant potential postixes, i.e., ScalarTask, ScalarObservable or ScalarFuture, etc.
Since we will use the experimental annotation, let's proceed with Single and come back to the name in a few months once enough use experience has been accumulated.
importjava.util.concurrent.TimeUnit;
importrx.Observable;
importrx.Single;
importrx.Single.Promise;
importrx.schedulers.Schedulers;
publicclassSingleExperiment {
publicstaticvoidmain(String... args) {
Observable<String> a_merge_b = getDataA().mergeWith(getDataB());
Single<String> a_and_b = getDataA().flatMap(t -> {
returngetDataB().map(r -> t + r);
});
Single<String> a_transformed = getDataA().map(a -> a + "_response");
Observable<String> a_concat_b = getDataA().concatWith(getDataB());
/** * This shows flatMapping a Single with an Observable */Observable<String> a_with_c = getDataA().flatMapObservable(a -> {
returngetDataC().map(c -> a + "-" + c);
});
Single<String> aWithTimeout = getDataA().timeout(100, TimeUnit.MILLISECONDS);
Single<String> aWithTimeoutAndSimpleDefaultValue = aWithTimeout.onErrorReturn(t -> "DefaultValue");
/** * We can use toObservable() for a Single to interact with an Observable. * Or we can add the various overloads to Observable.merge/concat/zip/mergeWith/concatWith/zipWith/etc * Adding the overloads though does result in a lot of API clutter. */getDataC().mergeWith(getDataA().toObservable());
}
publicstaticSingle<String> getDataA() {
returnSingle.<String> create(o -> {
o.onSuccess("DataA");
}).subscribeOn(Schedulers.io());
}
publicstaticSingle<String> getDataB() {
returnSingle.just("DataB")
.subscribeOn(Schedulers.io());
}
publicstaticObservable<String> getDataC() {
returnObservable.interval(10, TimeUnit.MILLISECONDS).map(i -> "c" + i);
}
/** * Example of Single.Promise */publicstaticSingle<String> getDataD() {
Promise<String> p = Single.Promise.create();
newThread(() -> {
p.onSuccess("data-d-from-thread");
}).start();
returnp.getSingle();
}
}
Reposting a modified version of #2641 (comment) to provide reasoning for this proposal:
The evolution of thinking after using RxJava for a couple years is that libraries that expose public APIs can benefit from being able to communicate if something is scalar or vector – despite the "functional" argument that a scalar is just a "list of one". We are able to choose to use T or List<T> when creating a synchronous API, so why not for async as well? In other words, why should the use of RxJava exclude communicating when we do know something is a scalar, which is a very common use case, and results in a simpler mental model?
When composition occurs that results in multiple values then the type changes to an Observable. Not all composition and transformation requires this though – zip, amb, map, flatMap all can return Single. Others like merge will return an Observable.
Another consideration is that APIs that expose data access are generally separate from the consuming code that composes them, and it is these data access APIs where the scalar Single is most effective at providing communicating via a static type such as:
Single<T> getData()
versus
Observable<T> getData()
does this actually simplify anything?
There is one thing I think it simplifies that is more than just making it easier due to familiarity. It communicates when a user does NOT need to think about multiple values, and that does indeed simplify code.
A Single will always behave in one of 3 ways:
respond with an error
never respond
respond with a success
An Observable on the other hand may:
response with an error
never respond
respond successfully with no data and terminate
respond successfully with a single value and terminate
respond successfully with multiple values and terminate
respond successfully with one or more values and never terminate (waiting for more data)
The mental model for these extra possibilities do definitely increase the complexity and are a big part of the pushback for using RxJava that I have seen for request/response models.
These more complex models do still end up happening as soon as composition occurs, but now that composition is declared by the consumer, and the consumer knows the contract of the producer to be simpler if it is a Single.
For example, in "service layer" type code we could have getters like this:
This is now simpler to understand because the types narrow the scope for A and B so we know we don't need to consider multi-value or infinite cases (which generally need to be explained in the javadoc). The C type in contrast clearly indicates that it wants to support multiple values otherwise it would have just been a Single.
familiarity can always be learned, but complexity is harder to avoid
My thinking has evolved to think that trying to force everything to be a vector results in increased complexity of a different kind that can be addressed by a slight increase in complexity of the RxJava APIs (2 types instead of 1). Static types communicate common information that simplifies the mental model and constrains available states and operations that can be applied. This seems worth the increase in API surface area.
could you explain why there is a need for this embedded Promise type
@sdeleuze it is not required and I should probably remove it. I added it to prove a common programming model.
Below are two ways of achieving the same thing. The first eliminates nesting but does involve an extra type. However, Promises are "hot" whereas the Single.create approach supports hot and cold execution.
Activity
Matthias247 commentedon Sep 18, 2014
I really would love to see this because of 2 reasons:
Futuretype in the Java standard library if you have to target < Java8. Even withCompleteableFutureavailable this uses executors for the continuations. For an RxJava integration using an Rx Scheduler is a better match - we can then receive Observable events and Future continuations on the same scheduler.Observablein the API and thereby won't know that it emits only a single item. He also won't know if it's hot (the function call which returns theObservablewill immediatly start an operation without the need forSubscribe()or not (needsSubscribe()). Therefore I think the ability to model such single return-value async operations with Futures makes an API better understandable.headinthebox commentedon Sep 19, 2014
https://github.com/BoltsFramework/Bolts-Java is the java version of .net's task.
Rx.net has seamless integration between observable and task.
However without async await syntax, tasks/futures do not provide much advantage. I don't ever expect async await to be added to java BTW.
Nilzor commentedon Nov 24, 2014
Yes, but RxJava hasn't. An integration/interfacing with Bolts could be an option instead of separate implementation, but it would probably require som changes to Bolts as well. Bolts, unlike RxJava, rely on Executors.
Over what? Observables? Simpler syntax and easier learning curve would be one. Right now I'm using Observables for Scalar data (HTTP responses) because that's the only thing Retrofit supports, but it's been a challenge to sift through the wiki finding just the methods I need for these kind of tasks. continueWith=flatMap? Not intuitive. I simply feel I'm using the wrong tool for the job.
benjchristensen commentedon Nov 24, 2014
The
flatMapoperator would almost certainly exist on therx.Future. For example, see the Twitter Future that hasflatMap: https://twitter.github.io/util/docs/index.html#com.twitter.util.FutureWhat makes the use of
flatMapnot intuitive on anObservablethat has a single value versus multiple values?Nilzor commentedon Nov 24, 2014
First and foremost the naming. "FlatMap" doesn't tell me anything. What's "unflat" about a Scalar value? What part of the operation involves mapping of any kind? Promise.then is much more descriptive to me.
But I realize it might be my background coming from Tasks in C# to Promises in Javascript and then Observables that might be "polluting my mind". I also might have misunderstood this isse. Maybe you could elaborate, since you're the OP?
headinthebox commentedon Nov 24, 2014
flatMapin RxJava isselectManyin C#. Since you mention you know Tasks form C# you should read this http://blogs.msdn.com/b/pfxteam/archive/2013/04/03/tasks-monads-and-linq.aspx. Technically speaking the Task API is a so-called co-monad, but since they are continuations, Task can also be formulated as a monad. In Rx .NET we hadmanySelectfor a while that corresponds tocontinueWith.benjchristensen commentedon Nov 25, 2014
An async continuation that returns another
Futureneeds to be flattened:In that case the
flatpart is flattening the returnedFuture, otherwise you'd end up with aFuture<Future<T>>rather than aFuture<T>.The function can
mapfromTtoRhowever you wish.If there is no need to flatten then it would be
map:If it is purely a side-effect, then the naming is up in the air but in RxJava thus far it is
doOn*method names to signal the side-effects, so something likedoOnSuccess/doOnErroron a scalarFutureand just pass the value through:Nilzor commentedon Nov 25, 2014
Ok thanks for taking the time to answer my questions, it made things clearer.
Matthias247 commentedon Nov 25, 2014
I think the issue here is that
flatmapis a description what happens in general whereasthenorcontinueWithdescribe it in a more concrete way for theFuturetype.Some time ago I had no experience with FP programming, so
flatmapdidn't tell me anything. HowevercontinueWithis more self-describing so I could understand it and it's easier to discover in an API documentation. I like it. But as I later learned thatthen/continueWithare equivalent toflatmapso now I understand it too. Maybe the same applies to other users?Why not expose both function signatures? Don't think it would be a problem and I think it would make the familarity higher for OO as well as FP programmers.
However I thought the more general question was whether RxJava would get a Task/Future like structure at all? I would still like to see it. Bolts looks good, but having a single library for all kinds of asynchronous things and with the possibility for interaction between structures (futures to observables and the other way around, sharing of schedulers, ...) would be even nicer.
benjchristensen commentedon Nov 25, 2014
Yes, that's the more general question. I tend to think we should.
Agreed, which is why I want it part of RxJava. Most everything would be shared, mostly just the type signature would differ.
[-]rx.Future[/-][+]rx.Future/Task[/+]akarnokd commentedon Feb 5, 2015
Can someone explain what this Future/Task should do, or more specifically, what is the underlying issue we want to solve that
ObservableorAsynccan't do (efficiency?)?benjchristensen commentedon Feb 6, 2015
Represent request/response of a single value. It is purely a design thing, not functional. Many, many people would prefer to represent scalar values such as IO request/response (think HTTP, REST, RPC) as a Future/Task but be able to merge them into Observable streams. It is one of the most common questions I get.
Rx.Net has it because .Net already has
Taskthat works nicely withObservablesince they were built for each other. The JavaFutureis unusable in this sense andCompletableFuturein Java 8 can be made to work but it's entire API design is incredibly different and they are always "hot".15 remaining items
akarnokd commentedon May 11, 2015
@benjchristensen I meant potential postixes, i.e., ScalarTask, ScalarObservable or ScalarFuture, etc.
Since we will use the experimental annotation, let's proceed with
Singleand come back to the name in a few months once enough use experience has been accumulated.schrepfler commentedon May 11, 2015
I like Single but just to throw more alternatives, Observable1 or OneObservable can be considered to avoid mixing up with other class names/libraries.
benjchristensen commentedon May 12, 2015
Here are examples of using
Single:sdeleuze commentedon May 12, 2015
@benjchristensen I like this new proposal but could you explain why there is a need for this embedded
Promisetype?benjchristensen commentedon May 12, 2015
Reposting a modified version of #2641 (comment) to provide reasoning for this proposal:
The evolution of thinking after using RxJava for a couple years is that libraries that expose public APIs can benefit from being able to communicate if something is scalar or vector – despite the "functional" argument that a scalar is just a "list of one". We are able to choose to use
TorList<T>when creating a synchronous API, so why not for async as well? In other words, why should the use of RxJava exclude communicating when we do know something is a scalar, which is a very common use case, and results in a simpler mental model?When composition occurs that results in multiple values then the type changes to an
Observable. Not all composition and transformation requires this though –zip,amb,map,flatMapall can returnSingle. Others likemergewill return anObservable.Another consideration is that APIs that expose data access are generally separate from the consuming code that composes them, and it is these data access APIs where the scalar
Singleis most effective at providing communicating via a static type such as:versus
There is one thing I think it simplifies that is more than just making it easier due to familiarity. It communicates when a user does NOT need to think about multiple values, and that does indeed simplify code.
A
Singlewill always behave in one of 3 ways:An
Observableon the other hand may:The mental model for these extra possibilities do definitely increase the complexity and are a big part of the pushback for using RxJava that I have seen for request/response models.
These more complex models do still end up happening as soon as composition occurs, but now that composition is declared by the consumer, and the consumer knows the contract of the producer to be simpler if it is a
Single.For example, in "service layer" type code we could have getters like this:
This is now simpler to understand because the types narrow the scope for
AandBso we know we don't need to consider multi-value or infinite cases (which generally need to be explained in the javadoc). TheCtype in contrast clearly indicates that it wants to support multiple values otherwise it would have just been aSingle.My thinking has evolved to think that trying to force everything to be a vector results in increased complexity of a different kind that can be addressed by a slight increase in complexity of the RxJava APIs (2 types instead of 1). Static types communicate common information that simplifies the mental model and constrains available states and operations that can be applied. This seems worth the increase in API surface area.
benjchristensen commentedon May 12, 2015
@sdeleuze it is not required and I should probably remove it. I added it to prove a common programming model.
Below are two ways of achieving the same thing. The first eliminates nesting but does involve an extra type. However, Promises are "hot" whereas the
Single.createapproach supports hot and cold execution.rx.Single
rx.Single
rx.Single
rx.Single
rx.Single
rx.Single
rx.Single
benjchristensen commentedon Jun 10, 2015
An
@Experimentalversion was merged in #3012