I'm wondering why F# allows shadowing, particularly within the same scope.
I've always thought of value binding in purely functional programming constructs as being akin to assignments in algebra/mathematics.
So for example,
y = x + 1
will be a valid mathematical expression but
y = y + 1
won't be.
However, because of shadowing,
let y = y + 1
is a perfectly valid expression in F#.
Why does the language allow this?
The most correct answer to this is because the creator, Don Syme, felt that it was a useful feature to add to the language.
There are some good uses for it, though. One is when working with F#-style optional parameters:
type C() =
member _.M(?x) =
let x = Option.defaultValue 0 x
printfn "%d" x
F# optional parameters are options, which brings a high degree of correctness and consistency. But imagine having 3 or more optional parameters to a method. It would get annoying to have to rebind the value for each of them and use a different name for them! This is one area where shadowing comes in handy.
It's also handy when writing recursive subroutines. Consider the following naiive implementation of sum
for a list:
let mySum xs =
let rec loop xs acc =
match xs with
| [] -> acc
| h :: t -> loop t (h + acc)
loop xs 0
I don't need to rebind xs
for the inner loop because of shadowing. Since it's generic, xs
is about as good of a name as I can come up with, so it would be annoying to have had to use a different name for the inner loop.
Shadowing isn't all good news, though. If you're not careful, types from one open
declaraction can shadow types from one that was declared previously. This can be confusing. F# editor tooling can distinguish bindings from the ones that shadow them, but you don't get that with plain text. So the bottom line is: think carefully when applying shadowing in F#.