What are the rules for shadowing versus duplicate definitions?

(I’m working from a script and have tried this in both VS and VSCode, just in case.)

I have three code examples, the first being:

let foo x y = x + y
let foo = foo 5
foo 10

…which gives me an “ FS0037 Duplicate definition of value ‘foo’ “ error on the second definition of foo.

However, my second bit of code:

module thing = 
 let foo x y = x + y
open thing
let foo = foo 5
foo 10

…is completely fine (no compiler errors).

And this bit of code:

let whatsit =
 let foo x y = x + y
 let foo = foo 5
 foo 10

…is also fine.

Can someone explain, in simple beginner-friendly terms, what’s happening here please?

1 Like

It’s easy to miss what the F# language reference says about shadowing when describing scope:

At any level of scope other than module scope, it is not an error to reuse a value or function name

In your example below, you’re opening the thing module before calling foo :

module thing = 
 let foo x y = x + y
open thing
let foo = foo 5
foo 10

That is most probably the compiler hiding thing.foo from us, but still keeping track of the fact that foo is defined under module thing, and that’s different than a foo defined in whatever module you’re opening thing from. So you’re not redefining foo within the same module here.

This accepted stackoverflow answer appears to confirm my understanding of how it all works.

Oh, the other thing re shadowing is, if you open multiple modules and they have the same definitions, the one from the module opened later shadows the one from the module opened earlier. So that’s something to watch for as style guide warns us about.

Thanks for your reply.

I read, and re-read, the documentation getting more confused until I realised that “module scope” can include code that isn’t explicitly in a module.

Reading this: Modules - F# | Microsoft Docs

I found:

“A top-level module declaration includes the whole file in the module.”

And later:

“If a code file does not begin with a top-level module declaration or a namespace declaration, the whole contents of the file, including any local modules, becomes part of an implicitly created top-level module that has the same name as the file, without the extension, with the first letter converted to uppercase.”

So, because the code in my first example was not in an explicitly-named module - it was just in a script - it was actually in an implicitly-named module and therefore:

“At any level of scope other than module scope, it is not an error to reuse a value or function name.”

…applies when coupled with:

“However, at the top level scope in a module, names must be unique.”

…because it is actually at “module scope” even though that’s not explicit, but the code is at the top level so the names have to be unique.

I think I now understand this a little bit better than I have explained my understanding of it above.

Thanks for pointing me in the right direction.

1 Like