В языке 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