dart-lang / dart-lang/language

Keyword to easily forward function calls proxy/delegate, mirrors intermediate

Open
#418 15 comments 3 reactions 0 assignees View on GitHub
request
Dominant language
TeX
Stars
2.9k
Forks
239
Avg merge
2d 18h
Merged PRs (30d)
14

Description

I want to be able to easily delegate an object's function call to another object, like a proxy. A way I think this could be nicely implemented and safe for static analysis, is the one described here.

I believe that this functionality would provide a solution to an important part of the users that for are [requesting mirrors for flutter](https://github.com/flutter/flutter/issues/1150). Many people want mirrors just to proxy function calls. It would also provide a much more general solution to a previous request about object wrappers/proxies dart-lang/sdk#414.

The idea is to introduce a keyword 'delegate' that repeats the original call made to a class method to another object. Some positive things about introducing this would be:

- Syntactic sugar for some common patterns, like calling super.methodName(args...)
- Introduces a subset of mirrors functionality, but static type safe
- Easy creation of proxy/wrapper classes

Details are clarified in the examples below. What do you think?

(is it more clear write simpler example with class A, B, bar, foo, etc?)

```
class House {
bool setEncompassingLimits(northLimit, soutLimit, westLimit, eastLimit) { /* ... */ }
// ...
}

class TreeHouse extends House {
@override
bool setEncompassingLimits(northLimit, southLimit, westLimit, eastLimit) {
// ...
delegate super; // equivalent to super.setEncompassingLimitss(args...) with original function call args
// ...
}

cleanTreeBranches(tool) {}
}

class HouseManager {
House house;

bool setEncompassingLimits(args) => delegate house;
//....
}

TreeHouse treeHouse;
HouseManager houseManager;

treeHouse.setEncompassingLimits(args...); // will call super.setEncompassingLimits(args...)

houseManager.setEncompassingLimits(args...); // will call houseManager.house.setEncompassingLimits(args...)
```

Essentially 'delegate' means, repeat the exact same call that was done in the current object on another object (or super). The object must therefore implement the exact same function otherwise it is a an undefined_method error. This is syntactic sugar for a common pattern that is calling super.methodName(args...), but is more versatile than that by allowing to repeat the call to another object, which is also used sometimes, specially in "proxy" classes.

### Special Case noSuchMethod

A special case is calling 'delegate' on noSuchMethod. When delegate is called inside a noSuchMethod function declaration, the call of the function to super or another object should be of the original function that was called and not to noSuchMethod. For example:

```
class HouseHolder implements TreeHouse {
House house;
TreeHouse treeHouse;

noSuchMethod(Invocation invocation) => {
delegate house;
delegate treeHouse;
};
// error in this class definition, HouseHolder must implement cleanTreeBranches as explained below
}

HouseHolder houseHolder;

houseHolder.setEncompassingLimits(args...) // allowed, makes same function call in house and treeHouse

houseHolder.cleanTreeBranches(args...) // static type check error, house does not implement cleanTreeBranches
```

If a method is on the interface of the class but is not implemented, the static type checks should assume a call of methodName(args...) to each object where the call is being delegated inside noSuchMethod. In other words, the implemented interface reached by noSuchMethod is equal to the intersection of the interfaces of the delegated class instances, in the above example it would be House and TreeHouse interfaces intersection.

### Small optimizations

It could also be allowed to skip the function body and do something like this:
methodName(args...): delegate another_object;

Which the compiler can immediately interpret as a call another_object.methodName(args...), without passing through the method that delegates. Like this:

```
class HouseProxy implements TreeHouse {
House house;
TreeHouse treeHouse;

cleanTreeBranches(tool): delegate treeHouse; // compiler send call to this method directly to treeHouse
noSuchMethod(Invocation invocation): delegate house; // the compiler forwards any call of the House interface directly to house
}

HouseProxy proxy;

proxy.cleanTreeBranches(tool) // compiler understands this is equivalent to proxy.treeHouse.cleanTreeBranches(args...)

proxy.setEncompassingLimis(args...) // compiler understands this is equivalent to proxy.house.setEncompaasingLimits(args...)
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.