dart-lang / sdk

The Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.
https://dart.dev
BSD 3-Clause "New" or "Revised" License
10.26k stars 1.58k forks source link

Enhancement request: Monkey Patching #32700

Closed NathanaelA closed 8 months ago

NathanaelA commented 6 years ago

What is the possibility that this could end up in the Dart engine. This is one feature that I find I really miss compared to JavaScript.

If someone industrious could add this feature; would it be accepted; or is it pretty hard to get new language level features added because of being under a language standard?

dgrove commented 6 years ago

(just to set expectations - this is not at all likely to happen. It doesn't fit well with many of Dart's fundamental tenets).

NathanaelA commented 6 years ago

@dgrove - Dan, Somehow I was afraid you were going to say that. :frowning_face:

I have to say out of all the things I do think JavaScript does right, is its ability to monkey patch. It is very easy to add new features to a framework that are impossible to do in the majority of languages.

I'll give you an example; in NativeScript it supports assigning id's & classes to any rendered object. However, because the framework team like to keep the core small it only supported a function getViewById which would find any view item that had that id.

I created a plugin that added a getter for element type and class name. By monkey patching it to the base view; that meant every view descendant now has that ability. So if my app had a reference to any container; I could do container.getViewByClass("someClassName") or getViewByType("Label") -- and it would return me an array of elements that matched from inside that specific container. It is things like that which are easy to add base functionality to a framework and the main core team didn't have to worry about maintaining a feature that maybe only a medium percentage of the user base would use... I have several plugins that I've added to NativeScript that use monkey patching to add features to the framework. Sometimes those features I've written; do get promoted into the core framework; but if they don't it is only a couple lines of code to activate any of my plugins to add it to your app and the code is indistinguishable to code that was written in core.


Btw, which tenets would monkey patching break? I read through all the docs; and didn't see anything... I do have to admit my knowledge of Dart is limited and I'm learning it because of Flutter; but I have to say this make Dart a lot less appealing than JavaScript -- as a plugin developer it is rare that I can't solve a issue in JS because of the monkey patching ability.

zoechi commented 6 years ago

out of all the things I do think JavaScript does right, is its ability to monkey patch

That's easy to say. This feature comes basically for free with a language as dynamic as JS. That JS allows this is also one of the main reasons why programming in JS is such a mess.

NathanaelA commented 6 years ago

@zoechi - I would totally disagree; it is a real strength of JavaScript. ( But just like something can be used for Good, it can just as easily be used for poorly. :grinning: )

Out of the 31 plugins that I have listed on https://plugins.nativescript.rocks/author/Nathanael%20Anderson site; ~10 of them use monkey patching. I think 9 of the 10 would have been impossible to do without monkey patching. They tie into the framework at a low level and add additional features.

This allows the NativeScript team to not worry about having to add the entire kitchen sink to the core NativeScript libraries. They keep the core framework much smaller and easier to maintain. However, if a plugin proved to be very valuable to the eco-system; it is merged into core. So this also helped judge what were "core" features and what weren't. So for example; of my "monkey patched" plugins that have now been added to core (so they aren't needed in NS 3.0) both Hidden, & Accessibility...

However, because Global-Events isn't in core -- If I still need to have global events, I just do a require('nativescript-globalevents') and bam, I can now do page.on("load", handlePageLoadEvent) --- this is where monkey patching is extremely useful. I have even used this global events plugin in several of my other plugins that are highly used in the eco-system. Without monkey patching, there would be several plugins that would be impossible to do properly without getting the NativeScript team to add extra hooks or adding a lot of extra code to the core that maybe only a percentage of users really care about.

At this moment I have a feature I would love to add to Flutter; I do know that at this moment the people who have been using flutter for a long time don't feel like it needs to be added. And eventually I might agree (or they might); but at this moment I do want to see it. If I had monkey patching; I could add it without having to argue about adding hooks I need into the framework. If it proved to be valuable to the eco-system then people might see its value; or I might come to see it was a fools errand; and drop it. But either way; the core team didn't have to do ANYTHING to support me trying it out...

fmorgado commented 6 years ago

Extension Methods would probably do it ... in a much saner way.

mraleph commented 6 years ago

There are multiple problems with monkey patching - but most importantly JS style monkey patching is imperative rather then declarative, which potentially makes AOT compilation to native code harder and has non local implications on code quality (depending on how monkey patching is specified and implemented).

As @fmorgado suggests please check out extension methods and see if it covers all use cases for monkey patching that you had in mind.

NathanaelA commented 6 years ago

@mraleph - Being able to extend classes would solve the vast majority of my use cases! There are a couple cases that it doesn't -- but in all honesty a solution that covers ~95% would be awesome, and I would definitely be a happy camper!

As long as when I extended say BaseView class with my coolMethod() that anything that descends from the BaseView class would also be able to use coolMethod(). Because the idea is that BaseView -> LabelWidget -> SuperDuperCustomLabelWidget I really want SuperDuperCustomLabelWidget to also be able to call coolMethod() just like BaseView can...

orikurt commented 8 months ago

for example,, it would be nice not to need to add this everywhere: ` bool disposed = false;

@override void dispose() { disposed = true; super.dispose(); }

@override void setState(VoidCallback fn) { if (disposed) return; super.setState(fn); }`

mraleph commented 8 months ago

I am just going to close this because we are unlikely to ever add monkey patching.

@orikurt you can use a mixin to reuse this piece of code:

mixin IgnoreSetStateAfterDisposeMixin<T extends StatefulWidget> on State<T> {
  bool disposed = false;

  @override
  void dispose() {
    disposed = true;
    super.dispose();
  }

  @override
  void setState(VoidCallback fn) {
    if (disposed) return;
    super.setState(fn);
  } 
}

class _MyWidgetState extends State<MyWidget> with IgnoreSetStateAfterDisposeMixin {
}

Though I must admit something is likely wrong with lifecycle of your widgets if you need this.

orikurt commented 8 months ago

Thanks @mraleph this is just a result of async operations, so when I have something like

value = await someAsyncStuff();
//user navigates away from the widget
setState()

then this error occurs. If you know of a better way to handle it then the disposed patch I would love to hear :) Thanks mate