Open tuchida opened 10 years ago
I was trying to fix https://github.com/tuchida/JSX/commit/8770306bddedd1d391b6a2ae15d67f7fd15eba45 . I found two problems.
First. This code fails to compile when add --optimize release --minify
options. I reported https://github.com/jsx/JSX/issues/328 .
static function f1(a : number, b : number = ++a) : void {
log a;
log b;
}
Second, It is not expected in the case of closure has side effects. I do not know how to deal with this case.
static function f2(a : number, b : () => number = () => ++a) : void {
log b(); // expect 2
log a; // expect 2 but 1
}
Do you have a good idea?
Thank you for looking into the issue.
IMO we should not allow the modification of preceding arguments within the default argument expression. Or please let me know if you have any reason to support such behaviour.
For example.
class _Main {
static function fn(a : number, b : () => number = () => a) : void {
log b(); // expect 1
a = 10;
log b(); // expect 10 but 1
}
static function main(args : string[]) : void {
_Main.fn(1);
}
}
If not allow the modification of a
, is there compatibility problem?
It might work if the output JavaScript code like this.
class _Main {
static function fn(a : number, b : () => number = () => a) : void {
log b(); // expect 1
a = 10;
log b(); // expect 10 but 1
}
static function main(args : string[]) : void {
_Main.fn(1);
_Main.fn(10, () => 20);
}
}
var NO_PARAM = {};
function _Main$fn$N(a, b) {
if (b === NO_PARAM) {
b = function () {
return a;
};
}
console.log(b());
a = 10;
console.log(b());
}
function _Main$main(args) {
_Main$fn$N(1, NO_PARAM);
_Main$fn$N(10, function() {
return 20;
});
};
Can decreased number of function calls. But many changes you will probably need.
If not allow the modification of a, is there compatibility problem?
The answer is no, practically, since the feature has only been introduced lately.
It might work if the output JavaScript code like this. (snip) Can decreased number of function calls. But many changes you will probably need.
Such design was considered when we introduced the default arguments, but had been dismissed for two reasons: that the model is complicated, and that it would be slow. Current design will run fast together with the inline optimizer, since the default argument expressions can be inline-expanded in the caller side by the optimizer.
Thanks!
IMO we should not allow the modification of preceding arguments within the default argument expression.
How do you like https://github.com/tuchida/JSX/commit/bbaabac8df01b921d0dbc6e6c972d2e92df8d6ac ?
Thank you for looking into the issue.
What I mean is that modification of preceding arguments should disallowed only within the default argument expressions, and it should continue to be allowed at other locations. i.e.
function fn(a : number, b : number = 10) : void {
a = 11;
}
should be considered valid, OTOH
function fn(a : number, b : number = ++a) : void {
}
should be invalid.
This case.
static function fn(a : number, b : () => number = () => a) : void {
log b(); // expect 1
a = 10;
log b(); // expect 10 but 1
}
static function main(args : string[]) : void {
_Main.fn(1);
}
a
is changed outside the default argument expressions.
Should b()
always returns 1 even after the change?
Or should disallowed modification of 'a', because it is referenced in the default argument expressions?
Thank you for the clarification.
a is changed outside the default argument expressions. Should b() always returns 1 even after the change?
IMO b()
should return 1 even after a
is being changed, since it is more consistent to consider the default argument expressions as something that is expanded at the caller-side rather than the opposite.
Below is a valid example using default argument expression that prints Derived:123
. Such behaviour would seem natural if the expression is considered to be expanded at the caller side (since the intended call signature would be Base#f(:number)
), but would seem unnatural if it were to be considered as expanded at the callee-side (i.e. method with no arguments is only defined in the base class, and thus the output should be Base:123
in such case).
class Base {
function f(n : number = 123) : void {
log "Base:" + n;
}
}
class Derived extends Base {
override function f(n : number) : void {
log "Derived:" + n;
}
}
class _Main {
static function main(args : string[]) : void {
(new Derived).f(); // output: Derived:123
}
}
Having said that, it might not be necessary to disallow the modification of preceding arguments within default argument expressions. Just documenting the behavior might be sufficient.
The very interesting case of override method. I do not know which is the best.
I do not know which is the best.
The override example illustrates the feature of the compiler that is already being used. That is why I believe it is better to consider the default argument expressions as something that is expanded at the caller-side which in turn means that the return value of b()
should not be affected by the modification of a
, unless there is a strong reason not to do so.
I run this JSX's code.
I expected to output
2
, but1
. (Sorry. I missed https://github.com/jsx/JSX/pull/326)This is compiled JavaScript code.
I think it should be this.