Breaking Change: Extension methods imported with a prefix are accessible.

68 views
Skip to first unread message

Leaf Petersen

unread,
Nov 12, 2019, 1:33:43 AM11/12/19
to General Dart Discussion
TL;DR: Extension methods defined in a library imported with a prefix will be accessible.  

What is changing?

In the extension method preview release, extension methods that were imported via an extension were not accessible: that is, they could not be invoked using the ordinary invocation syntax.

// "thing.dart"
class Thing { 
}
extension Show on Thing {
  void show() {
    print("This is a thing");
  }
}
// "client.dart"
import "thing.dart" as p;
void test() {
  p.Thing().show(); // Error: method not found
}

In the official release, we will be changing this so that extension methods will be accessible even if imported via a prefix.

// "thing.dart"
class Thing { 
}
extension Show on Thing {
  void show() {
    print("This is a thing");
  }
}
// "client.dart"
import "thing.dart" as p;
void test() {
  p.Thing().show(); // Ok
}

This change has landed in bleeding edge, and will be rolling out shortly (if not already)

Why is this changing?

Early testers of the feature found it very surprising that unlike instance members, extension members were not available if imported via a prefix.  Packages that defined extensions as part of the interface of a class being defined in the same library behaved unintuitively, since importing the package via a prefix yielded only a subset of the functionality.  This made extension members behave less "method-like" than users were expecting, and caused unnecessary verbosity (double imports) in client code.

Based on this feedback, we decided that making extension members accessible even when imported with a prefix was the right choice.

What will break?

This has been tested against google3 and should cause no breakage.  

It is unlikely that your code will break because of this.  Code will break only when:
  - A library imports two or more libraries that define an extension "m".
  - At least one of the libraries is imported without a prefix, and the rest are imported with a prefix.
  - The member "m" is invoked in the importing library.
  - All of the definitions of "m" apply at the invocation site.
  - The version of "m" defined in the un-prefixed library was the most specific version before the change, and is no longer the most specific version after the change.

In this unlikely scenario, the invocation of "m" that previously resolved to the extension member from the un-prefixed library will now either resolve to a more specific extension in a different (prefixed) library, or will cause a static error because the invocation becomes ambiguous.

Your code may also break if you import a library as deferred, and that library defines extension members, since it is currently disallowed to import extension methods from a deferred library.  We plan to relax this restriction in the near future.

How do I fix my code?

If your code breaks because an extension member resolves differently or becomes ambiguous, you can restore the previous behavior by hiding the extension name in the import of the prefixed library or libraries. 

// "thing.dart"
class Thing { 
}
extension Show on Thing {
  void show() {
    print("This is a thing");
  }
}

// "extra.dart"
extension ExtraShow on Thing {
  void show() {
    print("This is also a thing");
  }
}


// "client.dart"
import "thing.dart";
import "extra.dart" as p;
void test() {
  p.Thing().show(); // Error: ambiguous extension
}

In the code above, the call to "show" was previously unambiguous, and has now become ambiguous.  To fix it, replace the import as follows:

// "client.dart"
import "thing.dart";
import "extra.dart" as p hide ExtraShow;
void test() {
  p.Thing().show(); // Ok
}

If your code breaks because it imports a library which defines extensions as deferred, change the import to hide the name of the extension as in the previous example.

As usual, follow up here or in the Dart issue tracker with any issues or concerns.

-leaf
Reply all
Reply to author
Forward
0 new messages