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

[Feature Request] allow decorate for ALL javascript entities #306

Open
MaxmaxmaximusGitHub opened this issue Apr 26, 2020 · 15 comments
Open

Comments

@MaxmaxmaximusGitHub
Copy link

MaxmaxmaximusGitHub commented Apr 26, 2020

ReactJS usage example:

React functional components:

import {memo, forwardRef} from 'react'

@memo
@forwardRef
export default function Button() {
  return <button>my button</button>
}

React HOC:

import {withRouter} from 'router'

@withRouter
export default function Header({ router }) {
  return <header>my header {router}</button>
}

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:

var q = 200
var w = 300
var e = q + w 

@int var q = 200
@int var w = 300
@int var e = q + w

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

function watch(descriptor) {
   //  descriptor.king == "variable"
  return {get(){ }, set(){ }}
}


@watch
var q = 200

q = 300 // calls setter
q // calls getter

or like this

function plus(@int a, @int b ) { a + b }

and the decorator will round the number

or like this:

function limitCount(descriptor){
  return function decorator(count){ 
    if(count > 100) throw new Error
  }
}

function getMessages( @limitCount count ){ }

or like this:

function onWebsocketRequest (@clear params){
   console.log(params)
}

or like this:

@mustAuth
@mustBeInGroup('admin')
function getUsersList ( ) {

}

or like this:

jQuery.prototype.css = @normalizeArgs function (cssProps){

}

jQuery.css('prop', val) // all arguments are normalized
jQuery.css({'prop', val}) // all arguments are normalized

a many of examples for using decorators, and not just for decorating classes.

@MaxmaxmaximusGitHub MaxmaxmaximusGitHub changed the title allow decorate functions allow decorate export functions Apr 26, 2020
@NullVoxPopuli
Copy link

NullVoxPopuli commented Apr 26, 2020

This'd be functionally the same as existing class decorators, yeah?

@MaxmaxmaximusGitHub MaxmaxmaximusGitHub changed the title allow decorate export functions [Feature Request] allow decorate export functions Apr 26, 2020
@MaxmaxmaximusGitHub
Copy link
Author

@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.

@pzuraq
Copy link
Collaborator

pzuraq commented Apr 28, 2020

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){

}

@MaxmaxmaximusGitHub MaxmaxmaximusGitHub changed the title [Feature Request] allow decorate export functions [Feature Request] allow decorate for ALL javascript entities Apr 28, 2020
@littledan
Copy link
Member

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.

@MaxmaxmaximusGitHub
Copy link
Author

MaxmaxmaximusGitHub commented May 10, 2020

Function decorators face unresolved conflicts for semantics around hoisting.

everything is simple, if a decorator is applied to the function declaration, it becomes function expression =) since the use of the decorator is not a declaration, is action (expression)

functions

@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(){} )

args

function lol (@int age){  

}

is:

function lol(age){
  age = int({kind: 'argument'})( age )
}

@littledan
Copy link
Member

Those semantics come into conflict with some expectations that have been articulated:

  • For function decorators, the semantics proposed would prohibit function hoisting for decorated functions
  • For parameter decorators, you may want the parameter decorator to be somehow queryable without calling the function

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.

@MaxmaxmaximusGitHub
Copy link
Author

MaxmaxmaximusGitHub commented Jul 9, 2020

@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.

@trusktr
Copy link
Contributor

trusktr commented Aug 10, 2020

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.

@ceigey
Copy link

ceigey commented Sep 25, 2020

(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
}

@littledan
Copy link
Member

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.

@MaxmaxmaximusAWS
Copy link

MaxmaxmaximusAWS commented Jul 29, 2021

// 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. =)

@MaxGraey
Copy link

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;
}

@MaxmaxmaximusAWS
Copy link

MaxmaxmaximusAWS commented Sep 26, 2022

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 =)?

@MaxGraey
Copy link

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

@MaxmaxmaximusAWS
Copy link

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants