Open dgreensp opened 5 months ago
It's correct that the UP algorithm, which is what unifies two types, never give a FutureOr
type as result unless one or both of the original types is a FutureOr
type, and it doesn't (yet) depend on the context type.
@stereotype441 is currently working on making it take the context type into account, which will likely help here.
(At that point, it won't introduce a FutureOr
type unless at least one of the types, or the context type, is a FutureOr
type.)
Until then, cast one of the two branches to the type you want.
That sounds great!
On Fri, Mar 29, 2024 at 11:10 AM Lasse R.H. Nielsen < @.***> wrote:
Correct. The UP algorithm, which is what unifies two types, never give a FutureOr type as result unless one or both of the original types is a FutureOr type, and it doesn't (yet) depend on the context type.
@stereotype441 https://github.com/stereotype441 is currently working on making it take the context type into account, which will likely help here. (At that point, it won't introduce a FutureOr type unless at least one of the types, or the context type, is a FutureOr type.)
— Reply to this email directly, view it on GitHub https://github.com/dart-lang/sdk/issues/55337#issuecomment-2027563526, or unsubscribe https://github.com/notifications/unsubscribe-auth/AABGBJVGJDRLVRQSIWHJ3G3Y2WOBDAVCNFSM6AAAAABFOT6N6WVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDAMRXGU3DGNJSGY . You are receiving this because you authored the thread.Message ID: @.***>
There's also this case, probably similar, in which the bound of a type parameter comes into play:
import "dart:async";
// intent is for argument to be a map of Strings to a mixture of
// String, Future<String>, and (statically) FutureOr<String>.
// It would be nice if the implementation can check if T == String
// as a quick way to know if all the keys are statically known to be
// Strings.
void foo<T extends FutureOr<String>>(Map<String, T> map) {
print(T);
}
void main() {
// all these work fine
foo({"hello": "world"});
foo({"hi": Future(() => "there")});
final FutureOr<String> hey = "hey";
foo({"also": hey});
foo({"hello": "world", "also": hey});
foo({"hi": Future(() => "there"), "also": hey});
// mixing String and Future<String> doesn't work, whether or not there
// is a FutureOr<String> involved.
// error: Couldn't infer type parameter 'T'
foo({"hello": "world", "hi": Future(() => "there")});
// error: Couldn't infer type parameter 'T'
foo({"hello": "world", "hi": Future(() => "there"), "also": hey});
}
Ah, and in particular, because of the way Dart tracks collection type parameters at runtime, there is another advantage to the argument type of a call like foo({"asdf": "asdf"})
being Map<String, T>
where T extends FututureOr<String>
, because it actually allows the type of the map to be Map<String, String>
. Whereas if foo
simply takes a Map<String, FutureOr<String>>
, so that becomes the inferred type of {"asdf": "asdf"}
, now you have a Map whose values are all strings, but that's not reflected in the (runtime) type of the Map, so if you want to convert it into a Map<String, String>
, you actually have to copy it or use .cast()
which allocates a wrapper object.
I'm not sure if this should be a separate issue, but here's another FutureOr quirk: it prevents inference of function arguments during analysis:
import "dart:async";
FutureOr<void Function(int)> foo() {
return (n) => n + 1; // n is dynamic, not int
}
@stereotype441 Here is a case where unification doesn't work as expected, simply by replacing Future with a subclass of Future:
import "dart:async";
import "package:async/async.dart";
FutureOr<T> func<T>(Future<T>? a, FutureOr<T> b) {
return a ?? b; // works
}
FutureOr<T> func2<T>(ResultFuture<T>? a, FutureOr<T> b) {
return a ?? b; // error: a value of type FutureOr<Object?> can't be returned
}
This is becoming a pain point in code I am writing. Would it be possible to make FutureOr something that can be found as a LUB?
Is there any way to annotate or hint this code further without changing the conditional expression or switch expression into statements? It seems like it is maximally annotated; the type checker would need to do the rest.