Language: Static Extension Methods

Created on 11 Oct 2018  ·  121Comments  ·  Source: dart-lang/language

UPDATE: Feature specification is available here:
https://github.com/dart-lang/language/blob/master/accepted/2.7/static-extension-methods/feature-specification.md

Original in-line proposal:

Possible solution strategy for #40. This issue doesn't (currently) have an concrete proposal.

Scoped static extension methods are successfully used in C# and Kotlin to add extra functionality to existing classes.

The idea is that a declaration introduces a static extension method in a scope. The extension method is declared with a name (and signature) and on a type, and any member access with that name (and signature) on something with a matching static type, will call the extension method.
Example (C# syntax):
``c# class Container { // Because all methods in C# must be inside a class. public static String double(this String input) //this` marks it as an extension method on String
{
return input + input;
}
}
...
String x = "42";
String y = x.double(); // "4242"

The method is a *static* method, all extension methods do is to provide a more practical way to invoke the method.

In Kotlin, the syntax is:
```Kotlin
fun String String.double() {
    return this + this;
}

The String. in front of the name marks this as an extension method on String, and the body can access the receiver as this. Apart from these syntactic differences, the behavior is the same.

It's possible to declare multiple conflicting extension methods. Say, you add a floo extension method to both List and Queue, and then I write myQueueList.floo(). It's unclear which one to pick. In case of a conflict, the usual approach is to pick the one with the most specific receiver type (prefer the one on List over the one on Iterable), and if there is no most specific receiver type, it's a static error.
C# and Kotlin both allow overriding by signature, so there risk of conflict is lower than it would be in Dart, but the same reasoning can apply.

So, Dart should also have scoped extension methods. We can probably get away with a Kotlin-like syntax, for example:

String String.double() => this + this;

Extension methods can be used on any type, including function types and FutureOr (although probably not very practically on the latter). It can also be a generic function.
Example:

T List<T>.best<T>(bool preferFirst(T one, T other)) =>
    this.fold((T best, T other) => preferFirst(best, other) ? best : other);
...
List<int> l = ...;
print(l.best((a, b) => a > b));

In this case, the type argument should probably be inferred from the static type of the receiver (which it wouldn't be if the receiver was just treated like an extra argument).

Another option is to allow:

T List<var T>.best(bool preferFirst(T one, T other)) => ...

Here the method is not generic, so the type variable is bound by the list, and it will use the actual reified type argument at run-time (and the static type at compile-time, which can cause run-time errors as usual).
Example:

List<T> List<var T>.clone() => new List<T>.from(this);

Then anyList.clone() will create another list with the same run-time element type as anyList.
It effectively deconstructs the generic type, which nothing else in Dart currently does. The extension method gets access to the reified argument type, just as a proper member method does, which is likely to be necessary for some functionality to be implemented in a useful way.
(Type deconstruction is a kind of pattern-matching on types, it might make sense in other settings too, like if (x is List<var T>) ... use T ...).
This feature might not be possible, but if it is, it will be awesome :smile:.

One issue with extension members is that they can conflict with instance members.
If someone declares T Iterable<T>.first => this.isEmpty ? null : super.first; as a way to avoid state errors, it should probably shadow the Iterable.first instance getter. The decision on whether to use the extension method is based entirely on the static type of the receiver, not whether that type already has a member with the same name.
Even in the case where you have a static extension method on Iterable for a member added in a subclass, say shuffle on List, an invocation of listExpression.shuffle() should still pick the static extension method, otherwise things are too unpredictable.

Another issue is that static extension methods cannot be invoked dynamically. Since dispatch is based on the static type, and you can't put members on dynamic (or can you? Probably too dangerous), there is nothing to match against.
Or would putting a static extension method on Object work for dynamic expressions?
If so, there is no way out of an extension method on Object, so perhaps casting to dynamic would be that out.

We say that static extension methods are available if the declaration is imported and in scope. It's not clear whether it makes any difference to import the declaring library with a prefix. There is no place to put the prefix, and Dart does not have a way to open name-spaces, which would otherwise make sense.

If we get non-nullable types, it might matter whether you declare int String.foo() => ... or int String?.foo() => .... The latter would allow this to be null, the former would throw a NSMError instead of calling when the receiver is null (and not a String). Until such time, we would have to pick one of the behaviors, likely throwing on null because it's the safest default.

We should support all kinds of instance members (so also getters/setters/operators). The syntax allows this:

int get String.count => this.runes.length;
void set String.count(int _) => throw 'Not really";
int String.operator<(String other) => this.compareTo(other);

We cannot define fields because the method is static, it doesn't live on the object.
We could allow field declarations to be shorthand for introducing an Expando, but expandos don't work on all types (not on String, int, double, bool, or null, because it would be a memory leak - mainly for String, int and double - the value never becomes unavailable because it can be recreated with the same identity by a literal).

extension-methods feature

Most helpful comment

Note that this feature has now been turned on by default in 2.6.0-dev.5.0. If you download and run from that version, you won't need any experiment flags.

We'd love to have some of our early adopters try this out - please give us feedback and bug reports!

All 121 comments

This would be my preferred solution to
https://github.com/dart-lang/sdk/issues/34778 and #40 . Coming from Objective-C, it's second nature for me to add methods to classes (and instances, but, I'll take what I can get.)

It feels much more elegant than #42 too. My main desired use case is adding constructors to classes from other libraries that my library configures constantly.

Would it be possible to add an extension method on a type with a specified generic argument?

For example, could I add

num Iterable<num>.sum() => this.fold(0, (a,b) => a + b);

I have troubles seeing the benefit of class Container { // Because all methods in C# must be inside a class.
They are not methods, they are static functions used in a way they look like methods, which means the class name is merely a namespace.

I expect code with #43 to be easier to read because it's more clear what's going on
in comparison to code where it looks like a method is called but it's actually something else.

I was definitely not advocating using the C# syntax, I was just showing as an example of an existing implementation (with the constraints enforced by that language).

Dart should definitely go with top-level syntax for extension methods, like in Kotlin, perhaps a syntax like the one I suggest later.

It's an interesting idea to add static methods to a class. There should be no problem with that:

static int List.countElements(List<Object> o) => o.length;
...
   List.countElements(someList) 
...

It's simpler than adding instance methods, you don't have to worry about dispatching on the static type of the object. You still have to worry about conflicts.

I'm not sure whether adding static methods to a class is really that useful. After all, it's longer than just countListElements as a top-level function. It does introduce a potentially useful context to the use. More interestingly, adding a factory constructor would then be a simple extension.
Since it has to be a factory, I guess the syntax could just be:

factory String.reversed(String other) => ...;

So, factory, static or nothing would designate the operations as factory, static or instance.
Seems possible.

As for specializing on type argument, that should also be possible.
The type to the left of the . is a type, not a class. It should work with any type, and the extension methods apply whenever the static type of the receiver of a member invocation is a subtype of the extension member receiver type. So, it can work, and probably should.
My only worry is that it's too convenient - we can't do something similar with normal instance members, so I fear people will start using extension members for that reason. We should consider whether we'd want generic specialization of classes, even if it's just syntactic sugar for a run-time check.

Would it be possible to add an extension method on a type with a specified generic argument?

C# allows this and I have found it useful in practice, like the example you give. There are some interesting questions of how that interacts with subtyping. Is an extension method on Iterable<num> also available on Iterable<int>? Probably yes, but I'm not sure if that leads to confusing cases.

I'm not sure whether adding static methods to a class is really that useful.

It plays nice with auto-complete, which is one of the main way users discover APIs.

The autocomplete is literally why I want it so badly.

Sent from my iPhone

On Oct 16, 2018, at 17:45, Bob Nystrom notifications@github.com wrote:

Would it be possible to add an extension method on a type with a specified generic argument?

C# allows this and I have found it useful in practice, like the example you give. There are some interesting questions of how that interacts with subtyping. Is an extension method on Iterable also available on Iterable? Probably yes, but I'm not sure if that leads to confusing cases.

I'm not sure whether adding static methods to a class is really that useful.

It plays nice with auto-complete, which is one of the main way users discover APIs.


You are receiving this because you commented.
Reply to this email directly, view it on GitHub, or mute the thread.

This proposal could make Flutter's .of pattern auto-completable and probably more terse. E.g. you could auto-complete context. and get context.theme, context.localizations, and others as suggestions.

I think this would be awesome to have for libs like RxDart or to make the of methods in Flutter more idiomatic.

I like the Kotlin syntax where you just do fun SomeClass.saySomething() = "say something". Probably the = should be changed with the => used in Dart but I think the approach overall makes sense.

Hopefully, with Dart going towards being a more strictly typed language, this is more possible than before? Are there any plans for this? I have seen it asked in a number threads.

Hopefully, with Dart going towards being a more strictly typed language, this is more possible than before?

Yes, the shift to the new type system is absolutely what enables us to consider features like this now. They were basically off the table before.

Are there any plans for this? I have seen it asked in a number threads.

No concrete plans yet, but lots of discussion and ideas (which is what this issue is part of).

Are static extension getters/setters planned to be supported as well?

Md5Sum String.get md5 => Md5Sum.from(this);

I'm not sure what a reasonable use case of an extension setter would be, unless you count this example:

void Stream<T>.set latest<T>(T value) => this.add(value);

myStream.latest = 4; // so you can add events via assignment?
myStream.latest = 6;

How would type inference work for generic extension methods? Would it be possible to write:

T T.get staticType<T> => T;

4.staticType; // int
(4 as num).staticType; // num

Not sure if this is good or bad. :)

I'm not entirely enthusiastic about adding deconstruction, also. For the sake of clearly being able to explain how the feature works, I think it's best to keep it a static concept, at least until there are other ways of doing runtime deconstruction in dart first. However I do see the issues in attempting to implement a function like List<T>.clone without it.

I'm also not stoked about the interaction with dynamic. I foresee this making json decoding worse when people inevitable add methods to Map, List, String, etc, and then use them in their unmarshalling code and get unexpected dynamic static values in the process....perhaps we should

  • match all possible extension methods with dynamic
  • report a compile-time error if more than one extension method matches
  • do a runtime check on the lookup in checked mode and report an error
  • launch this feature with a lint for extension method calls on dynamic values

I don't see a clear win/win here which concerns me.

Otherwise, I love it.

Most Kotlin extension function for Iterable are inline functions. One cool feature about inlined functions are non-local returns. This case should be covered in the proposal when even reified functions are covered.

// Kotlin inline function (forEach) allows return of outer scope
fun hasZeros(ints: List<Int>): Boolean {
    ints.forEach {
        if (it == 0) return true // returns from hasZeros
    }
    return false
}
// Dart's forEach is not inlined and can't return outer scope
bool hasZeros(List<int> ints) {
    ints.forEach((i) {
        if (i == 0) return true; // forEach isn't inlined and can't return hasZeros here
    });
    return false; // always returns false
}

// for loops allow return but why should a for loop have more features than forEach?
bool hasZeros(List<int> ints) {
    for (var i in ints) {
        if (i == 0) return true; // returns from hasZeros
    }
    return false;
}

Are static extension getters/setters planned to be supported as well?

Personally, I strongly feel that if we add extension methods, we should support all kinds of members: getters, setters, subscript, other operators.

In my C# days, I often missed the lack of extension getters.

Most Kotlin extension function for Iterable are inline functions.

Is this really considered a good idea? It's largely a performance hack (basically a kind of macro), and it stops you from using actual functions as parameters. It's not that uncommon in Dart from what I've seen to pass named functions or tearoffs to forEach, which I think you can't do in Kotlin.

Passing functions instead of a lambda is also supported in Kotlin. It also works for inlined functions.
Inlining is not only a performance "hack", it's the only way non-local returns and reified type parameters can be implemented. See https://kotlinlang.org/docs/reference/inline-functions.html

Ah, I see, it looks like you can use the ::foo syntax to pass a function.

What about factory/static extension too?

factory MyClass.name() {
  return MyClass(42);
}

static void MyClass.anotherName() {

}

yeah, this should support factory extension too. In its simplest form, it would without the factory keyword. That can always be v1.

I was thinking about this the other night, and I think we should use some kind of surrounding "class"-like declaration for extension methods. Something like:

extension MyExtensions on Iterable<T> {
  Iterable<T> get whereNotNull => this.where((e) => e != null);
}

My reasons are thus:

  • It allows multiple extension methods with the same name attached to different types in the same library. We could support this without a surrounding declaration, but it makes the namespace of the library weird because there is effectively some kind of overloading going on. In concert with that...

  • It gives you a name you can use in an import combinator to hide or show a set of extensions to avoid a collision. If we place them at the top level and let you show/hide them by the method's name, that gets weird if we want to allow multiple extension methods with the same name (but extending different types) in the same library.

  • It gives you a way to define private extension methods that have nice names without an underscore prefix but aren't exported from the library.

  • It clearly separates out the type of the receiver from the type of the extension method itself. In particular, I think it helps clarify extension methods on generic classes versus generic extension methods on classes.

  • It keeps the grammar simpler and more orthogonal. Users don't have to learn a new syntax for defining an extension method, generic extension method, extension method on generic type, extension getter, extension operator, etc. Instead, they just learn how to define an extension type, and then the body of that is just normal regular member declarations like they already know how to do.

  • It potentially interacts better with metaprogramming because there is a surrounding declaration you can get to that contains the extension members and specifies the type they extend. Otherwise, we'll have to define separate constructors for extension methods, extension getters, etc. We can't reuse existing machinery for methods and getters because those don't have any convenient place to register the type that they extend. (I suppose we could stuff that type in an implicit parameter, but then things like arity get weird.)

I realize this syntax is a little more redundant and coming up with useful names might be hard, but I think the verbosity is worth it. In cases where you are defining multiple extensions on the same type (which I believe is pretty common in practice) this is less redundant because it means you don't have to specify the type being extended on every member.

and I think we should use some kind of surrounding "class"-like

So basically https://github.com/dart-lang/language/issues/42?

Or is it "just" a namespace?

I think we _should_ use some kind of surrounding "class"-like declaration for extension methods.

Yes, I'm more of a fan of this as well. I think it's much clearer, especially if we want to allow access to the generic type variables of the class.

Or is it "just" a namespace?

Still just a namespace.

Do you have an example demonstrating the difficulty with generic type access in the absence of a name? Wouldn't something like the following work?

extend Iterable<T> {
  Iterable<T> get whereNotNull => this.where((e) => e != null);
  // Has access to both C and T.
  Iterable<C> convertNonNull<C>(C Function(T e) converter) => this.whereNotNull.map(converter);
}

I do see some value in being able to hide extensions due to conflicts or because they don't work for your app (maybe you need non-lazy map functions only). I think even there you can do it without a name, e.g.:

import 'library_with_extensions.dart'
  hide
    Foo,
    Iterable extension,
    Bar;

Or hide specific methods:

import 'library_with_extensions.dart'
  hide
    Foo,
    Iterable { convertNonNull },
    Bar;

I do realize this may complicate the grammar, but if it's the right thing to do the expense might be worth it.

Do you have an example demonstrating the difficulty with generic type access in the absence of a name?

It's less about having a name for the surrounding construct as it is the lack of a surrounding construct at all. With Kotlin-like syntax, I think this gets pretty confusing:

Iterable<C> Iterable<T>.convertNonNull<C>(C Function(T e) converter) => this.whereNotNull.map(converter);

A more confusing example is when you want to distinguish between hanging an extension off a specific generic type versus off a parameterized type. In other words, both of these are valid:

// Applies to Iterables of any type:
Iterable<T>.foo() => ...

// Applies to Iterables of a specific type:
Iterable<int>.foo() => ...

So when the parser sees an identifier inside a generic type for an extension method, how does it know when that names a type parameter ("T" here) versus a type argument ("int")? With a surrounding declaration, we could clarify that like:

extension MyExtensions<T> on Iterable<T> {
  foo() => ...
}

extension MyExtensions on Iterable<int> {
  foo() => ...
}

Here, it's clear that the former makes the extension itself generic and the latter only applies to a specific type. (This means my example in my earlier comment is wrong. I should have written extension MyExtensions<T> on Iterable<T>.)

Pardon me joining the conversation here, I just have a usability question re: namespaces. Would it be required to "typecast" as extension type to access extension methods or not? E.g.

extension MyExtensions on Iterable<int> {
  int sum() => reduce(...);
}

void getTotal(List<int> values) {
  int total = values.sum(); // would this work or we'll need to do
  total = (values as MyExtensions).sum();
  return total;
}

If we don't need to typecast (which I'd prefer) and there are two extension types declaring sum method how to know which method is used? (I suspect that analyzer may produce a warning and suggest to typecast in this case, possibly with a quick-fix action in IDE).

Would it be required to "typecast" as extension type to access extension methods or not?

No, there's a different issue (https://github.com/dart-lang/language/issues/42) that talks about defining a new type whose underlying representation is the same as an existing type. That would require you to cast.

Here, the named thing that contains the extension methods exists just as a namespace. It's a syntactic entity and lets you control which extensions are imported by a library. But once you've imported it, any members it defines would be accessible directly on any value of the type it extends with no cast required.

and there are two extension types declaring sum method how to know which method is used?

This would be an error since it's ambiguous. You'd have to resolve it, likely by hiding one of the sum methods so that only one is in scope.

@pulyaevskiy You might have found the strongest argument in favor of requiring a name. We could use it to disambiguate name collisions.

I think Golang-style cast syntax, values.(MyExtensions).sum(), might be better, but not sure if it's worth investing in alternative cast syntax just to resolve name collisions.

Ok, so extensions are not necessarily types.

It seems that hiding with imports (import 'something' hide FooExtensions;) should work nicely in most cases. There might be one scenario when a more fine-grained control is needed. E.g. user wants to use methods from different extensions at the same time.

Would it be possible to allow per-method control on extensions when importing? For instance if I import two packages which both add sum and avg methods but I want to use sum from the first package and avg from the other.

import 'package:a/a.dart' hide AExtensions.avg;
import 'package:b/b.dart' hide BExtensions.sum;

Would it be possible to allow per-method control on extensions when importing?

Possible, yes, but I'm not sure it will be necessary. Collisions tend to be rare in practice.

At this point this really is #42 if you need a cast.

I don't think that's a good thing. Many things would benefits from extensions _without_ cast. Like BuildContext of Flutter for the of pattern.

Possible, yes, but I'm not sure it will be necessary. Collisions tend to be rare in practice.

I guess this could be implemented at later point if it becomes a big issue. Agree it's better to start simpler.

At this point this really is #42 if you need a cast.

According to above answer by @munificent there is no need to cast. Which is definitely a better UX, so 👍 .

The syntax proposed here is definitely a strawman. I've also begun to think that a wrapper would make sense for the cases where you declare multiple extension methods on the same type.

Whether it should have a name is more unclear to me. It's annoying to have to write a name if it has no meaning (and it doesn't in most cases). I think it's very confusing that the C# extension methods are in scope just because an arbitrary wrapper class is, and that you need the wrapper class to begin with, but since all code in C# is inside a class, it makes a kind of sense there. It doesn't in Dart.
So, I'd probably prefer to not require a name (and then we might as well not allow a name since there is no incentive for the author to add one).
Avoiding collisions should still be doable by matching on type: hide List<int>.*

I'm not very convinced about adding static extension static members or constructors. You just write Class1.foo(x) instead of Class2.foo(x) or class1Foo(x) to access the static method.
I'd much rather keep the option of writing static helper functions inside the extension block, like:

extends String {
  String firstGrapheme => _graphemes(this).first;
  String lastGrapheme => _graphemes(this).last;
  static Iterable<String> _graphemes(String source) sync* { ... }
}

If the block is really a namespace, then static members declared there should belong to that namespace, not to the class being extended. So, only instance members, but all instance members (except fields ... so I guess all instance members which can be external).

One interesting thing about having a name for the extension is it could give you syntax for tearing off the extension as a function.

extension Text on String {
  GraphemeIterator substring(int start, [int end]) {//...}
}

GraphemeIterator Function(String, int, [int]) substring = Text.substring;

So, I'd probably prefer to not require a name (and then we might as well not allow a name since there is no incentive for the author to add one).

It does feel a little pointless to give it a name most of the time. But having a name makes it easier to show/hide it without having to add more complexity to show/hide itself. Given that the number of extension "classes" is probably relatively low, I think a little verbosity in return for a simpler import system might be a good trade-off.

One interesting thing about having a name for the extension is it could give you syntax for tearing off the extension as a function.

This too. Here's an interesting question. Is something like this worth supporting:

extension StringStuff on String {
  void someExtensionMethod() {
    _helper();
  }

  void _helper() {
    print(this); // <-- The string!
  }
}

In other words, will we want to take advantage of implicit this in extension methods and support defining helper methods and other stuff like the above? If so, having a surrounding declaration makes that natural. Without that, you have to explicitly turn this into a parameter to hand it off somewhere else:

void someExtensionMethod() {
  _helper(this);
}

void _helper(String self) {
  print(self); // <-- The string!
}

There's an argument that the latter is better because it's more explicit. But it also might just be more tedious, especially if the type you are extending is some complex generic type. Users might get confused about having to make that helper method a generic method in order to shuttle along the type parameter.

When the _helper method is a extension, this is also string. The wrapper doesn't make it any more helpful (in that case).

void String.someExtensionMethod() {
  _helper(this);
}

void String._helper() {
  print(this); // <-- The string!
}

About the imports: Kotlin imports extensions one by one, always by name. It is not possible that importing a file/class/library automatically imports all extensions. Conflicts are very rare and can be overcome with as otherName.

I'm not a big fan of the wrapper. Lack of tooling (auto-import of classes is still not possible in IntelliJ or VSCode) should not drive language features. For me, the namespace wrapper is boilerplate, something dart usually tries to avoid. The import problem should be completely automated by tools, one of the last things a dev has to think about.

[...with the extension wrapper] Users don't have to learn a new syntax
This is s a good point.

For me, the namespace wrapper is boilerplate, something dart usually tries to avoid.

Interesting - for me the Kotlin style feels really noisy and verbose. It's shorter for one extension, but as soon as you have more than one, having to repeat the name over the class you're extending over and over seems really annoying. Do you find it more common to only write single standalone extension methods? That's the only case I can see the Kotlin syntax being shorter. And I still like the grouping that the block syntax gives you, as well as the natural analogy to class syntax.

Unrelated to the naming discussion, will access to generic type parameter from an extension allow "stealing" it? AFAICT this is not supported today:

List produceListOfSomethings() { ... }

var list = produceListOfSomething();
// no way to extract the generic type of the `list`

However, it seems now I can do this:

extension TypeBurglar<T> on List<T> {
  T stealType() => T;
}

var list = produceListOfSomething();
Type stolenType = list.stealType();

Not sure if there's anything wrong with allowing it.

@yjbanov are you talking about this? :

Type _typeOf<T>(List<T> list) => T;


var list = produceListOfSomething();
Type stolenType = _typeOf(list);

Because this is something we can already do.

Do you find it more common to only write single standalone extension methods?

I analyzed and categorized all extensions in my current project, 113 in total.

  • 57% (64) are standalone extensions. Meaning they would not share the same extension group with any other extension.
  • 47% (53) are file private scoped. Those would not result in import problems, they are only used within a single file. 7 of them were even defined inside a function.
  • 35% (40) are single line expressions.

By looking at my existing functions I found a few cases which would lead to problems with grouping into an extension object/thing:

Extensions for nullable types

In kotlin, with non-nullable types, extensions can be defined for T and T?. Defining extensions for both would result in two different extension groups in dart. (Assuming non-nullable types will be added to dart)

// dart
extension StringExt on String {
  String first5Chars() => this.take(5);
}

extension _InternalStringExt on String? {
  String? first7Chars() => this?.take(7);
}
// kotlin
fun String.first5Chars() = this.take(5)
private fun String?.first7Chars() = this?.take(7)

Add visibility to that problem and I can only very rarely see any extension group having multiple functions.

Extensions inside classes:

Inside a Presenter I found a extension for a external type (Disposable) which requires accses to internal properties of that class.

class Presenter {
    private rxhelper: RxHelper = RxHelper(this)

    protected fun Disposable.disposeInOnDestroy(): Disposable  {
        return rxHelper.manageDisposable(this) // extension body has access to class scope
    }  
}

This may be my misunderstanding, but the current proposal requests extension to be defined top-level, not inside classes. (Also very likely since dart doesn't support inner classes).

Extensions inside functions

I also found myself defining extensions inside a function. Dart supports functions inside functions. It should not prevent extension functions beeing defined inside functions. Example

@rrousselGit

AFAICT functions will accept any generic argument as long as the argument is assignable. So for example, I can pass <Object> or <dynamic> because the reified List<String> is assignable to List<Object>. When omitted, Dart will not attempt to extract the reified type. For example, the following program prints dynamic despite the list being List<String>:

Type _typeOf<T>(List<T> list) => T;

main() {
  var list = produceListOfSomething();
  Type stolenType = _typeOf(list);
  print(stolenType);
}

produceListOfSomething() {
  return <String>[];
}

I would love if the extension method has access to the actual type arguments of the object.
There are potential extensions that won't work well without being able to know, e.g., the exact element type of the list (say, adding a splice function to List).
Can't guarantee that it's possible, but it's definitely desirable. Bob is correct that adding a name to the declaration gives us a place to put a type parameter:

extension SomeName<T> on List<T> { ... }

It's still a little magical that the T is bound to the actual type argument of the List when we match.

The _typeOf function indeed only reifies a static type, not the actual run-time type argument of the list.

Here's a concrete reason to use a surrounding declaration for extension methods instead of something like Kotlin's syntax:

void Function() Function().method() {}

Is this an extension method on functions returning functions, or an extension method on functions that returns a function? With a surrounding declaration, it's explicit:

extension A on void Function() Function() {
  method() {}
}

extension A on Function() {
  void Function() method() {}
}

I analyzed and categorized all extensions in my current project, 113 in total.

@passsy This is great, thanks for doing this! It's super useful to get concrete data. I've been thinking about extensions mostly as an API level affordance, but it sounds like you get a lot of value out of them as a way to define local helpers. Definitely something for us to keep in mind.

Another potential syntax:

void function(String this, int parameter) {}

Since this is a reserved keyword it's not a breaking change.
The bonus point here is that we could use the function in 2 different ways:

"string".function(42);
function("string", 42);

It can be interesting for the functional side of Dart. It's not necessarily incompatible with the current extension Name on Type proposal either.
This is a syntax we will find in typescript for example.

I'm not very convinced about adding static extension static members or constructors. You just write Class1.foo(x) instead of Class2.foo(x) or class1Foo(x) to access the static method.
I'd much rather keep the option of writing static helper functions inside the extension block, like:

I will disagree. I think this is a relatively important aspect of extensions when combined with something like code-generators.

Currently, code-generators requires to modify the class itself, which is far from ideal.
With extension constructors, we could make things such as json_serializable works on _every_ classes, not just those defined by the user.

Hmm it would be amazing if this could support extension functions over <T>, just like in Kotlin.

See this section on scoping functions if you're unfamiliar with what I'm talking about.

inline fun <R, T> T.let(transform: (T) -> R): R = transform(this) 

inline fun <R, T> T.run(transform: T.() -> R): R = transform(this)

inline fun <T> T.also(action: (T) -> Unit): T {
    action(this)
    return this
}

inline fun <T> T.apply(action: T.() -> Unit): T {
    action(this)
    return this
}

Then for example

class DogFragment: Fragment() {
    companion object {
        fun newInstance(dogId: String) = DogFragment().withArgs {
             putString("dogId", dogId)
        }
    }
}

Where withArgs is:

inline fun <T: Fragment> T.withArgs(argsBuilder: Bundle.() -> Unit): T = 
    this.apply {
        arguments = Bundle().apply(argsBuilder)
    }

Not sure how plausible it is for Dart, but I can guarantee that the ability to use .apply { and .let { can greatly reduce clutter (caused by having to declare a local variable that nobody is interested in, then having to talk to it multiple times, having to copy-paste the variable name over and over)

Another potential syntax:

void function(this: String, int parameter) {}

Since this is a reserved keyword it's not a breaking change.

True, but this is also already used inside parameter lists for initializing formals:

class Foo {
  String field;
  Foo(this.field); // <--
}

It's not ambiguous, but I think using this to mean two very different things would make it harder to read. Also, the syntax you propose doesn't gracefully extend to support extension getters, extension setters, extension operators, or generic extension methods on generic classes. For that last one, I mean things like:

extension ListStuff<S> on List<S> {
  foo<T>() { ... }
}

Here, I'm declaring an extension method that can be applied to lists of any type S. The method itself is also generic and takes a type argument T, which might be unrelated to S.

@munificent I can't see why generic extensions or getters/setters couldn't be possible with the syntax proposed by @rrousselGit.
I slightly changed Remis syntax, because I think he accidentally used this: T instead of T this. It reminds me very much on how extensions work in Groovy.

Generic extensions

// Bob syntax
extension ListStuff<S> on List<S> {
  foo<T>() { ... }
}

// Remi syntax
void foo<S, T>(List<S> this, T param) { ... }

getter/setter extensions

class Rectangle {
  num left, top, width, height;

  Rectangle(this.left, this.top, this.width, this.height);

  // normal getter/setter
  num get bottom => top + height;
  set bottom(num value) => top = value - height;
}

// Bob syntax
extension MyRectangleExtension on Rectangle {
  num get right => left + width;
  set right(num value) => left = value - width;
}

// Remi syntax
num get right(Rectangle this) => left + width;
set right(Rectangle this, num value) => left = value - width;

operator extensions

class Vector {
  final int x, y;

  Vector(this.x, this.y);

  // normal operator
  Vector operator +(Vector v) => Vector(x + v.x, y + v.y);
}

// Bob syntax
extension MyVectorExtension on Vector {
  Vector operator -(Vector v) => Vector(x - v.x, y - v.y);
}

// Remi syntax
Vector operator -(Vector this, Vector v) => Vector(x - v.x, y - v.y);

Remis solution has two advantages:

  • The extensions can be defined everywhere, even inside classes and functions. It's just a function with an argument following a convention.
  • I don't have to come up with a new name for every extension.

The extensions can be defined everywhere, even inside classes and functions. It's just a function with an argument following a convention.

Oh, that's an interesting idea.

Which means we could have multiple times a method with the same "name" in a class?

class Foo {
  Vector v;

  bool operator ==(Vector this, Vector v) => true;

  // always return true because of the vector override above
  bool operator ==(Foo foo) => v == foo.v;
}

And that there's a notion of scope when accessing method/property?

num get bar(Vector this) => 42;

Vector v;
v.bar // 42

void function(Vector v) {
  num get bar(Vector this) => 0;

  v.bar // 0
}

I thought about naming clashes as well. It shouldn't cause too many issues. Dart doesn't allow method overloading. Therefore it shouldn't be possible to define an extension which clashes with an existing member function or another extension with the same name.

num get bar(Vector this) => 42;

Vector v;
v.bar // 42

void function(Vector v) {
  num get bar(Vector this) => 0; // ERROR: `Vector.bar -> num` is already defined

  v.bar
}

But it should be possible to define extensions with the same name for the same type in two different scopes.

class Vector {
  final int x, y;
  Vector(this.x, this.y);
}

void functionA(Vector v) {
  num get bar(Vector this) => 0;

  v.bar // 0
}

void functionA(Vector v) {
  // no naming clash because it's the only Vector.bar function in scope
  num get bar(Vector this) => -1; 

  v.bar // -1
}

I'm not sure about that one. Multiple extensions with the same name are not about method overload, but variable shadowing instead.

object.myExtension();

is just syntax sugar for:

myExtension(object)

With plain functions, the following is entirely valid:

num bar(Vector v) => 42;

Vector v;
bar(v); // 42

void function() {
  num bar(Vector v) => 0;

  bar(v); // 0
}

So I don't see any logical reasons for extensions to prevent such usage if the syntax allows it.

Hmm it would be amazing if this could support extension functions over <T>, just like in Kotlin.

This is a really interesting wrinkle. Basically it lets you be polymorphic over the receiver type and still use the "." syntax. Based on the test below, it looks like they resolve clashes in preference for the more specific extension.

inline fun <R, T> T.foo(transform: (T) -> R): R = transform(this) 
inline fun <R> String.foo(transform: (String) -> R) : R = transform("world")

fun start() {
    "hello".foo({print(it.toString() + "\n")})
    3.foo({print(it.toString())})
}

fun main(args: Array<String>) {
start()
}
void foo<S, T>(List<S> this, T param) { ... }

This still has some challenges around type inference. Consider a case like:

T firstOfType<S, T>(Iterable<T> this, S type) => this.firstWhere((e) => e is S);

In practice, users will want type inference to handle the first type argument since it's known from the type of the receiver. However, the second type argument can't (usually) be inferred and the user does expect to pass it explicitly. Dart doesn't have any mechanism for filling in "part" of a type argument list and leaving the rest inferred. We could add that, but it feels weird to me.

num get right(Rectangle this) => left + width;

Ah, interesting. Using get here avoids the ambiguity between an extension getter and a zero-argument extension method.

Overall, though, this syntax still feels strange to me. The way it is declared gets farther and farther from how it is used. Here, it looks like a getter has a parenthesized argument list, but you don't call it like that. In the generic example, it looks like it takes two type arguments when really a user should only ever explicitly pass one.

I just don't see a lot of value in making the receiver look like a parameter in the declaration when the entire intent of the feature is that it's not like an argument at the invocation site.

It interacts with namespacing in strange ways too. Consider:

foo(int a) {}
foo(String a) {} // Error. Name collision.

bar(int this) {}
bar(String this) {} // OK.

I think users will rightfully be surprised if a keyword inside the parameter list also changes how surrounding name is resolved.

The extensions can be defined everywhere, even inside classes and functions. It's just a function with an argument following a convention.

This is orthogonal. Whatever syntax we choose, we could choose to allow it inside the body of a function if we want.

 num get right(Rectangle this) => left + width;

Or just do that Kotlin does,

num get Rectangle.right() => left + width;

Or what C# does

num get right(this Rectangle rect) => rect.left + rect.width;

T firstOfType(Iterable this, S type) => this.firstWhere((e) => e is S);

I think the second parameter is pointless, but in any case, would you not want this to be

S firstOfType<S, T>(Iterable<T> this) => this.firstWhere((e) => e is S) as S;

in which case downwards inference would fill it in? In any case, I think this applies to Kotlin as well, and presumably it doesn't seem to be too much of a problem? I suppose the non-parametricity of Dart means that there are some use cases (such as the above) which don't come up in Kotlin though.

Thanks for fixing my mistakes. Yes, downwards inference will fill in the type sometimes, but not all the time:

S firstOfType<S, T>(Iterable<T> this) => this.firstWhere((e) => e is S) as S;

var list = [1, 2.2, 3];
var firstDouble = list.firstOfType<double, num>();

Here, there's no context type, so you have to write the type argument. That's fine, but it also forces you to write the other type argument too, because (as far as I know) we wouldn't support partially applying the type argument list and letting inference fill in the other parts.

I've recently started using callable classes to have functions overload:

const someFunction = _SomeFunction();

class _SomeFunction {
  const _SomeFunction();

  void call() {}
  int customName() => 42;
}

which allows

someFunction();
int result = someFunction.customName();

I've been thinking, it would be cool to allow extensions to work on functions too:

void someFunction() {}

extension on someFunction {
  int customName() => 42;
}

This allows the same behavior as previously showcased, but as extensions.
In this example, there's obviously no this (unless used on methods), but the principle is the same.

This could be a solution to method overload from https://github.com/dart-lang/language/issues/145

I'm proposing 'scoped class extensions' (#177). The point is that 'static scoped extension methods' are just that, _static_, and it is useful to keep in mind that object-oriented dispatch (that is, instance method invocation whereby the chosen implementation is the one that is most specific for the dynamic type of the given receiver) is a useful mechanism, also for extensions. Scoped class extensions will add something to an existing class hierarchy which works very much like a regular instance method.

Scoped class extensions are compatible with static scoped extension methods: Both mechanisms can be given a similar syntax, and they are so different that it makes sense to have both.

In particular, static scoped extension methods make sense for receiver types that are function types or FutureOr, but scoped class extensions only support extensions of class types; static scoped extension methods can support more expressive type patterns (#170) for specifying which types of receivers they can be applied to, but scoped class extensions only admit a restricted form of type patterns, because they should offer the guarantee that the dynamic pattern match will always succeed whenever the statically known receiver type matches. And so on—the point is that these two mechanisms are quite different.

Yet, static scoped extension methods fall out as a special case of scoped class extensions: If only one class is extended, then the methods in a scoped class extension will be statically resolved.

So the point I'm making here is that we should keep in mind that the static scoped extension methods are similar to static methods, but that mechanism can very well coexist with a mechanism that is similar to instance methods, and I think it's useful to have both.

I filed an issue for discussion of the implications of the two approaches to dealing with generic parameter arguments discussed above (that is, allowing the extension to get the true runtime type parameters of a generic receiver as opposed to only the statically known approximation thereof).

Filed an issue for discussion of the interaction between short-circuiting null aware operators and extensions on nullable types here.

I don't know too many practical use cases for static extension methods, but those I know are not necessarily expressible via proposed syntax. The proposal is complicated, so I could easily have missed something, but - suppose we want to implement a method "sum" for an Iterable.

The sum can be defined generically for an Iterable<T> where T has operator+(T,T)->T defined and we have an empty value (zero) available. E.g. we can imagine "sum" for the list of ints, or doubles, or BigInts, or Strings. With great difficulty, we can (probably) lump ints and doubles together, using the fact that each implements "num", and that value "0" can serve as empty value for both, but BigInt is not num, String is not num, so there's no way we can write a generic "sum". Even with this proposal, we would have to write 3 different implementations anyway. Maybe we can do it in a much simpler manner just by defining
Iterable\.sum, Iterable\.sum etc each for a concrete class, as opposed to abstract "T where T has an operation + and T.empty"? Who knows how many facts about T we need to know in other scenarios? My point is that generality of this kind requires much more mathiness in the language than dart provides. And again, there are not too many use cases of practical interest IMO. Do you know more examples?

T has operator+(T,T)->T defined and we have an empty value (zero) available. E.g. we can imagine "sum" for the list of ints, or doubles, or BigInts, or Strings.

@tatumizer you sound like you've just implemented monoids in your head and you're attempting to implement typeclasses, but I think that's far above what the original proposition is for.

you sound like you've just implemented monoids in your head

I did??? WOW! Honestly, I didn't expect that much from myself. Nice surprise! And I'm just warming up! :)
(Relevant song)

but I think that's far above what the original proposition is for.

And what was it for? That was actually my question. :)

@tatumizer the idea for the original proposition (as far as I know, considering they started saying things like "static extension classes" which I don't understand) is that:

1.) there is a class out there that was written by someone else

2.) you want this class to know something that it doesn't know (aka, you'd like to call a particular method that it doesn't have)

3.) you have two options:

  • a.) you extend the class and add the method into it

  • b.) you create a static global function that takes the class instance as its first argument


You don't want to do 3.a) because you don't want to use inheritance just to add a new method, but using a StringUtils.isEmpty(someString) is clunky compared to someString.isEmpty().

In Android using Kotlin for example, you can easily replace a view.setVisibility(View.VISIBLE) with view.show() using fun View.show() { this.visibility = View.VISIBLE; } and now you can just call view.show() over any View.

Nicer than for example ViewUtils.show(view). Internally though it does the same thing, so it's kinda "syntactic sugar" over 3.b).


It gets trickier when there are generic constraints involved, for example in Kotlin you could do this:

infix fun <A, B, C> Pair<A, B>.to(third: C) = Triple(this.first, this.second, third)

And you could call to on any Pair<A, B> and it would be preferred to a to defined as

infix fun <A, B> A.to(second: B) = Pair(this, second)

because the first version for a Pair<A, B> is "more concrete" than A.

This behavior lets you do the following:

    val pair: Pair<String, String> = "a" to "b"
    val triple1: Triple<String, String, String> = pair to "c"
    val triple2: Triple<String, String, String> = "a" to "b" to "c"

    // val triple3: Pair<Pair<String, String>, String> = "a" to "b" to "c" // this is not what happens

@Zhuinden : In fact, we have 2 different problems addresses by extensions: the first one is what you describe in your paragraph "there is a class out there that was written by someone else", but the second one is indeed trickier: write a function that applies to a collection of values of specific type, like List\.sum(). This case is different from the first one: there's no class "List\" written by somebody else at all, there's only List\. I'm not sure that the same term "extension" can characterize both cases, and not sure the term "static extension methods" really captures the difference (there's another proposal #177 by @eernstg that is targeting the first meaning specifically). I think the difference between 2 use cases is pretty significant. The second case is all about collections/containers (see *). (Not sure how your example with pairs and triplets fits into this).

What I'd like to get is a better rationale for is the need to make the solution for the second case so abstract.
Because if you'd like to make it more abstract really, then you have to go all the way towards haskell (which, if you ask me, is not necessarily a good idea b/c too much of a good thing)

My point was exactly that maybe, for practical purposes, we will be fine with less generality b/c the use cases are limited to computing sums, averages etc on numeric values, and that case (the most common one in practice!) is not covered by the proposal anyway: there's still no way to write a "sum" function that works for ints, doubles and BigInts alike (at least in current incarnation of dart). Is it worth the effort to develop a complicated machinery just for aggregates, which still doesn't work for aggregates? That was my question. Specifically, what if we write

int List<int>.sum() => this.fold(0, (acc, it)=>acc + it);
double List<double>.sum() => this.fold(0.0, (acc, it)=>acc + it);
BigInt List<BigInt>.sum() => this.fold(BigInt.zero, (acc, it)=>acc + it);

instead of trying to roll it all into a single method? We can use code generator here, if we wish - after all, code generation is now all over the place in dart anyway.

(*) if you remove the term "collection" from the explanation, it would be difficult to explain what "in" and "out" means in this context).

Forget what I said earlier, making U-turn now.

What is "extension" anyway? How the extension is different from a type that extends given type?
Here's the concept. Let's treat "extension" as a wrapper around the type, similar to subclass, with the restriction that the wrapper cannot add new members or constructors, but can do anything else: implement an interface, adds some methods etc. This wrapper doesn't introduce a new type - rather, it's like a lens through which you can see the type under in a different perspective. Due to added implementation of an interface, the perspective in question can encompass several types, thus establishing a "horizontal" relationship between them, which remained hidden in the initial design.

The natural syntax idea for an extension would be to use the same syntax as for normal class declaration,
and add whatever stuff you want: methods, getters, operators (but not constructors or fields - the layout should not change). Extension can also use "implements" clause to mark the class as compilant with the new interface.

Example:

abstract class Monoid<T> { // maybe private? not sure.
  T operator+(T other);
  // should also provide zero, don't know how, b/c it's a static thing
}
// retrofitting classes with the interface that they implement already, but don't yet know about it
class double extension implements Monoid<double> {};
class int extension implements Monoid<int> {};
class BigInt extension implements Monoid<BigInt> {};

class Iterable<Monoid<T>> extension {
  // new language feature is needed to pass static info associated with the type 
  // without passing anything explicitly
  T sum() => fold(give_me_zero_for_type(Monoid<T>), (acc, it)=> acc + it);
}

Though I still don't know how to pass zero, it gets us quite close IMO.

I have no idea whether it's feasible or not, but at least the syntax is consistent with the rest of dart, and leverages existing conceptutal framework.

Another example (same as in #177 with new syntax):

class Expr extenstion { // to be inherited by all subclasses
  int eval() {  throw UnsupportedError(); }
}
class Literal extension {
  eval() => value;
}
class Sum extenstion {
  eval() => leftOperand.eval() + rightOperand.eval();
}

Here I'm unsure what is best: to add interface Eval and make all Expr, Literal and Sum implement it, or add the eval() method directly into Expr as above.

And now you reinvented Rust traits! :)
```rust
trait Monoid {
T operator+(T other);
T get zero;
}
impl Monoid for double {
get zero => 0;
}
impl Monoid for int {
get zero => 0;
}
impl Monoid for String {
get zero => "";
}
impl Monoid> for List{
get zero => [];
}
````
(-ish, caveat: I don't know what I'm doing).

Exciting news!!! Dart learned about monoid, too!

abstract class Monoid<T> {
  T operator+(T other);
}
class Int implements Monoid<Int> {
  static get statsCalculator {
    StatsCalculator.typeToZero[Int]??=Int(0);
    return StatsCalculator<Int>();
  }
  int value;
  Int(this.value);
  Int operator+(Int other)=> Int(this.value + other.value);
  toString() => value.toString(); 
}
class StatsCalculator<T extends Monoid<T>> {
  static final typeToZero = <Type, Monoid> {};
  T sum(Iterable<T> list) => list.fold(typeToZero[T], (acc, it)=>acc+it);
  // other methods (like Iterable.average) etc need more work
}   
main() {
  print(Int.statsCalculator.sum(<Int>[])); // prints 0 YES!!!
  print(Int.statsCalculator.sum([Int(1), Int(2)])); // prints 3
}

Can someone explain how my program (this monoid thing) actually works?
Specifically, how typeToZero[T] is implemented? Does compiler pass a hidden parameter containing the declared type of T to the method? What else gets passed surreptitiously? Does anyone know?
Or maybe it just works for no reason? I'm confused.

Another (re)discovery: mixins!

TL;DR: automatic mixins might be more powerful than extensions, while reusing the syntax we already have.

The rediscovery is based upon a couple of observations.

Observation 1: the main problem with implementing Monoid in dart is that we don't have any way to retrieve the value of zero for a given type. When we call method "sum" for an empty list of T, the result should be zero specific to type T (e.g. 0 or 0.0 or BigInt.zero), but we don't have any instance of T in the empty list to ask it "what is your zero?". It looks like a variant of chicken-and-egg problem.

The crucial (re)insight is: although we indeed have no instance of T, we certainly have an instance of Iterable<T> for which method sum is called. We can get zero by asking Iterable<T> about the zero for T. (obvious in retrospect, but took quite a while to figure it out!).

Observation 2: OK, but how can we retrieve information related to type T from Iterable<T>.
E.g., now we have to ask the instance of Iterable<int>: what is the zero for the type int?
How can we implement this question?

The idea is: use mixin.

What is mixin anyway? It's a number of methods available for a type as soon as the type satisfies (generally) a conjunction of some interfaces - "on" clause tells which ones. If our mixin wants to provide a method sum for Iterable<T>, it has to require 2 preconditions: 1) instance of Iterable<T> provides a getter for zero 2) instance of T supports operator +. Here's how it can be done:

auto mixin <T> on Iterable<T extends Additive<T>>, HasZero<T> {
   T sum() => this.fold(this.zero, (acc, it) => acc + it);
}

Automatic mixin is applied whenever the preconditions are met.
To make use of the above mixin, we need to retrofit some relevant classes with the interfaces required by its definition - this can be done through the same mechanism of automatic mixins.

abstract class Additive<T> {  T operator+(T other); } // operators are OK - no new syntax
abstract class HasZero<T> { T get zero; } // getters are OK - no new syntax
auto mixin on Iterable<int> implements HasZero<int> { int get zero => 0; }
auto mixin on Iterable<double> implements HasZero<double> { double get zero => 0.0; }
auto mixin on Iterable<BigInt> implements HasZero<BigInt>{ BigInt get zero => BigInt.zero; }
auto mixin on int implements Additive<int>;
auto mixin on double implements Additive<double>;
auto mixin on BigInt implements Additive<BigInt>;

What's going on here is this: the syntax of mixin allows to express preconditions imposed on a collection of items and on items themselves. Isn't it beautiful?

More (down-to-earth) examples:

auto mixin on String {
   String capitalize(String s) {...}
   // other methods
}
auto mixin <T> on void Function(T) { Type get parameterType => T; }
auto mixin <T> on List<T> {
    List<T> copyReversed() => List<T>(this.length)..setAll(0, this.reversed);
}

Restrictions: auto mixins are not allowed to modify the layout of the objects they apply to, and cannot override the methods of classes they are applied to; auto mixins are not allowed to conflict with each other when applied to the same object (if 2 mixins define the method with the same name, they cannot be applied to the same object). In short: everything is based on static types (sugar-only feature).

One last step - rename auto mixin back to extension - and we are done :)

P.S. Runtime checks are still required, but which ones - I haven't figured out yet.:)

I went through the strawman. Aside from capturing runtime type arguments, I think I'm on board with just about all of it. I really like it. A couple of minor points:

Allowing nullable extensions is something we can add later, but we should figure out whether there are known use-case for it, and if so, we might as well include it from the beginning.

I definitely want this. I've profitably relied on being able to define extension methods on "nullable" types in C# before and it's really handy. Things is String.IsNullOrEmpty() work better as extension methods. I imagine users will come up with other convenience methods like this for moving between nullable and non-nullable types. (That does raise interesting questions around short-circuiting null-aware operators...)

Regarding static extension methods, like:

extension StringStuff on String {
  static foo() { print("ok!"); }
}

main() {
  String.foo(); // "ok!"
}

In terms of strict expressiveness, it doesn't accomplish much. But I actually do think we should seriously consider it. It allows outside libraries to add new "constructor-like" methods to existing classes in a discoverable way that plays nicely with auto-complete.

One of our main use cases is improving the string API for Flutter, and I can certainly imagine them wanting to add factory methods like:

String.fromCodePoints(...)

It's just a nice affordance for making extension methods feel like they really do extend a library. Also, I worry somewhat that if we don't do this, users will instead making extensions on other types. For example, with the above, I'd worry that if we don't let them add String.fromCodePoints(...) they'll instead add a "backwards" extension on List<int>:

extension CodePointLists on List<int> {
  String asCodePointString() { ... }
}

In other words, they'll just grab some other random argument and make that one the receiver even when doing so doesn't feel natural. Maybe this won't be a problem in practice, though.

Take 5: dead-simple version. No monoid (which is just a trivial case of potentially infinite variety of similar concepts). No retrofitting with interfaces. Going straight to the metal :-)

extension Sum on Iterable<X> where X in {BigInt, num} {
   X get _zero {
       match (X) {
         case BigInt: return BigInt.zero;
         case num: return 0;
       }
   }
   X sum() => this.fold(this._zero, (acc, it) => acc + it);
}

EDIT: changed the signature; we need both "on" and "where" clause. Noticed that "on" clause cannot be a comma-separated list because we already have "on" clause (on A, B, C) in the syntax of mixin definition where comma means conjunction (AND)

My 2 cents on extensions methods from my C# days, esp. from .NET Core: It made exploring APIs super difficult. Disrupts regular flow of reading code. I always had a feeling that this is just a feature to do some _plumbing_ without real benefits. "Why couldn't developers think of a pattern that supports modularity, instead of resorting to...this?" For larger libraries and projects, it was always a pain to explore what assemblies I had to have and what using statements I needed to access certain functionality.

Maybe I am too late to the party but wanted to give you my thoughts.

In Android Studio, the import statements (same as using for Visual Studio) can be automatically added as the auto-completion offers extension functions that are not yet imported explicitly by the user by hand.

So I think that's more-so an IDE problem than language feature problem.

Scoped static extension methods are successfully used in C#

About that:
What about extensions on _functions_?

Should we be able to do:

extension Foo on void Function() {
  // TODO: something
}

?

That would be useful for currying functions.

Static extension methods should work on any expressible type, including function types.
That means:

extension Curry2<R,S,T> on R Function(S, T) {
  R Function(T) curry(S arg1) => (T arg2) => this(arg1, arg2);
}

(or something similar) should be able to work.

You probably won't be able to overload the existing call method.
I hope it's possible to add a call extension method to types that don't already have one, so that they become callable.

can you please share the way to activate this experimental extension in vscode build ? we really need it for our development process

can you please share the way to activate this experimental extension in vscode build ? we really need it for our development process

@DanTup might know the answer to this. Note that this feature is not yet shipped though, and is still subject to breaking changes (including the very unlikely possibility that we would unship it). So by all means experiment with it and file any issues you encounter, but please don't publish packages that rely on it until it ships.

@leafpetersen I believe @renanyoy's question is about build/runtime. I know how to enable the analysis support (an experiment flag in analysis_options.yaml) but I'm not familiar with using it at runtime (or how complete that is).

@DanTup What is the experiment flag for the analyzer for extension methods?

Add the following to the analysis options file:

analyzer:
  enable-experiment:
    - extension-methods

Note that this feature has now been turned on by default in 2.6.0-dev.5.0. If you download and run from that version, you won't need any experiment flags.

We'd love to have some of our early adopters try this out - please give us feedback and bug reports!

I have a project where I'm porting Swift code over to Dart and the biggest missing piece of the puzzle was extension methods so I'll be happy to check it out

Just looking for some clarification, is it possible to use extensions to add implements or extends to a class? Here is an example in Swift

https://github.com/uber/RIBs/blob/master/ios/tutorials/tutorial1/TicTacToe/Root/RootComponent%2BLoggedOut.swift#L27

Something like:

class Foo {
  String get foo => "foo";
}

abstract class Protocol {
  String get bar;
}

extension on Foo implements Protocol {
  String get bar => "bar";
}

is it possible to use extensions to add implements or extends to a class?

No. For a slightly longer discussion see https://github.com/dart-lang/language/issues/475

I am having problem with auto completing the extensions. I have added a new file called "time.dart" with some extensions, like this:

image

My main file just can't auto complete it. If I try to fix the error I get the following:

image

As soon as I move the extensions to the same file as main, everything works:

image

If I add the import manually, it also works:

image

Is it an intended behavior? Isn't that really bad?

@shinayser - you might want to follow up on https://github.com/dart-lang/sdk/issues/25820 or file a new SDK issue. Discussing autocomplete behavior is a bit out of scope for this issue.

Is it an intended behavior?

Yes, it's intended that you need the import to use the extension. If you typed out the extension method manually I expect this behavior.

@shinayser - you might want to follow up on dart-lang/sdk#25820 or file a new SDK issue. Discussing autocomplete behavior is a bit out of scope for this issue.

Is it an intended behavior?

Yes, it's intended that you need the import to use the extension. If you typed out the extension method manually I expect this behavior.

Ops, sorry for that, I am just providing the feedback as @leafpetersen asked. I will post it on the right place now. Thanks! :)

I believe you need to import it since Dart seems to namespace to the individual file. I've noticed that Swift has a pretty wide namespace so it seems like you don't really need to import extensions for them to work.

Ops, sorry for that, I am just providing the feedback as @leafpetersen asked. I will post it on the right place now. Thanks! :)

Right, sorry, I should have mentioned - feedback on the design is useful here, but for bugs or implementation issues please file in github.com/dart-lang/sdk .

As others have mentioned, Dart's extension methods are statically scoped, so you only get access to the extensions if you import the file which defines them.

I know this is still a work in progress but i was curious if static extension methods would be able to fulfill implements contracts?

For example:

abstract class SomeInterface {
  String get someString;
}

class SomeClass implements SomeInterface {}

extension SomeExtension on SomeClass {
  String get someString => 'test';
}

As of 2.6.0-dev.7.0 they do not, with SomeClass claiming Missing concrete implementation of getter SomeInterface.someString., will they eventually or is this idea/usage not going to be supported?

I know this is still a work in progress but i was curious if static extension methods would be able to fulfill implements contracts?

For example:

abstract class SomeInterface {
  String get someString;
}

class SomeClass implements SomeInterface {}

extension SomeExtension on SomeClass {
  String get someString => 'test';
}

As of 2.6.0-dev.7.0 they do not, with SomeClass claiming Missing concrete implementation of getter SomeInterface.someString., will they eventually or is this idea/usage not going to be supported?

https://github.com/dart-lang/language/issues/475

Hello @jodinathan
I've seen that but I don't mean the extension adding the implements, or adding constructors, or adding static fields.

I am curious if the extension will be able to fulfill implements contract for getters/setters/fields of the class it is on as shown in my example, as long as the extension and class are in the same scope.

As of 2.6.0-dev.7.0 they do not, with SomeClass claiming Missing concrete implementation of getter SomeInterface.someString., will they eventually or is this idea/usage not going to be supported?

No, this won't be supported. I'm not sure how we would enforce the property that both always have to be in scope simultaneously, especially in the presence of subclassing. To be useful it seems like you'd also want to be able to substitute in different extensions? Do you have a concrete use case in mind for this?

No, this won't be supported. I'm not sure how we would enforce the property that both always have to be in scope simultaneously, especially in the presence of subclassing. To be useful it seems like you'd also want to be able to substitute in different extensions? Do you have a concrete use case in mind for this?

After talking it over with co-workers and re-reading some specs it seems like https://github.com/dart-lang/language/issues/177 (Scoped Class Extensions) will address the implements requirements, so I think we can wait for that. :) thanks for the info @leafpetersen!

Scoped class extensions do support late binding. However, they are still scoped. This means that, e.g., when x.foo() is executed, the choice about the following properties are made statically: (1) _Whether_ this is an invocation of a method from a class extension or not; if so, (2) from _which_ class extension we will select an implementation of foo. The latter is determined by the normal scope rules: If you import E1 and E2 but not E3 then we may choose E1.foo or E2.foo, but E3.foo is never considered. Let's say that we selected E2; then at run time we will select one of the implementations of foo in E2, based on the dynamic type of the receiver.

But this means that even though an instance of a class C could have all the methods needed in order to have the type I because some of them are declared in C and others are declared in E2, it would still be necessary to know exactly which ones come from C itself and which ones come from E2 (because the generated code is different).

So if you want to pass an object _o_ around (between different libraries with different imports) then we cannot be allowed to forget that the support in _o_ for the interface I consists of different kinds of methods.

However, we could of course obtain an object that genuinely implements I by creating a forwarder:

class C ... {...}
class I ... {...}

// Assume that we have declarations/imports such that all members of `I`
// can be called on an instance of `C`, as instance or extension methods.
class C_as_I implements C, I {
  final C forwardee;
  C_as_I(this.forwardee);

  // ... write forwarding methods/getters/setters for all members of C, I here.
}

Of course, it would be nice (and presumably not so hard) to add support for customized code generation for missing members of the interface of a class (as a variant of the support that we already have for generating forwarders that call noSuchMethod). We would then just have this:

class C_as_I implements C, I, NoSuchMethod {
  final C forwardee;
  C_as_I(this.forwardee);
  _RETURN_TYPE_ _NAME_(_PARAMETERS_) => forwardee._NAME_(_ARGUMENTS_);
  _RETURN_TYPE_ get _NAME_ => forwardee._NAME_;
  void set _NAME_(_PARAMETER_) => forwardee._NAME_ = _ARGUMENT_;
}

The idea is that any class that implements NoSuchMethod would get generated members following the templates. In this case we just forward everything to forwardee, and that would compile to instance member invocations or extension method invocations as appropriate.

With that, an instance myC of C could be wrapped using C_as_I(myC), and the wrapped object could then be passed around and it would support I for both statically checked and dynamic invocations.

The automatic method generation mechanism mentioned here has been spelled out a bit more here: https://github.com/dart-lang/language/issues/631.

very curious if having this available in experimental mode is possible before 2020?

very curious if having this available in experimental mode is possible before 2020?

It's already available, on dart 2.6-dev

when stable channel will hit 2.6 version it will be available in stable channel?
I use some plugins who doesn't like the 2.6-dev ;)

I am having a lot of fun playing around with Dart 2.6.0-dev.8.0. The following example surprised me. Is this expected?

class Base {}

extension Extension on Base {
  static int method() => 42;
}

void main() {
  Extension.method();
  Base.method(); // Error: Method not found: 'Base.method'.
}

Yes, Base.method() is an attempt to call the static method in Extension as if it had been added to the target class Base, and there is no support for doing that. There was some discussion about supporting this, but it didn't get enough traction to actually happen.

Wait, static extensions are not supported? I didn't even see the discussion. 😱

That's very sad. It allows pretty powerful things.
For example, it allows using hide to make a public static method, but not exposed outside of the package.

It's also very convenient with the static of pattern that Flutter uses.
We could have a model with no dependency on Flutter:

// model.dart
class Model {}

And, in the context of Flutter, add a static of method to it:

// another-file.dart
import 'package:flutter/widgets.dart';

extension on Model {
  static Model of(BuildContext context) {...}
}

Only extension static methods allows this.

maybe he should try

class Base {}

extension on Base {
  static int method() => 42;
}

void main() {
  Base.method(); 
}

@renanyoy nice try. but that doesn't work.

@rrousselGit I agree with Remi here. I too needed a static extension method and found this exact problem also.

@rrousselGit why not creating an extension on context instead of a static accessor?

class MyModel {
  final String name = "Hello";
}

extension MyModelExt on Context {
  MyModel get myModel => Provider.of<MyModel>(this);
}

class SomeWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Text(context.myModel.name);
  }
}

Another point is support for generics. When the class you are extending has generics to be able to use those in your extension.

extension IterableExtension on Iterable {
  List<E> toImmutableList<E>() => List.unmodifiable(this);
}

/// outputs:
///  List<dynamic>
///  List<int>
///  List<int>
void main() {
  final ex1 = [1, 2, 3].toImmutableList();
  print(ex1.runtimeType);

  final ex2 = [1, 2, 3].toImmutableList<int>();
  print(ex2.runtimeType);

  final ex3 = List<int>.unmodifiable([1, 2, 3]);
  print(ex3.runtimeType);
}

What I'd like is this.. but doesn't compile.

extension IterableExtension2 on Iterable<T> {
  List<T> toImmutableList() => List<T>.unmodifiable(this);
}

@passsy That could work on small projects. But as the app grows, it's likely that we'll have two classes with the same name.

With an extension static method, then we can use the as/show/hide import directive.

import 'foo.dart' as foo;
import 'bar.dart' as bar;

foo.Model.of(context); // `.of` is a static extension on `Model`
bar.Model.of(context); // same here

Whereas with extension getters/methods we'll have a name clash.

It's also semantically better. That's how Flutter works. We do Theme.of(context), not context.theme.

@slightfoot That's supported already:

extension IterableExtension<T> on Iterable<T> {
  List<T> toImmutableList() => List<T>.unmodifiable(this);
}

@slightfoot That's supported already:

extension IterableExtension<T> on Iterable<T> {
  List<T> toImmutableList() => List<T>.unmodifiable(this);
}

Doh!

I bet the current "default" - using static methods Theme.of(context) - will soon change to context.theme. That's the whole point of extensions: Making object visible via auto-completion rather than knowing about all InheritedWidgets.

Name clashes are possible but you can also use as/show/hide for extensions. That's why they have a name.

// one.dart
extension One on String {
  void hello() => print("Hey");
}

extension Two on String {
  void hello() => print("Hello");
}
import 'one.dart' show One;
import 'one.dart' as two show Two;

void main() {
  "test".hello(); // Hey

  // name clash, fallback to static method
  two.Two("test").hello(); // Hello
}

That's the whole point of extensions: Making object visible via auto-completion

The discussions weren't always about that.

There were a lot of mentions about "extensions allows adding any kind of member to a class", which includes static methods but also factory constructors (see https://github.com/dart-lang/language/issues/41#issuecomment-430158750)

extension on Foo {
  factory foo() { ... }
}

Although it's not supported either for some reason.

Then I am wondering what is the benefit of supporting extension Extension on Base { static int method() => 42; }? This seems equivalent to class Extension { static int method() => 42; } in terms of calling method()?

More concrete: In my case I have various libraries that would benefit from static method/factory extensions. In each case I have abstract base classes with various static/factory-methods that help the users to instantiate sub-classes. I'd like to split up the existing code into multiple libraries as well as allow others to add their own extensions.

I don't think having the library users know the various extension classes is a viable solution. The most attractive workaround would be to introduce a separate factory object that can be extended and accessed from the base class, i.e. Base.factory.method(). However, this introduces some boilerplate, would change existing calling patterns, and looks quite unusual.

this is pretty much how C# works, extension methods are instance methods, not static methods ... I have wanted true static extension methods many times ... but the bigger use case does seem to be instance methods ... maybe next go around on dart

Closing; this launched in preview in Dart 2.6. See the blog post:
https://medium.com/dartlang/dart2native-a76c815e6baf

🎉

Is there an issue tracking the autocomplete/import in the Flutter IntelliJ plugin? Because this is not working at all.

EDIT:
Nvm this is probably a problem with the IntelliJ Dart plugin.

In the future, will it be possible to allow the addition of property with extension?

I have this use case, I currently use a custom list class implementation and I would like to use the extension to not need a custom class from the list just to add a simple property that allows to page results from the back end.

extension TotalRecords on List {
  int _totalRecords = 0;

  int get totalRecords => _totalRecords;

  set totalRecords(int totalRecords) {
    _totalRecords = totalRecords;
  }
}
import 'dart:collection';

class RList<E> extends ListBase<E> {
  final List<E> l = [];

  RList();

  @override
  set length(int newLength) {
    l.length = newLength;
  }

  @override
  int get length => l.length;

  @override
  E operator [](int index) => l[index];

  @override
  void operator []=(int index, E value) {
    l[index] = value;
  }

  int _totalRecords = 0;

  int get totalRecords => _totalRecords;

  set totalRecords(int totalRecords) {
    _totalRecords = totalRecords;
  }
}

I use this implementation in this package https://pub.dev/packages/essential_rest

Static extension methods cannot change the shape of the object, so it cannot add more storage slots.

What you can already do is to use an Expando.

extension TotalRecords on List {
  static final _totalRecords = Expando<int>();
  int get totalRecords => _totalRecords[this];
  set totalRecords(int value) {
    _totalRecords[this] = value;
  }
}

Expandos don't work on strings, numbers, booleans or null, but they should be fine for lists.

You won't be able to use extension methods to avoid the custom class, though. Since List already has an [] operator, you cannot override that with an extension method.

@eernstg , re "Base.method()", what is the rational for not supporting it?

The rationale was that the affordances of the on type selection did not match well with acting like a real static method.

You can write extension on List<int>, but you cannot write List<int>.staticMethod, so it's unclear whether a static extension static method declared in that extension would apply to List.staticMethod, or to nothing.

An extension, as currently defined by the language, is defined on a type. A static method is defined on a class (or mixin) declaration. For generic classes (or mixins), those are very different things. Some types have no corresponding class at all, some do. There was no clear an easy way to extend the static instance member functionality to also add static members to types, and there was no clear and easy way to restrict the declaration to extensions on "classes" because there is no such thing.

Agreeing with @lrhn that a type and a namespace are very different things, I'd just add that a feature for scoped injection of declarations into namespaces (e.g., adding static methods, class variables, factory constructors to a class) should probably be a completely separate construct. It might turn out to be useful with classes, import prefixes, and other targets, and it might involve regular declarations as well as declaration aliases. So it's basically a whole new design process rather than a twist on static extension methods.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

wytesk133 picture wytesk133  ·  4Comments

stategen picture stategen  ·  4Comments

har79 picture har79  ·  5Comments

leonsenft picture leonsenft  ·  4Comments

creativecreatorormaybenot picture creativecreatorormaybenot  ·  3Comments