Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scoped object extensions #9991

Closed
jmesserly opened this issue Apr 18, 2013 · 31 comments
Closed

Scoped object extensions #9991

jmesserly opened this issue Apr 18, 2013 · 31 comments
Labels
area-language Dart language related items (some items might be better tracked at github.com/dart-lang/language). closed-duplicate Closed in favor of an existing report core-l customer-google3 type-enhancement A request for a change that isn't a bug

Comments

@jmesserly
Copy link

In Dart right now, it's painfully difficult to polyfill new features for the HTML nodes without making changes to dart:html itself.

Here's what they do in JavaScript:
https://github.com/toolkitchen/mdv/blob/stable/src/template_element.js

If you include this code into your application, it seamlessly extends a bunch of DOM types, giving you access to new features. It can do this without requiring you to download a new browser or rebuild Chrome yourself.

I don't see how we can do this in Dart. You can't add an instance method to dart:html types without changing the code of dart:html itself. This shuts down a valuable source of prototyping and feedback for the DOM APIs.

JavaScript has a proposal for "scoped object extensions" that is friendly to dynamic typing:
http://wiki.ecmascript.org/doku.php?id=strawman:scoped_object_extensions

The problem for JS is they don't have much urgency. They get by okay with monkeypatching existing types (see my example above). In Dart we don't have monkeypatching, so we need something else.

Note: IMO, this should not be merged into issue #13, because that bug was closed because it asked for statically typed extension methods. Here I want to discuss the above proposal that relies on dynamic typing. At least, if this one is to be WontFix'd, it should be done for a different reason :)

@jmesserly
Copy link
Author

BTW, the reason I'm making this request now, is I'm feeling backed into a corner with HTML5 polyfills. The only option I have is to hack on dart:html itself and add @­Experimental APIs directly into it. But IMO this is not a viable long term strategy for web platform features. They need time to bake in their own libraries without being forced to ship in Dartium/Chromium.

@justinfagnani
Copy link
Contributor

Similar to John's dilema, I've been recently asked how you could access properties on an HTML element that were added in JS. This could be the polyfills that John mentions, or application or framework code. Right now the only option seems to be to get a hold of a JS interop Proxy to an element (how might be tricky if elements are automatically converted) and then call methods on that. Extension methods, obviously, could be written to use JS interop but appear to add the method directly to the element.

@gbracha
Copy link
Contributor

gbracha commented Apr 22, 2013

Needs study.


Set owner to @gbracha.
Added Accepted label.

@dgrove
Copy link
Contributor

dgrove commented Apr 29, 2013

Added C1 label.

@mraleph
Copy link
Member

mraleph commented Apr 30, 2013

fyi scoped objects extensions harmony proposal is dead.

@munificent
Copy link
Member

That's true but I believe that's mostly because the author stopped doing JS-related work and not as much to do with the proposal's merits.

@mraleph
Copy link
Member

mraleph commented Apr 30, 2013

Scoped Objects Extensions proposal mixes static properties (lexical scope) and dynamic properties (lookup) together which leads to immense implementation complexity. Especially in JavaScript where everything is extremely dynamic and library functions are specified in terms of [[Get]] and [[Put]] that are affected by this in obscure ways.

I also think that resulting behavior might be extremely confusing.

I honestly think we should not consider adding anything like that to the language.

@DartBot
Copy link

DartBot commented Apr 30, 2013

This comment was originally written by Jim.J....@gmail.com


Isn't this request roughly the equivalent of adding a method to an javascript object's prototype at runtime? I believe the resulting functionality is equivalent to C#'s extension methods as well. Given that these are both languages that Dart developers are coming from, I don't see the feature itself as being potentially confusing. Unless there's something I'm missing here...if so, please let me know.

I also agree that it provides much needed support for poly filling new browser features, as well as prototyping changes to libraries without affecting the library's entire user base.

@jmesserly
Copy link
Author

fyi -- for my purposes I don't care if it's scoped or not. Rather the ability to extend an existing type. In essence, parity with JS monkeypatching.

@jmesserly
Copy link
Author

@vegorov if it is purely dynamic, does that address your concern?

look, we can't reject this one hand because it's too static and on another because it's too dynamic. That's just admitting we're stuck in a worst-of-both worlds. We should strive for best-of-both-worlds :)

I'm by no means attached to the details of the solution; rather I'm pointing out this is a problem we must solve if we want Dart to be able to make draft web standards available like JavaScript does.

Gilad had an interesting idea that requires no language changes: put noSuchMethod on Node, and have a means of delegation. It might work, assuming we can address the dart2js challenges.

@munificent
Copy link
Member

Gilad had an interesting idea that requires no language changes: put noSuchMethod on Node, and have a means of delegation.

Why not on Object?

@gbracha
Copy link
Contributor

gbracha commented May 21, 2013

John,

What you describe is a very different beast. That goes under the rubric of mirror builders and such.

This bug is about a rather ill defined language feature that is a half dynamic cousin of things like C# extension methods, Haskell type classes and others. The literature is littered with such things: Class Boxes, selector namespaces and other variants.

@DartBot
Copy link

DartBot commented May 27, 2013

This comment was originally written by ladicek@gmail.com


I'd like to add that there are mainstream-ish languages doing very similar things: refinements in Ruby 2.0 or augmentations in Golo.

@DartBot
Copy link

DartBot commented May 27, 2013

This comment was originally written by @MarkBennett


From a language user perspective, I appreciate the convenience and safety that scoped object extensions add, however after hearing thoughts like these from Charles Nutter of jRuby fame:

http://blog.headius.com/2012/11/refining-ruby.html

I'm not sure they're worth the complexity it would add to the language unless the rules for handling name collisions were very clear and could all be resolved at compile time.

Would a solution implemented with noSuchMethod make compile time checks possible?

@munificent
Copy link
Member

I'm not a Rubyist, so I don't have a solid grasp of Nutter's post, but I believe any scoped-monkey-patch-like proposal for Dart would not suffer from the same problems that refinements have. In particular, he says refinements are available in:

  1. The direct scope, such as the top-level of a script, the body of a class, or the body of a method or block
  2. Classes down-hierarchy from a refined class or module body
  3. Bodies of code run via eval forms that change the "self" of the code, such as module_eval

Dart doesn't have any eval forms, much less ones that rebind self, so (3) isn't a problem. I don't think any proposal we'd want for Dart would support (2) either. That gets you back to simpler (i.e. faster) dispatch mechanics.

@DartBot
Copy link

DartBot commented May 28, 2013

This comment was originally written by @MarkBennett


Those make sense to me @­rnystrom. I honestly don't have any experience with language implementations or VMs so most of my understanding of this issue comes from what I know of Ruby.

It would be great to be able to use something like this, especially if it didn't add the VM complexity seen in Ruby. Really happy with how you guys have been trying to integrate as many practical refinements to the language without making the VM or spec to complex. It's a hard balance but you seem to be doing a good job.

@DartBot
Copy link

DartBot commented May 28, 2013

This comment was originally written by ladicek@gmail.com


Yeah, I think that extensions should only be scoped lexically. Heck, I wouldn't even mind to have a special operator for accessing extensions, provided that it's as easy to write and read as a dot. Like colon, for example.

@DartBot
Copy link

DartBot commented Jun 25, 2013

This comment was originally written by gir...@gmail.com


Doesnt the problem get solved by allowing inheritance to libraries. For example you
can have a mdv_html library which inherits code from html but overrides some methods and adds a few more

// mdv_html.dart
library mdv_html extends dart:html;

// Override function,class,variable definitions

// mdv user file
import "package:mdv_html"

@DartBot
Copy link

DartBot commented Jan 23, 2014

This comment was originally written by @MarkBennett


Not to poke this issue again, but I wanted to share the way Rust is specifying traits, as it's my understanding their successfully able to resolve all method lookups at compile time. Here's the best description of them I was able to find.

http://pcwalton.github.io/blog/2012/08/08/a-gentle-introduction-to-traits-in-rust/

As a practical example @­wycats is able to expose an implementation of additional methods on an integer using this code:

https://github.com/wycats/rust-activesupport/blob/master/dsl.rs

You can then call methods like 1.days().ago(). Perhaps the Rust convention of passing self as the first parameter to these functions helps resolve some of the ambiguity in the implementation, and I understand this isn't the Dart norm when defining instance methods on a class or mixin, but could a convention like this be adopted in the case of traits?

I only continue to raise this issue as a feature like this would be very useful when prototyping new features that may or may not be accepted into the Dart standard libraries.

Thanks again for all the hard work and continuing to have these discussions about the language. Providing a smooth path for prototyping and deprecating language and library features seems like the only element still missing from the Dart ecosystem.

@jmesserly jmesserly added Type-Enhancement area-language Dart language related items (some items might be better tracked at github.com/dart-lang/language). labels Jan 23, 2014
@kevmoo kevmoo added P2 A bug or feature request we're likely to work on type-enhancement A request for a change that isn't a bug and removed accepted labels Feb 29, 2016
@munificent munificent changed the title scoped object extensions Scoped object extensions Dec 16, 2016
@mikeaustin
Copy link

mikeaustin commented Jun 5, 2017

I realize this is an old topic, but the same thing has been debated in TypeScript with no solution.
In my [early stage] JavaScript dialect, I implemented extension methods via external methods:

($ = "foo", $.capitalize || _.capitalize).apply($, []);

_.capitalize is a registered function for the 'this' type, so behaves just like a regular method. More details here: https://www.npmjs.com/package/impulse-js

I'm not saying Dart could/should do it that way, but that's how I tackled the problem. It's very straightforward, and as an added bonus, I added partial application if 'this' is undefined:

let evens = _.filter(_.even());
evens([1, 2, 3]) // [1, 3]

@munificent I miss Magpie :)

@munificent
Copy link
Member

@munificent I miss Magpie :)

Sometimes I do too!

@yjbanov
Copy link

yjbanov commented Dec 6, 2017

Should the issue description be reworded to take into account the recent developments in the type system as well as include Flutter use-cases? I'd like to have something like this in Flutter too.

For example, we have class CommonFinders, which could be made vastly more useful if users could extend it to add domain-specific finders:

// package:flutter_test
class CommonFinders {
  // ... standard finders ...
}
const CommonFinders find = const CommonFinders._();

// package:flutter_emoji
extend CommonFinders {
  Finder emotion(Emotion emotion) => new _EmojiFinder(emotion);
}

// package:chatapp
import 'package:flutter_test';
import 'package:flutter_emoji';

testWidgets('displays happy face', (tester) async {
  tester.pumpWidget(new ChatApp());
  // Use standard finder
  expect(find.text('Chat App'), findsOneWidget);
  // Use domain-specific one
  expect(find.emotion(Emotion.happy), findsOneWidget);
});

Another example is Flutter's .of pattern, which is used all over the place. It would be great if classes that publish themselves through the BuildContext were discoverable on the BuildContext object itself. This would provide much better IDE code completion feedback than the static method approach used today. Example:

// Today:
build(BuildContext context) {
  // Hmm, what can I access via "context"? /goes to docs.flutter.io
  // 10 minutes later:
  final l10n = MaterialLocalizations.of(context);
  final theme = Theme.of(context);
}

// With extensions:
build(BuildContext context) {
  // Type "context." + CTRL + Space.
  // Gets "localizations", "theme" and other suggestions.
  final l10n = context.localizations;
  final theme = context.theme;
}

To find real-world examples search for ".of(" and "find." in Flutter apps and tests.

@touseefbsb
Copy link

is there any hope on this feature at all?

@munificent
Copy link
Member

There is always hope!

Now that Dart has a strong, sound static type system, we have the option of doing statically dispatched extension methods similar to C#, Kotlin, et al. That makes this more feasible than it was before. (The JavaScript "scoped object extensions" proposal never went anywhere, and doing "extension methods" using dynamic dispatch is mostly an unproven research topic at this point. Doing them statically is well-understood.)

@touseefbsb
Copy link

yeah I am also in favor of statically like in kotlin and c#

@HerrNiklasRaab
Copy link

Would be great to have! :)

@marcguilera
Copy link

Is there any hope for this to happen? It would be great for Dart as it is one of my favorite things from Kotlin / C#. It would be a great addition for libs such as RxDart too.

@yjbanov
Copy link

yjbanov commented Nov 13, 2018

Most of the discussion is happening in the language repo:

dart-lang/language#40
dart-lang/language#41
dart-lang/language#42

@lrhn lrhn added core-l and removed P2 A bug or feature request we're likely to work on labels Dec 6, 2018
@lrhn lrhn added this to Non-Breaking and Complex in Language Enhancement Categories Dec 14, 2018
@jmesserly
Copy link
Author

I believe we could close this in light of the current language work?

Or perhaps I should update the title/description--the goal here was some sort of extension methods; I'm quite happy with the approaches being discussed in the language repository. (Static dispatch seems like the best approach now that we have Dart 2.)

@eernstg
Copy link
Member

eernstg commented Mar 14, 2019

@jmesserly wrote:

.. Static dispatch ..

Note that dart-lang/language#177 is a proposal which is designed to be compatible with scoped static extension methods (dart-lang/language#41), such that we can use instance method syntax (receiver.method(args)) to invoke both kinds of behavior: For a static extension method the implementation is selected statically (like a 'non-virtual instance method'), and for the extension methods of dart-lang/language#177, the implementation is selected based on the dynamic type of the receiver (like a regular instance method).

Static extension methods are statically safe in exactly the same way as a global function. The extension methods of dart-lang/language#177 are statically safe in exactly the same way and for exactly the same reasons as a normal instance method. So we don't have to settle for static dispatch only.

@jmesserly jmesserly added the closed-duplicate Closed in favor of an existing report label Mar 14, 2019
@jmesserly
Copy link
Author

Oh yeah, I saw dart-lang/language#177 and it's really exciting! Closing this one, since we've got other issues tracking the new work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-language Dart language related items (some items might be better tracked at github.com/dart-lang/language). closed-duplicate Closed in favor of an existing report core-l customer-google3 type-enhancement A request for a change that isn't a bug
Projects
Language Enhancement Categories
Non-Breaking and Complex
Development

No branches or pull requests