Ошибка 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 становится очевидным сразу.