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

[proposal] [tc39] Static variables inside functions #25628

Closed
3 of 4 tasks
zikaari opened this issue Jul 13, 2018 · 15 comments
Closed
3 of 4 tasks

[proposal] [tc39] Static variables inside functions #25628

zikaari opened this issue Jul 13, 2018 · 15 comments
Labels
Duplicate An existing issue was already created

Comments

@zikaari
Copy link

zikaari commented Jul 13, 2018

Search Terms

function,static,variable

Suggestion

I believe it'd be really useful to have static variables inside methods/functions. Initialized once, reused for subsequent calls and in-memory lifetime as long as containing scope i.e the function itself.

This issue is not a proposal to get this implemented into TypeScript as I am well aware that TS team won't implement stuff that is not standard/draft. Instead, this issue is a proof of concept and market validation attempt to see if the community would want something like that. And if so, we can move forward with proposing this to tc39 folks.

Use Cases

Almost every app has a function or method that needs to "preserve" its state across multiple calls, a counter for example. Current ways out of this situation are either IIFE's or making those variables top-level. Both of which are ugly. I think the same could be done much more neatly with static variables.

Examples

// main.ts

class DBService {
    static checkURI = (uri: string) => {
    	// this way pattern won't be (re)compiled every time checkURI is called
        static const httpRE = /^https?/;

        // relevant variables contained in relevant scopes
        // historically counters are either top-level variables or props of the class
        static let errCount = 0;

        if (errCount > 10) {
            throw new Error(`Some error`);
        }

        if (!httpRE.test(uri)) {
            errCount++;
            return false;
        }
    }
}

===

// compiled.js

var DBService = /** @class */ (function () {
    function DBService() {
    }
    DBService.checkURI = (function () {
        var httpRE = /^https?/;
        var errCount = 0;
        return function (uri) {
            if (errCount > 10) {
                throw new Error("Some error");
            }
            if (!httpRE.test(uri)) {
                errCount++;
                return false;
            }
        };
    })();
    return DBService;
}());

Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript / JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This isn't a runtime feature (e.g. new expression-level syntax)
@Kingwl
Copy link
Contributor

Kingwl commented Jul 13, 2018

As far as I know, it is generally used to be a thread-safe singleton in c++.
so what does it do in js?
only extract the static variable out of the function scope?

@zikaari
Copy link
Author

zikaari commented Jul 13, 2018

As far as I know, it is generally used to be a thread-safe singleton in c++

Didn't know about that

so what does it do in js? only extract the static variable out of the function scope?

Quite the opposite.

Relevant variables should live in relevant scope.

Variables that a function will need over and over should still remain inside that function body if they have no business outside.

Examples are: Regex patterns, inner functions, state etc.

@zikaari zikaari changed the title [proposal] [tc39] Static variables like C++ [proposal] [tc39] Static variables inside functions Jul 13, 2018
@zikaari
Copy link
Author

zikaari commented Jul 13, 2018

EDIT: Original post edited to remove any reference to C++ realm

@qti3e
Copy link

qti3e commented Jul 13, 2018

I absolutely like this proposal;

const name_a = new ArrayBuffer(2000);
function a() {
  ...
}
const name_b = new ArrayBuffer(5000);
function b() {
  ...
}

becomes:

function a() {
  static const name = new ArrayBuffer(2000);
}
function b() {
  static const name = new ArrayBuffer(5000); // I can use the same name here.
}

@zikaari
Copy link
Author

zikaari commented Jul 13, 2018

Apologies but I don't quite understand your question.

As a note though, this issue doesn't necessarily apply to classes or it's static methods or non-static methods.

The example in OP just happens to use a class with a static method, it could very also apply to regular functions, like:

function traverse(tree) {
	// inner function that JS engine won't need to "(re)compile" everytime traverse is called
	static function nextNode(n) {
    	// implement recursion logic
    }

    return nextNode(tree.children)
}

@zikaari
Copy link
Author

zikaari commented Jul 13, 2018

But that's not very pretty?

It's like saying, instead of:

const res = await fetch('http://example.com')

can't you just

fetch('http://example.com')
    .then(res => /* impl */)

We're astronauts. We evolve.

@yortus
Copy link
Contributor

yortus commented Jul 13, 2018

Duplicate of #8419

@zikaari
Copy link
Author

zikaari commented Jul 13, 2018

I read that thread before creating this issue. You're welcome to call this a revist to #8419 with subtle differences.

  • That issue focuses on declaring those "statics" as function property that can be accessed outside using fnName.someStaticVar, unlike this one which wants to remain "inside" function scope because it has no business outside of function body.

  • This is an attempt to get static variables standardized.

Anyway, the previous issue discussed the implentation details and limitations but in this one, I urge you guys to look at it from "user" and "uses" standpoint. Imagine the wonders it can do at minimal implementaion cost.

@mhegazy mhegazy added the Duplicate An existing issue was already created label Jul 13, 2018
@mhegazy
Copy link
Contributor

mhegazy commented Jul 13, 2018

C-style static variables in functions has been something we considered at the very beginning of TS, and chose not to support. This issue is a duplicate of #8419. if we were going to tackle this again, #15868 would be our most likely approach.

@zikaari
Copy link
Author

zikaari commented Jul 13, 2018

Perhaps I'm confusing what I am proposing with statics. Maybe you guys are getting the wrong impression due to some dark connotation linked with static keyword and C.

But please don't look at the choice of keywords, instead the role/function of the proposal (which I want to get in tc39).

For the thing I am proposing, the keyword could very well be persist, alive or retain instead of static, once again the role is what matters.

With all that being said, would you guys like to see this in tc39 and then TS? If not, I'd love if you guys can share the flaws so I can convince the inner me the same.

@mhegazy
Copy link
Contributor

mhegazy commented Jul 13, 2018

would you guys like to see this in tc39 and then TS?

yes. we do not add expression-level syntax unless we really have to. for features like these we recommend posting them to TC39. TypeScript is committed to trailing TC39 for new features.

@zikaari
Copy link
Author

zikaari commented Jul 13, 2018

In a nutshell, this is my proposal:

function foo(n) {
	// local stack variable
	let a =  12
    
    // initialized on first call to foo, persists for subsequent calls
    persist let counter =  0
    
    counter++
    return n * a
}

is 1:1 as

let foo = (() => {
	let counter = 0
	return (n) => {
    	let a = 12
        counter++
        return  n * a
    }
})()

Just syntactic sugar to IIFE's

The reason I created this issue here is because proposing to tc39 requires some sort of membership or being accompanied by a tc39 member. I meet neither of those requirements, but I'm pretty sure someone from Microsoft does, and that is why I'm here.

@RyanCavanaugh
Copy link
Member

You can bring this up on es-discuss; if it gains traction there it will naturally acquire a TC39 champion. Our TC39 advocacy plate is completely full.

@zikaari
Copy link
Author

zikaari commented Jul 14, 2018

Posted on es-discuss

@LifeIsStrange
Copy link

@RyanCavanaugh
Our TC39 advocacy plate is completely full
Does that still hold true one year later?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

7 participants