-
Notifications
You must be signed in to change notification settings - Fork 1.9k
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
Detect single-assignment bindings #482
Comments
Sadly it's not quite that simple... but it's an interesting situation:
So I guess the direct answer is that it's easy enough to detect that x is never re-assigned - in fact, an implementation of |
Thanks for putting it so clearly! I think my problem was that I started without specific problem code in mind, but just a principle. When I started playing around with more realistic code, I noticed that it's actually quite difficult to confuse flow w.r.t. refinements and non-constant bindings. Good! I will note that it's slightly surprising that the type checker would treat the following code snippet differently from the one above (I understand why it does this): /* @flow */
var x: ?number = 1;
if (x) {
var y = x;
setTimeout(() => {
(y : number) // OK
})
} That aside, I still think there could be some use for tracking constant bindings. A realistic example comes from a change I tried to make a while ago in #318.
If we knew that the So, not a great example again, but is it enough to show the usefulness of tracking constant bindings? You mentioned that an implementation of |
Aha - the example actually reinforces the point, though, check it out: y is assigned only once, from a (refined to) number rvalue, and there's no widening annotation. So it's guaranteed to always be a number, and thus safe. (We're not doing anything special to recognize it as a single assignment, it's just that the complete set of assignments yields a number type.) So again, it's the annotation that starts the dominos falling in the previous example: it means that the refinement is required to narrow x back down to number, but that refinement is busted by the fact that the closure can run anywhere. In the #318 example, I'm not entirely sure what's going on, but it looks to me like it's again a victim of closures running anywhere: we definitely can't be sure whether s is "hello" or "oops" when it runs. Unless I'm missing something, for me the real takeaway here is the need for const - even if we were to track de facto const vars, the problem with exploiting them is the lack of developer intent (consider e.g. what happens when somebody reassigns what is by all appearances an ordinary var, maybe in a place that's distant from its definition, and starts getting type errors on all the stuff that was using the thing as if it were a bona fide const). I haven't been following #431 (which, my bad) but I'll take a look now. |
So my take-away here is: yes, we can track de facto const vars, but it's I opened this issue after being convinced by a tweet from @wycats. Yehuda, I'll think a bit more on use cases for inferred const and close this if I On Tue, Jun 2, 2015 at 7:27 AM, Basil Hosmer notifications@github.com
|
Yeah. Further, though, and sorry to be tiresome: the issue with the first example is specifically that it's problematic to override an annotated type with a narrower inferred type - even though we can see that the latter holds throughout the variable's lifetime. Without the annotation, one naturally gets the benefit of assignment tracking - per your second example. :) You can think of it as a precedence ordering, strongest first:
The first example has an error because it can't rely on (1), due to closures running anywhere, but would instead need for (3) to be higher precedence than (2). Make sense? |
What would the benefits of Flow detecting de-facto const bindings be? |
OK, so I've been thinking on this a bit, and I did come up with what I believe is a compelling use case where the benefit outweighs the cost1. Flow doesn't currently support computed object keys. We could support them today for string literals (e.g., This is a very similar use case for React components in #318, but a lot more applicable to every day code. Now, we could leverage
|
@samwgoldman Looks like it works now. Can you confirm? |
We have the ability to detect effectively-const variables for a while. |
Flow should be able to determine that
x
is never re-assigned. I would be interested to discuss how we might accomplish this in this issue.Possible solution: on each init_env, store a boolean
mutated
property with the new scope entry initially set tofalse
. When an assignment happens, flipmutated
totrue
. Then, whenever we need to havoc the env, we could only do so for scope entries wheremutated
is true.Is that sound?
The text was updated successfully, but these errors were encountered: