Ошибка FS0037 иногда очень сбивает с толку

Если я напишу следующий код F#, компилятор выдаст ошибку.

let a = 123
let a = 123

Произошла ошибка:

ошибка FS0037: повторное определение значения "а"

Если я напишу тот же код в функции, как это:

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

это не производит никакой ошибки.

Я не понимаю разницу. Кто-нибудь может объяснить, пожалуйста?

Изменить: первый код, который я пишу на уровне модуля.

3 ответа

Решение

Я согласен, что это сбивает с толку. Проблема в том, что let ведет себя по-разному, когда он используется в качестве локальной переменной (внутри функции) и когда он используется в качестве глобального определения (в модуле).

Глобальные определения (в модуле) компилируются как статические члены статического класса, поэтому имя можно использовать только один раз. Это означает, что использование верхнего уровня:

let a = 10
let a = 11

... это ошибка, потому что F# должен был бы создать два статических члена с одинаковым именем.

Локальные определения (внутри функции или другой вложенной области видимости) компилируются в IL, и имя переменной по существу исчезает (вместо этого IL использует стек). В этом случае F# позволяет скрывать переменные, и вы можете скрыть переменную существующего имени. Это может быть внутри функции, или даже просто do блок:

do
  let a = 10
  let a = 11
  ()

Это немного сбивает с толку, потому что затенение переменных работает только внутри локальных областей, но не на верхнем уровне. Это имеет смысл, когда вы знаете, как все компилируется, хотя..

по объему и слежке

как упомянул (но не объяснил) CaringDev, вы, вероятно, увидите, что такое затенение, когда сделаете область видимости более очевидной (используя let ... in ... сооружать #light Позвольте вам немного сократить - но вы все еще можете использовать его даже без #light off)

Попробуй это:

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

как вы можете видеть выражение оценивается в затененное значение a - но оригинал не пропал:

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

это просто не входит в сферу во внутреннем let ... in ...

Кстати: вы можете переписать свой пример:

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

(Я добавил скобки, чтобы сделать это более очевидным)


другой на уровне модуля действительно определяет значение для области действия модуля и на самом деле является не выражением, а определением

Первый определяет два открытых значения с одним и тем же именем.

Вторая скрывает (затеняет) значение.

При первом у вас будет внешне видимое изменение состояния (a ведет себя как изменчивый) в то время как со вторым вы не можете (у вас есть два aв разных сферах).

Если вы пишете свои заявления в #light off Синтаксис ML становится очевидным сразу.

Другие вопросы по тегам