Open eernstg opened 5 years ago
The forward
name is not a built-in identifier, so making it one will be a breaking change, and it's necessary to avoid ambiguity in parsing. I personally think I would prefer some other notation than a keyword for this.
Apart from that, I like the idea of forwarding functions. Forwarding a function's parameters already happens in exactly one place: redirecting factory constructors. We use the syntax ConstructorName(full parameters) = Constructor.designator
. It makes sense to use =
for other ways of forwarding formal parameters. It should work the same wr. types and parameter signatures, for consistency and because it's a good choice.
If you specify a formal parameter list, then all actual instances of that argument list should be valid arguments to the forwardee as well. For optimization reasons, it should be possible to keep the arguments on the stack when calling through a forwarder. It might need to add extra type checking, but in most cases, it should just be an indirection to the real function.
So, if you write
forward g(String) = complexExpression2; // No need for a name unless you want one.
then the static type of complexExpression2
must be a subtype of Object Function(String)
.
(And no statically detectable cyclic forwarding, obviously).
Whether a function declaration implements its own body or forwards to another function seems like an implementation detail, like the async
modifier, so maybe the marker could be put after the arguments part, maybe something like g(String y) forward = complexExpression2;
. On the other hand, then the marker isn't really needed any more, since the =
is unambiguous, and we don't get the advantage of knowing up-front that this is a forwarding method when parsing the signature (so it would preclude f forward = ...
without no formal parameters).
If we don't do top-level type inference from the bodies of arrow functions, then it seems unlikely that we can do parameter inference for forwarding functions. So, omitting the formal parameters of forwarding functions might not be possible at all (or be as hard as inferring the type of top-level or class members) since it requires finding the static type of an expression. We might be able to do it in cases where the RHS is a static reference to a method or static function.
In response to #57, we might be able to use a keyword like
forward
to specify that a given declaration of a function contains abbreviated or omitted formal parameter lists, and it is not doing anything else than passing its arguments on without changes to some other function.This would be applicable to library functions, static methods, instance methods, and local functions, including getters and setters and operators, and it would also be applicable to constructors.
For constructors, however, we will often have a situation where initializing formals can be used to accept some additional arguments, and hence we'd want to specify a partial formal parameter list, and then use something similar to the forwarding abbreviation for all the remaining arguments, which will then be declared and forwarded implicitly.
For constructors, it is easy to define the semantics of adding a named parameter or a positional parameter to a given statically known formal parameter list (and raise an error if we try to add a named parameter to a formal parameter list that already has an optional positional parameter, or vice versa, or if we try to add a required parameter to a formal parameter list that already has one or more optional parameters).
The semantics would in all cases be that the new formal parameters are added at the end of the existing formal parameter list, and that may succeed or it may be an error.
Partial formal parameter lists are more tricky in the case where we are forwarding to an instance method, or the forwarding declaration has a target which is a first class function object. First, we have no notion like 'initializing formals', so it is likely to be a useless feature to be able to add more parameters (they will always be ignored anyway). But it might be useful in order to create wrappers for functions that manipulate their type a little bit (so we must deliver a
Function({bool b})
, and we have a functionf
of typeFunction()
, but then we just declare a local forwarderforward g({bool b}) = f;
).This seems to imply that the partial formal parameter lists are rather easy to handle in the static case, and they are not very useful in the case where function type subtyping may occur. So we should probably just have then for the static case, and most likely only for constructors.