Hello,
New to dart but coming from Kotlin/Java world, I miss the Single/Maybe and Completable type. I found them really useless at least to express what you really want to return Observable can be all of those types and you can't really know what to expect if you didn't write the code yourself ^^ (and even in that case sometimes :D)
By having those types it will make more clear what you'll have as a result. I might be able to work on this and make a PR if it's something you might consider :)
Hey hey :) Yah, I also like those types provided by RxJava! It's something I've thought of before, but I think it's interesting to think about these types, what Dart offers, and which ones really add value to the ecosystem.
For example, I wonder if we need Single? Dart has the Future class and support baked-in at the language level. It's not identical, but the intention is the same: Represent an async call that returns a single value or error. The Stream standard lib & RxDart also return Future in several cases: .toList, .first, .last, etc. Therefore, I think implementing a new Single type would be a bit confusing, since it would be another way to represent the same thing without the language-level support and compatibility with other libraries.
In addition, I could see Completable being a class that simply implements Future<void> (since it's a one-off call with no return value). Perhaps Completable is more descriptive than Future<void> once you're used to it, but I think Future<void> is also a clear indication of an async value that will complete with no useful value and doesn't introduce a new abstractions to understand.
I think Maybe is where it gets interesting. This is something unique that Dart / RxDart don't currently offer, and might be worth taking a look at. It's kind of a union type of Future<void | T>. Perhaps it could be implemented as a Maybe<T> which extends Future?
Anyhow, these are my thoughts on the matter! What do you think? Convinced by these arguments? Total Hogwash? Got ideas of how to implement Maybe in a solid way?
Hum not quite convinced but maybe that's just me :D
Tell me if I'm wrong but we also have Future in Java, and we still have Single/Completable... I understand that Future is close to Single but for me you loose the context/interoperability of RX by using them so you'll have each time need to convert it into an observable to be able to chain them.
And that's that point that I personally don't like, or maybe I didn't understand how it works more easily ^^ I'm very new to dart and didn't dig into it too much. I'll be glad to have some enlightenment if I'm wrong :)
I also prefer Completable than Future
I'll dig more into Future in dart but I think it might be doable by extending Future :)
Sure thing -- If ya haven't worked with Dart Future much, I'd definitely consider playing with it a bit more and seeing whatcha think. Dart's Future is used much more broadly for Flutter development than Java's Future is for Android development (I haven't done as much Java server-side). This includes Widgets like FutureBuilder and many APIs that return Futures, such as Navigator.push. Furthermore, you get language-level support for working with Futures using async and inside async* functions.
In addition, you often need to convert between Single and Observable in RxJava as well. For example, operators like toList() return a Single<List<T>> whereas Dart Streams / RxDart toList() returns a Future<List<T>>. You may also need to convert a Single to an Observable via mySingle.toObservable(), for example if you want to concat the Single to the end of another observable. In Dart, this is myFuture.asStream().
I guess I feel like if we introduce Single it would be a poor imitation of what Future has to offer, but please feel free to play around and see if ya come up with something interesting! These are just my thoughts.
Hey @jaumard -- any update or feedback on this one?
Yes I had a chance to play a bit with Future and yes as it's already deep integrated it will be complicated to add Single that does almost the same.
But might worth writing in the doc somewhere that if you're coming from another implement of RX Single is replaced by Future instead.
About Maybe and Completable I'm still convince that it's needed :) from what I saw Maybe might be doable by extending Future. And completable will be only Future
For now didn't have time to try implement the Maybe form a Future, I'm starting my new job beginning of july and I want to try to push Flutter ^^ if I manage to do it I'll have more time to try implementing it
Future is nice replacement indeed, at least until you need to chain it with observables, e.g. .andThen() from RxJava. How do you suggest to do so?
I like Future a lot, but still using Single and the others we can use all the Rx operators and combine them into complex structures that with Future it won't be possible.
I think they should be implemented and the docs should make it clear that for most cases Future is enough and Single might be overengineering. But at the end the choice is there and people can use it if they want to.
One example I can think of is a webserver call, returning a Single, where we can compose it to auto-retry in a timeout, with exponential back-off. This would be a lot simpler with Rx composition then with the Future API.
I'm open to adding some Rx-y features on Future, but not to introducing a separate Single.
We've had tons of discussions regarding Observable vs Stream and have finally settled with extension methods on Stream.
The same could be done for Future, although a lot of functionality is already there:
future.then accepts FutureOr, so this effectively is map and flatMap.
Single.just is Future.value
etc...
I'd say this isn't a high priority, because switching between Future and Stream is straightforward:
For the retry use case, we'd need something similar to:
Rx.retryFuture(() => Future.value(1).timeout(const Duration(seconds: 1)), 3);
which can be done today via
Rx.retry(() => Stream.value(1).timeout(const Duration(seconds: 1)), 3).first;
It would be useful as well if the How to between Future and Single and others be documented, like the map and flatMap you mentioned. This helps to speed up people coming from other languages.
Single is really not a Future. Future have memory; they store a value after the complete. Single is a Stream that is guaranteed to emit at most once on the type level. Although for some (or even most?) use cases it's a similar thing, for other it is not. Additionally, Future doesn't support unsubscription and Stream does. While it might be considered a limitation of Future, it's one more pragmatic argument to have first-class Single.
I added Single type to rxdart_ext pkg: https://pub.dev/documentation/rxdart_ext/0.1.1-dev.1/rxdart_ext/Single-class.html 馃槂
A Stream which emits single event, either data or error, and then close with a done-event.
Success case: ------------(*)|------
data done
Failure case: ------------(x)|------
error done
NOTE: Single extends Stream, so all operators and transformers for Stream are available for Single as well.
Single is suitable for one-shot operations (likes Future but lazy - executes when listening), eg. making API request, reading local storage, ...
import 'package:http/http.dart' as http;
Single<User> fetchUser(String id) {
return Single.fromCallable(() => http.get(Uri.parse('$baseUrl/users/$id')))
.flatMapSingle((res) => res.statusCode == HttpStatus.ok
? Single.value(res.body)
: Single.error(Exception('Cannot fetch user with id=$id')))
.map((body) => User.fromJson(jsonEncode(body)));
}
Create Single
Factory constructors.
Static methods provided by RxSingles class
Convert others to Single via extensions.
Operators for Single (returns a Single instead of Stream)
Most helpful comment
Sure thing -- If ya haven't worked with Dart
Futuremuch, I'd definitely consider playing with it a bit more and seeing whatcha think. Dart's Future is used much more broadly for Flutter development than Java's Future is for Android development (I haven't done as much Java server-side). This includes Widgets likeFutureBuilderand many APIs that return Futures, such asNavigator.push. Furthermore, you get language-level support for working with Futures usingasyncand insideasync*functions.In addition, you often need to convert between Single and Observable in RxJava as well. For example, operators like
toList()return aSingle<List<T>>whereas Dart Streams / RxDarttoList()returns aFuture<List<T>>. You may also need to convert aSingleto anObservableviamySingle.toObservable(), for example if you want to concat the Single to the end of another observable. In Dart, this ismyFuture.asStream().I guess I feel like if we introduce
Singleit would be a poor imitation of what Future has to offer, but please feel free to play around and see if ya come up with something interesting! These are just my thoughts.