dart-lang / dart-lang/language

Let a wildcard as an actual type argument indicate a request for inference

Open
#3,963 7 comments 11 reactions 0 assignees View on GitHub
feature type-inference
Dominant language
TeX
Stars
2.9k
Forks
239
Avg merge
2d 18h
Merged PRs (30d)
14

Description

This is a proposal for supporting expressions like `MyClass('Hello')`, where `_` is used to indicate that the corresponding type parameter should be obtained from type inference. This allows us to specify some type arguments whose value would otherwise not fit the needs, and omit other type arguments whose inferred value is as desired. For example:

```dart
class A {
final X x;
final _ys = [];
A(x);
void add(X Function(Y) g) => _ys.add(g(x));
}

// Assume that we want to create an `A`.
void main() {
// We can specify all type arguments, ..
var a1 = A(42); // .. but `int` is redundant.

// We can infer all type arguments, ..
var a2 = A(42); // .. but `Y` is now `dynamic`.

// With this proposal, we get the best of two worlds.
var a3 = A<_, String>(42);
}
```

We could allow a wildcard type argument that occurs as the last element of the actual type argument list to stand for multiple type arguments. For example, this would allow us to use `C` to stand for `C` where both `T2` and `T3` are inferred.

There should not be a large amount of expressive power in this feature, but it does take a few steps to emulate it in the current language.

We can get the same effect today if we're willing to create a type alias for each choice of fixed type arguments, and if those type arguments can be denoted globally:

```dart
// Same `class A`.

typedef AHelper = A;

void main() {
// Emulate `A<_, String>(42)`.
var a4 = AHelper(42); // Creates an `A` and infers that type for `a4`.
}
```

We can also use a function to support arbitrary choices of "fixed" type arguments:

```dart
// Same `class A`.

// Create a context where we can denote a type which isn't denotable globally.
void foo() {
// Emulate `A<_, Z>(42)`.
// Note that we can't declare `typedef AHelper = A;`
// because type aliases are global.

// We can still specify all type arguments, brute force.
var a5 = A(42);

// However, we want to avoid specifying `int` because it can be inferred. So we
// use a local function that has the choice of `Z` as 2nd type argument baked in.
A local(V v) => A(v);
var a6 = local(42);
}

void main() => foo();
```

It is a new feature to include a special (wildcard-ish) treatment of `_` when it is used as an actual type argument. It is also (slightly) breaking because it is possible today to declare some entities whose name is `_` (e.g., top-level variables or, indeed, type parameters).

However, I think it's more helpful to allow developers to request this kind of partial type inference by means of `_` than it is to preserve the ability to use a type whose name is `_` as an actual type argument.

In the end, it would actually be possible to create a type alias to provide access to such types under a different name:

```dart
class _ {}
typedef AMuchBetterName = _;
List list = []; // Can't use `List<_>`, but this is better, anyway.
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.