Open MaxmaxmaximusGitHub opened 4 years ago
This'd be functionally the same as existing class decorators, yeah?
@NullVoxPopuli yes, decorators should be able to decorate anything by analyzing Descriptor, and should be able to return a new value by wrapping the original, as is customary in decorators in functional programming languages. In this way, higher-order functions will be created using decorators.
I believe the previous discussion around this was that it was out of scope for this proposal, because they might have decently different semantics depending on what the proposal ends up looking like, and it would be too much to do at once. The idea was to followup with additional proposals that add things like function decorators, parameter decorators, etc.
It's worth noting that the "before/after export
" debate would probably have an impact here though as well. I think for instance, with the "after" syntax, the example you have at the end with jquery would end up being:
jQuery.prototype.css = @normalizeArgs function (cssProps){
}
I agree with @pzuraq here. We're going to have to limit the scope of this proposal, initially, in order to be able to make progress and make a standard. I would like to be able to decorate all JavaScript entities! But this proposal focuses on classes, where there is a clearly demonstrated demand. Function decorators face unresolved conflicts for semantics around hoisting.
Function decorators face unresolved conflicts for semantics around hoisting.
everything is simple, if a decorator is applied to the function declaration
, it becomesfunction expression
=) since the use of the decorator is not a declaration, is action (expression)
@decorator(2 + 2) // decorator can use expression 2 + 2, cuz decorator is expression
function lol(){ }
is:
var lol = decorator({kind: 'function', options: 2 + 2})( function lol(){} )
function lol (@int age){
}
is:
function lol(age){
age = int({kind: 'argument'})( age )
}
Those semantics come into conflict with some expectations that have been articulated:
I don't think it's impossible to overcome these issues, but it involves complicated tradeoffs. For that reason, this proposal is scoped to decorators on classes and elements of classes.
@littledan
Old code does not use decorators, this will cause a syntax error.
The new code, in which decorators are used, expresses an explicit intention, and the programmer realizes that the function does not rise. When using decorators, Function Declaration becomes Function Expression.
Thus, backward compatibility does not break.
AssemblyScript is TypeScript that compiles to WebAssembly, but it has some incompatible additional features like decorators for functions (I haven't checked how hoisting works, also don't use AS function decorators if you intend to also compile to JavaScript with TypeScript compiler).
It'd would be neat to support this.
(acknowledging this is discussion for a followup proposal and out if scope for this one)
If you could decorate variable declarations, then you could decorate functions indirectly, without affecting hoisting (I think).
E.g.
@myDecorator
let myFun = (x, y) => {
// ....
}
(I avoided const
for simplicity; much easier to reassign to myFun
than figure out name mangling and other things I guess)
I really like the idea of argument decorators, that opens up new opportunities for design by contract and static type checking.
@returns(z => Number.isFinite(z))
let myFunc = (@isNum x, @isNum y) => {
return x + y
}
I think decorating more kinds of syntactic elements is an exciting idea, and I really do support this investigation. I've put a writeup in https://github.com/tc39/proposal-decorators/blob/master/EXTENSIONS.md. I'd be happy to get PRs to add more ideas.
// this is decorator of function (kind === "function")
Object.prop = @decorator function () {}
// this is decorator of object accessor (kind === "accessor")
// but we can also decorate the assigned value of the function here, right during the assignment.
@decorator
Object.prop = function () {}
Moreover, we can write universal decorators that behave differently depending on kind. =)
I'd like to bring it up again. Especially interesting are top level function decorators and block level decorators. The main motivation for top-level functions is the stack-based composition.
For example, these two examples not only look different, but also have different semantics, different hoisting, different readability:
const add = type.lhs("a", "int")(type.rhs("b", "int")(function (a, b) {
return a + b;
});
and
@type.lhs("a", "int")
@type.rhs("b", "int")
function add(a, b) {
return a + b;
}
I'd like to bring it up again. Especially interesting are top level function decorators and block level decorators. The main motivation for top-level functions is the stack-based composition.
For example, these two examples not only look different, but also have different semantics, different hoisting, different readability:
const add = type.lhs("a", "int")(type.rhs("b", "int")(function (a, b) { return a + b; });
and
@type.lhs("a", "int") @type.rhs("b", "int") function add(a, b) { return a + b; }
what is the question? executing decorators is an "expression" and it's "executing", you mean that should the subsequent decorator get the bare result, or should it somehow be able to sense that it's getting the result of another decorator, and have access to meta information? What's the question? what is the dilemma =)?
It's not a question, it's more of an emphasis of the problem. In this case, introducing top-level function decorators such as @foo function boo() {}
will cause such a function to become expression based and will break hoisting. I'm interested in how this could be solved and preserve hoisting ability
@MaxGraey no way. applying a decorator is an action. which means it happens over the essence. in this case over a function. the very mechanism of declaring functions is a vestige and birth trauma from the creator of javascript. we should not be sensitive about the ability of functions to rise. We, for the sake of good, should neglect this feature. Therefore, applying a decorator is an ACTION, imperative, line by line. And what is under the influence of the decorator is not subject to the function bubbling mechanics. I think so. There is no point in trying to keep the harmful lifting mechanic.
ReactJS usage example:
React functional components:
React HOC:
decorators must be able wrap value and return new value, if they wish it. as well as decorators should be applied to any values, including variables. and this should be reflected in the Descriptor kind
we should be able to write like this:
however, in the case of variables, I'm not sure, since this is probably a big blow to performance, because we will have to use the decorator every time we try to read or write the variable. but nevertheless, it’s definitely worth decorating the functions and function args.
and we can create watchable variables, like getters and setters
or like this
and the decorator will round the number
or like this:
or like this:
or like this:
or like this:
a many of examples for using decorators, and not just for decorating classes.