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

Support understands() method/construct on objects #12606

Closed
gbracha opened this issue Aug 21, 2013 · 10 comments
Closed

Support understands() method/construct on objects #12606

gbracha opened this issue Aug 21, 2013 · 10 comments
Assignees
Labels
area-language Dart language related items (some items might be better tracked at github.com/dart-lang/language). closed-not-planned Closed as we don't intend to take action on the reported issue type-enhancement A request for a change that isn't a bug

Comments

@gbracha
Copy link
Contributor

gbracha commented Aug 21, 2013

A mechanism for detecting if an object understands a given method/getter/setter is needed

@gbracha
Copy link
Contributor Author

gbracha commented Aug 21, 2013

cc @justinfagnani.
cc @cbracken.
cc @larsbak.
cc @kasperl.
cc @iposva-google.
Added C1 label.

@justinfagnani
Copy link
Contributor

Note that for this to work with objects that implement noSuchMethod, the object itself needs to be able to answer the "understands" query, when it can't be determined otherwise.

@gbracha
Copy link
Contributor Author

gbracha commented Aug 21, 2013

The design space here includes several variants.

If you assume that this is a base level operation (like "is" and "as") then it you can argue whether there should be a dedicated syntax ("has"?). Next you can debate whether the behavior is customizable by the user as comment 2 suggests. The advantage of making it customizable is that it works well with proxies and upholds the principle that its the actual behavior that matters. This does however have several disadvantages as well:

a. It is inconsistent with the behavior of "is". This is however more of an argument for changing "is".
b. It entails a significant performance cost. That is why "is" works the way it does.
c. It allows for lies. It may be that an object claims to understand a member name but doesn't - even via noSuchMethod. It can also claim not to understand a name but it actually does.

It may be possible to define things in a way that addresses some of these issues. For example, understands first does a phny lookup without calling the member in question. If that fails, it goes to a user defined method "alsoUnderstands" if one exists. That way, only proxies pay a penalty (and at least the 2nd scenario of lying is eliminated).

One can implement this behavior in user space today using mirrors. The mirror system will check if the object truly understands something (i.e., has a declaration) and if it says no, you can call user code of your own. The main weakness is that the user method is not standardized, so it will only work for your own code.

Another tack is to say this really is a meta-level operation and you should just use mirrors. In this case, lying is not an option.

Along another dimension, one may have to distinguish between understands and understandsGetter.

@justinfagnani
Copy link
Contributor

One of the reasons for adding understands is to capture the intent of an operation that would otherwise be done via mirrors so that it costs less. With mirrors under dart2js it's hard, if not impossible, to tell that a certain pattern of access a mirrors is only to see whether the object supports a specific method call, thus the full mirror capability needs to be included, which is expensive. understands() might very well be a less expensive option.

The argument that this belongs in mirrors is pretty strong, IMO. One way around this, while keeping the functionality in mirrors is to add understands to InstanceMirror with the hope that only calling reflect(o).understands(...) leaves some room for optimization in dart2js.

Something like "alsoUnderstands" could definitely be user space. If the problem is standardization, we can try to encourage all proxy-like objects to use an common interface in something like package:proxy. Frameworks that need to check for understandability can just do:

if (reflect(o).understands('m') || o is Proxy && o.understands('m')) {
  o.m();
}

@lrhn
Copy link
Member

lrhn commented Aug 22, 2013

This seems like a move towards structural typing.

If the problem being solved is that proxies cannot handle "is" checks dynamically, then I think we should fix that instead.
  operator is(Type type) => Type == TheTypeImProxying || Type == Object || Type == dynamic;
I'd probably want a subtype relation on the Type object as well for implementing this, but that's a different problem.

The problem with "understands" is that it's not obvious what it means. Even if an object understands a message, say a "length" getter, it doesn't mean that it understands it the way it was meant. If I give you my own Event object, and it understands "length" ... but it returns the Duration of the event - did it really understand the message then?

Nominative typing works because it not only matches the signature, but also the intent of the signature, and I think we should stay with that if at all possible.

@DartBot
Copy link

DartBot commented Dec 19, 2013

This comment was originally written by Misko.He...@gmail.com


I would like to increase the priority of this enhancement.

AngularDart needs to support both v1.0.0 and v1.1.0 of dart. But there few minor changes between the two dart VMs. Our prefered way of dealing with this is for angular to auto-detect the API presence and then chose the right one.

Example

v1.0.0: reflect(object).type.members
v1.1.0: reflect(object).type.instanceMembers

This can easily be solved with a simple if provided that we can easily test for method presence.

var type = reflect(object).type;
var members = understandands(type, #members) ? type.members : type.instanceMembers

Currently we have to detect this through a try catch.

@iposva-google
Copy link
Contributor

Re comment #­6:
Since we cannot retroactively add understands() to 1.0 you are still in the same position even if understands() is added later. The same way you have to determine now whether you should be calling members vs. instanceMembers, you would have to determine whether understands is available.

I added Ryan here, as he has some ideas on you can use the 1.0 API to get what you need.


cc @rmacnak-google.

@gbracha gbracha added Type-Enhancement area-language Dart language related items (some items might be better tracked at github.com/dart-lang/language). labels Dec 19, 2013
@gbracha gbracha self-assigned this Dec 19, 2013
@kevmoo kevmoo added type-enhancement A request for a change that isn't a bug and removed priority-unassigned labels Feb 29, 2016
@munificent
Copy link
Member

Given all of the pain that noSuchMethod() has caused for our ahead-of-time static compilers, we're pretty disinclined to put more metaprogramming APIs right on Object itself. :)

Adding members to Object is also a big breaking change and prevents user-defined classes from using that name. We'd rather these kinds of APIs be separated out into a separate distinct meta layer—i.e. mirrors.

@munificent munificent added the closed-not-planned Closed as we don't intend to take action on the reported issue label Dec 16, 2016
@rmacnak-google
Copy link
Contributor

noSuchMethod() does not cause any pain for the VM's ahead-of-time compiler.

@munificent
Copy link
Member

noSuchMethod() does not cause any pain for the VM's ahead-of-time compiler.

If the VM was our only compiler, lots of things would be easier. :)

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-not-planned Closed as we don't intend to take action on the reported issue type-enhancement A request for a change that isn't a bug
Projects
None yet
Development

No branches or pull requests

8 participants