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

Guarded function declarations #9253

Closed
tinganho opened this issue Jun 19, 2016 · 10 comments
Closed

Guarded function declarations #9253

tinganho opened this issue Jun 19, 2016 · 10 comments
Labels
Suggestion An idea for TypeScript Too Complex An issue which adding support for may be too complex for the value it adds

Comments

@tinganho
Copy link
Contributor

I sometimes reference outer scope variables and class members in my function/method declarations. The problem is that those references are nullables. I want a way to ensure that a function is called on a call site where those references are not null.

let corns: string | null;
function makePopCorn(): string 
    requires corns !== null
{
   return 'pop';
}
if (corns) {
   makePopCorn();
}
makePopCorn(); // error corns can be null

Related #198. But not duplicate.

@aluanhaddad
Copy link
Contributor

If makePopCorn dereferenced corns enabling structNullChecks would require a check inside it. I assume the requires clause to be lexically scoped so why should there be an external check.
Or is the intent to make the function only callable based on arbitrary restrictions on members in its declarations scope that it specifies declaratively? That would seem like an odd coupling and if the function were passed as a callback, corns would not be available to the caller. Is the intended behavior in that case that an Error would be thrown at runtime?

@tinganho
Copy link
Contributor Author

Or is the intent to make the function only callable based on arbitrary restrictions on members in its declarations scope that it specifies declaratively

Yes this was my original intention.

That would seem like an odd coupling and if the function were passed as a callback, corns would not be available to the caller.

I'm not sure I understand you. Why would the caller need corns? Assuming caller is the function that calls the callback.

If the functions is being passed as a callback corns still needs to be checked that it is not null.

let corns: string | null;
function makePopCorn(): string 
    requires corns !== null
{
   return 'pop';
}
function call(callback: any) {
   callback();
}
if (corns) {
   call(makePopCorn);
}
call(makePopCorn); // error corns can be null

@aluanhaddad
Copy link
Contributor

aluanhaddad commented Jun 19, 2016

If the functions is being passed as a callback corns still needs to be checked that it is not null.

let corns: string | null;
function makePopCorn(): string 
    requires corns !== null
{
   return 'pop';
}
function call(callback: any) {
   callback();
}
if (corns) {
   call(makePopCorn);
}
call(makePopCorn); // error corns can be null

But that has a different meaning. It means don't allow makePopCorn to be passed as an argument when corns is null. That does not mean that corns will not be null when it is invoked.
Just as an arbitrary example:

let corns: string | null;
function makePopCorn(): string 
    requires corns !== null
{
   return 'pop';
}
function callLater(callback: any) {
   setTimeout(() => callback(), 1000);
}
if (corns) {
   callLater(makePopCorn);
}
corns = null;

@tinganho
Copy link
Contributor Author

tinganho commented Jun 19, 2016

That does not mean that corns will not be null when it is invoked.
Just as an arbitrary example:

Yes I know. Your example have been covered before in the topic of callbacks in strictNullChecks. And I think it is an ongoing issue. But I have proposed to declare callbacks as immediately invoked or not in #7770 (comment).

@aluanhaddad
Copy link
Contributor

OK but then why not just check inside the function? The questions of immediacy, how the function is called and by whom all become irrelevant if you check inside the function itself. It seems like a very strange way to partially guarantee something that can be guaranteed by checking at the time of the call. Maybe I need to see a more comprehensive example, but it seems odd to have a function that has a precondition which is not related to any argument, or state used by the function and which cannot be checked when it is passed to a separate lexical scope.

Also, I believe the issue with strictNullChecks and deferred execution is related to assumptions made about the nullity of a value between the time a closure using it is defined and invoked. That's not the case here.

@tinganho
Copy link
Contributor Author

OK but then why not just check inside the function

I write a lot of functions and private methods that expects some referenced variables or properties to be not null. I can use the unwrap operator to unwrap them or just add an if statement in those cases. The latter paying a runtime cost and the former paying an unsafe cost.

I think declaring the function to expect some references to be not null is the most type safest and you also don't pay a runtime cost with one or more if checks. The function declarations are also more readable function ... requires something !== null.

An example case given below. Model can be null. bindModel method is declared below that this.model cannot be null when called.

class PostView() {
   model: Post | null;
   view() {
   }

   bind() {
     if (this.post) {
        this.bindModel();
     }
   }

   bindModel(): void
        requires this.model != null
   {
        this.model.on('change:title', () => alert('hello'))
   }
}

@aluanhaddad
Copy link
Contributor

OK, I see what you are saying, but now that you can use strictNullChecks, parameterization as in

bindModel(model: Post): void {
    model.on('change:title', () => alert('hello'));
}

gives you what you want without adding additional syntactic complexity to the language. This also makes the function more reusable, readable, and explicit about its dependencies. It says requires model !== null by simply declaring a parameter.

@tinganho
Copy link
Contributor Author

I haven't really liked parameterisation solutions. It can get quite ugly if you have a lot of parameters you need to define. Though I agree it solves the problem.

@zpdDG4gta8XKpMCd
Copy link

how can you ensure anything via checks at runtime?

@RyanCavanaugh RyanCavanaugh added Suggestion An idea for TypeScript Too Complex An issue which adding support for may be too complex for the value it adds labels Aug 18, 2016
@RyanCavanaugh
Copy link
Member

RyanCavanaugh commented Aug 18, 2016

This seems way too complex for something that could really only represent a small fraction of the kind of "I need X side effects first" things you'd really need to make this a comprehensive solution.

@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Suggestion An idea for TypeScript Too Complex An issue which adding support for may be too complex for the value it adds
Projects
None yet
Development

No branches or pull requests

4 participants