В языке F# функции могут быть вложенными, то есть располагаться внутри других функций. Например:
let outer() =
let inner() = printfn "Inner scope"
printfn "Outer scope"
В данном случае внутри функции outer определена функция inner.
Однако здесь функция inner хотя и определена, но не вызывается. Даже если мы вызовем функцию outer:
let outer() =
let inner() = printfn "Inner scope"
printfn "Outer scope"
outer() // вызываем функцию outer
Функция inner не будет автоматически выполняться. Для выполнения ее надо вызвать в функции outer:
let outer() =
let inner() = printfn "Inner scope"
inner() // вызываем функцию inner
printfn "Outer scope"
outer() // вызываем функцию outer
Программа на языке F# можно иметь различные области видимости (scope), одна область видимости может располагаться внутри другой и таким образом образовывать различные уровни. Каждая отдельная функциия образует свою область видимости. Например:
// глобальная область видимости
printfn "Global scope"
let outer() = // область видимости функции outer
let inner() = // область видимости функции inner
printfn "Inner scope"
printfn "Outer scope"
Здесь мы сталкиваемся с тремя областями видимости или контекстами. Вне функций расположена глобальная область видимости. Именно в ней определена
функция outer.
Функция outer определяет вложенную свою область видимости. Именно в этой области видимости определена функция inner.
Функция inner определяет вложенную по отношению к функции outer область видимости.
За счет вложенных функций мы можем создавать все более глубокие уровни областей видимостей:
// глобальная область видимости
printfn "Global scope"
let outer() = // область видимости outer
let inner() = // область видимости inner
let subinner() = // область видимости subinner
printfn "Subinner scope"
printfn "Inner scope"
printfn "Outer scope"
Но из этого следует ограничение: все значения и функции доступны только в рамках той области видимости, где они определены или во вложенных областях видимости, но НЕ во внешних. Например:
// глобальная область видимости
let a = 5
let helloGlobal() = printfn "Global scope"
let outer() = // область видимости функции outer
let inner() = // область видимости функции inner
helloGlobal() // обращение к фунции helloGlobal из глобального контекста
printfn $"Inner scope. a: {a}" // обращение к значению a из глобального контекста
inner()
printfn "Outer scope"
outer()
Здесь на глобальном уровне определено одно значение a и две функции helloGlobal и outer. В любом месте глобальной области видимости,
в том числе в функции outer и ее вложенных контекстах мы можем обратиться к глобальным значениям и функциям.
Другой пример: попробуем обратиться к значениям и функциям, определенным внутри другой функции:
let outer() = // область видимости функции outer
let helloOuter() = printfn "Outer scope"
let b = 15
let inner() = // область видимости функции inner
helloOuter() // обращение к фунции helloOuter из контекста outer
printfn $"Inner scope. b: {b}" // обращение к значению b из контекста outer
inner()
// глобальная область видимости
outer()
// helloOuter() // функция helloOuter из контекста outer недоступна на глобальном контексте
// printfn $"Global scope. b: {b}" // значение b из контекста outer недоступно на глобальном контексте
Здесь в функции outer определено одно значение b и две функции helloOuter и inner.
В любом месте функции outer, в том числе ее вложенных контекстах мы можем обратиться к ее значениям и функциям. Однако вне функции outer
определенные внутри нее значения и функции будут не доступны:
// глобальная область видимости
outer()
// helloOuter() // функция helloOuter из контекста outer недоступна на глобальном контексте
// printfn $"Global scope. b: {b}" // значение b из контекста outer недоступно на глобальном контексте
Соответственно все значения и функции, определенные внутри функции inner, были бы доступны только в рамках этой функции.
Внутри внутренней области видимости F# позволяет задавать значения и функции с теми же именами, что и во внешней области видимости.
// глобальная область видимости
let n = 10
let hello() = printfn $"Global scope. n = {n}"
let outer() = // область видимости функции outer
let n = 50
let hello() = printfn $"Outer scope. n = {n}"
let inner() = // область видимости функции inner
let n = 250
let hello() = printfn $"Inner scope. n = {n}"
hello() // Inner scope. n = 250
inner() // Inner scope. n = 250
hello() // Outer scope. n = 50
outer()
hello() // Global scope. n = 10
Консольный вывод программы:
Inner scope. n = 250 Outer scope. n = 50 Global scope. n = 10
Здесь на глобальном уровне определены значение n и функция hello:
let n = 10
let hello() = printfn $"Global scope. n = {n}"
На уровне функции outer определяются те же значение и функция:
let n = 50
let hello() = printfn $"Outer scope. n = {n}"
Далее все обращения к значению n и функции hello будут использовать их определения на уровне функции outer, а не из глобальной области видимости.
То есть значение n из функции outer скрывает значение n из глобального контекста, а определение функции hello скрывает определение
этой функции из глобального контекста.
Но далее во вложенной функции inner также определяются значение и функция с теми же именами:
let n = 250
let hello() = printfn $"Inner scope. n = {n}"
Подобным образом определение значения n и функции hello из функции inner скрывают определения значение n и
функции hello из контекста функции outer. Поэтому далее внутри функции inner будут использоваться
определения этих значения и функции из функции inner, а не из функции outer