Error FS0037 sometimes, very confusing
Asked Answered
N

3

8

If I write the following F# code, the compiler issues an error.

let a = 123
let a = 123

The error produced is:

error FS0037: Duplicate definition of value 'a'

If I write the same code in a function like this:

let fctn = 
    let a =123
    let a =123
    a

it doesn't produce any error.

I don't understand the difference. Can anyone please explain?

Edit : first code I write in module level.

Nonintervention answered 8/4, 2016 at 5:32 Comment(3)
isn't clear what you're asking. provide mcvePox
cut and pasting these into fsi gives essentially the same error for both.Hosea
From MSDN Functions (F#) : At any level of scope other than module scope, it is not an error to reuse a value or function name. If you reuse a name, the name declared later shadows the name declared earlier. However, at the top level scope in a module, names must be unique.Botheration
P
9

I agree this is confusing. The problem is that let behaves differently when it is used as a local variable (within a function) and when it is used as a global definition (within a module).

Global definitions (in a module) are compiled as static members of a static class and so a name can be used only once. This means that top-level use of:

let a = 10
let a = 11

... is an error, because F# would have to produce two static members of the same name.

Local definitions (inside a function or some other nested scope) are compiled to Common IL and the variable name essentially disappears (the IL uses the stack instead). In this case, F# allows you to shadow variables, that is, you can hide a previous variable by re-using an existing name. This can be inside a function, or even just a do block inside a module, type or other function:

do
  let a = 10
  let a = 11
  ()

This is a bit confusing, because variable shadowing only works inside local scopes but not at the top level. It makes sense when you know how things are compiled though.

As an aside, while IL allows overloads of members by the same name, such overloads cannot be defined at module level in F#. Instead, you'd need to define them specifically as static member on a class (type in F#).

Perfectionism answered 8/4, 2016 at 11:34 Comment(4)
Yep!! very much satisfied with your answer.Nonintervention
Not only is it confusing, but it also isn't how it works in OCamlLoughlin
In OCaml, "a let binding in an inner scope can shadow". I don't know if top-level bindings can shadow (other than through let ... in), that document isn't clear on that, but there aren't any examples there. Though, in F#, you can shadow by opening two modules with the same let bindings.Mckay
I just tried it on OCaml online, and it works the same. By default it expects you to write let f x ... in, which creates scope. So you aren't really shadowing top level, you are just at an inner scope in an OCaml function. But I'm not an OCaml expert, so I may be missing something.Mckay
M
5

on scope and shadowing

as CaringDev mentioned (but not explained) you will probably see what the shadowing is about when you make the scope a bit more obvious (using the let ... in ... construct #light let you shorten a bit - but you still can use it even without #light off)

Try this:

> let a = 233 in let a = 555 in a;;
val it : int = 555

as you can see the expression evaluates to the shadowed value of a - but the original is not lost:

> let a = 233 in (let a = 555 in a), a;;
val it : int * int = (555, 233)

it's just not in scope in the inner let ... in ...

btw: you can rewrite your example to:

let fctn = 
    let a = 123 in
    (let a =123 in a)

(I added the parentheses just to make this more obvious)


the other on the module level really defines a value for the scope of the module and is not really an expression but a definition

Mozza answered 8/4, 2016 at 6:50 Comment(1)
Yeah, writing on my mobile... Planned to add the explanation later. Thanks.Opia
O
3

The first snippet defines two public values with the same name.

The second hides (shadows) a value.

With the first you would have externally visible change of state (a behaves like mutable) whereas with the second you can't (you have two as in different scopes).

If you write your statements in #light off ML syntax it becomes obvious immediately.

Opia answered 8/4, 2016 at 6:34 Comment(1)
I guess you meant s/first/latter, s/second/formerArchivist

© 2022 - 2024 — McMap. All rights reserved.