Skip to content

What's the right syntax for an implicit cast/inline cast operator? #193

@leafpetersen

Description

@leafpetersen

There is a longstanding desire to have better syntax for casts that appear inline in a method chain:

  ((a as Whatsit).frobIt(3) as Whosit).fizzle();

If we move ahead with #192 there is also a desire to have a short syntax for implicitly casting to the context type (see for example the hand-rolled cast function used in this cl removing implicit casts).

It seems likely that we can solve both of these with a single piece of syntax. For example, we could add a postfix cast operator !! which casts to the context type, also usable in the form !!<T> which casts to T. Code that formerly used an implicit cast:

takeList(List<int> f) {}
Iterable<int> mkIterable() {}
void test() {
  takeList(mkIterable());
}

could be rewritten as follows:

takeList(List<int> f) {}
Iterable<int> mkIterable() {}
void test() {
  takeList(mkIterable()!!);
}

And the example from above with inline casts could be written more cleanly as well:

  a!!<Whatsit>.frobIt(3)!!<Whosit>.fizzle();

We might wish to specify it to be an error to use !! to perform a side cast when the type is not explicitly written.

An alternative syntax which would be more consistent with existing Dart code would be to use .as and .as<T>.

takeList(List<int> f) {}
Iterable<int> mkIterable() {}
void test() {
  takeList(mkIterable().as);
}
  a.as<Whatsit>.frobIt(3).as<Whosit>.fizzle();

Unfortunately this would be a breaking change, since as is not a reserved word in Dart. In practice this is likely non-breaking, @munificent is proposing to scrape some code to verify this.

If we had extension methods + generic getters, we could specify this as a generic extension method:

extension Cast on Object {
  T get as<T> => this as T;
}

This doesn't capture a side cast restriction, but it may avoid the breaking change issue. It seems unlikely that we will have both of these features in place in a suitable time frame to use this encoding though.

Other ideas for syntax that have been put out for consideration:

  • x.(as T) and something like x.(as) or x.(as _)
  • this could possibly be a use of two general mechanisms:
    • a way of using an operator in a postfix position
      • enabling await a -> a.await, a + b -> a.+(b), possibly also foo(x, y) -> x.foo(y)
        - a prefix version of our existing cast operator
  • x.cast<T>
    • This is probably not feasible, since we have cast methods on various core library types.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions