-
Notifications
You must be signed in to change notification settings - Fork 798
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
F# allows re-defining name although documentation says otherwise #9900
Comments
This is by design - the restiction applies to definitions which result in an accessible, not for local definitions in expressions, e.g. this is disallowed:
but in your own example the Sometimes it's helpful to think in terms of the one-line expression form
|
@dsyme Please, read more carefully documentation. It doesn't say about whether they are local. There's a statement which says "fails to compile". I checked it - compiles fine. |
@JohnyL -- I agree that the documentation doesn't make clear the distinction between a let binding on a module and local bindings, and expects the reader to figure it out. Perhaps you could suggest a way of clarifying the documentation, at the bottom of the page is a feedback button for the page: For example perhaps the comment in the documentation could be amended:
Perhaps you could provide feedback to the documentation team, and they can work on making it clearer. There is a link at the bottom of each page of documentation. |
@KevinRansom I have already done that. 😉 Anyway, it's strange design. If F# relies on immutability of this binding, then I see no immutability here. It's like in JavaScript with |
@JohnyL The values are immutable, what you're doing is rebinding a variable identifier, not changing the value at that memory location. This shadows the original name and under the hood, F# will create a new name. It's still immutable, you cannot change the value and at the same time keep the same memory location. You can use |
Here's an example let x = 3
for i = 0 to 10 do
let x = i
ignore()
printfn "%i" x // prints 3 Compare that with let mutable x = 3
for i = 0 to 10 do
x <- i
ignore()
printfn "%i" x // prints 10 Likewise, you compared it with |
@JohnyL, you definitely point to a problem with that piece of documentation. I understand that you are new to F#, and would like to suggest that you join one of the discussion forums of the F# community in order to discuss issues like this one. In particular, all of us commenting so far are just a few among thousands of F# programmers in the F# Slack forum, and we can help with pretty much any topic. We'd love to welcome you there. |
@abelbraaksma Thanks for explanation, but your example is a bit different. You're redefining in different scope, while my example (more accurately, doc's example) is in the same scope. If I can redefine the variable in the same scope, then what's the point in immutability? What's the difference between this: let GetData =
50
let x = GetData
// ... some code ...
let x = 100 //Redefining
// ... some code ...
let z = x //Oops, z expects 50, but gets 100 and this: let mutable x = 50
// ... some code ...
x <- 100
// ... some code ...
let z = x ? |
@JohnyL, what you are struggling to understand is called shadowing. That's the name of the concept. It is only allowed in a local scope. This is explained in this SO issue. But I'd like to have a go at explaining it myself too. The trick is simply that the name - and only the name - is reused for what is actually a totally new binding that is not related to the old binding. That means that in your example, you have two local bindings with the same name. The one defined last is said to be shadowing the one defined first. Let me give you another example to clarify further.
There are two bindings here - the first x, and the second x. You can also see that the second binding's expression can access the first, but after that the print statement would not be able to refer to the first x, because it has been shadowed by the second. In this example we also happen to have different types on the two bindings, which is no problem since they have nothing to do with each other, besides having the same name. The first x is an int, and the second x is a float. Why shadowing? There are advantages. It blocks trying to access the first x by its name. It helps make intentions clearer. In F# we tend to think about how data flows, and in this case it's clearer that we're calculating something we'd like to call x step by step, reusing the name along the way. I hope this clarified things. |
And to add to that excellent explanation, recall that a newline is light syntax for using However, that also makes it harder to code. Btw, the shadowing in one scope or in nested scopes is technically the same, each If, and only if, you're assigning a value to an existing binding using |
@abelbraaksma @BentTranberg Thanks for explanation! Now the picture is clear!😉 OFF: I must use my work e-mail to enter F# Slack forums? |
I did not register with my work email, but with my private GMail address. Guess you can use whichever you want. |
@JohnyL FYI, From another little research, I happened upon this little beauty that explains this concept very easily and succinctly: |
@abelbraaksma Thanks for clarifying! 😉 |
Please provide a succinct description of the issue.
The documentation says:
This is not true. The following compiles and executes perfectly:
Related information
The text was updated successfully, but these errors were encountered: