Nested function: Difference between revisions
No edit summary Tags: Visual edit Mobile edit Mobile web edit |
Golang function closures Tag: Disambiguation links added |
||
(38 intermediate revisions by 22 users not shown) | |||
Line 1: | Line 1: | ||
{{Short description|A named function defined within a function}} |
|||
In [[computer programming]], a '''nested function''' (or '''nested procedure''' or '''subroutine''') is a [[subroutine|function]] which is defined within another function, the ''enclosing function''. Due to simple recursive [[scope (programming)|scope]] rules, a nested function is itself invisible outside of its immediately enclosing function, but can see (access) all local objects (data, functions, types, etc.) of its immediately enclosing function as well as of any function(s) which, in turn, encloses that function. The nesting is theoretically possible to unlimited depth, although only a few levels are normally used in practical programs. |
|||
In [[computer programming]], a '''nested function''' (or '''nested procedure''' or '''subroutine''') is a [[identifier|named]] [[subroutine|function]] that is defined within another, enclosing, block and is [[lexically scoped]] within the enclosing block {{endash}} meaning it is only callable by name within the body of the enclosing block and can use [[identifiers]] declared in outer [[Block (programming)|blocks]], including outer functions. The enclosing block is typically, but not always, another function. |
|||
Nested functions are used in many approaches to [[structured programming]], including early ones, such as [[ALGOL]], [[Simula 67]] and [[Pascal (programming language)|Pascal]], and also in many modern [[dynamic language]]s and [[functional language]]s. However, they are traditionally not supported in the (originally simple) C-family of languages. |
|||
<!-- Is there a clear but more concise wording? --> |
|||
[[Programming language]] support for nested functions varies. With respect to [[structured programming]] languages, it is supported in some outdated languages such as [[ALGOL]], [[Simula 67]] and [[Pascal (programming language)|Pascal]] and in the commonly used [[JavaScript]]. It is commonly supported in [[dynamic language|dynamic]] and [[functional language|functional]] languages. |
|||
==Effects== |
|||
However, it is not supported in some commonly used languages including standard [[C language|C]] and [[C++]]. |
|||
Nested functions assumes [[function scope]] or [[block scope]]. The scope of a nested function is inside the enclosing function, i.e. inside one of the constituent blocks of that function, which means that it is invisible outside that block and also outside the enclosing function. A nested function can access other local functions, variables, constants, types, classes, etc. that are in the same scope, or in any enclosing scope, without explicit parameter passing, which greatly simplifies passing data into and out of the nested function. This is typically allowed for both reading and writing. |
|||
Other programming technologies provide similar benefit. For example, a [[Lambda function (computer programming)|lambda function]] also allows for a function to be defined inside of a function (as well as elsewhere) and allows for similar data hiding and encapsulation. Notably, a lambda function has no name (is anonymous) and therefore cannot be called by name and has no visibility aspect. |
|||
Nested functions may in certain situations (and languages) lead to the creation of a [[Closure (computer programming)|closure]]. If it is possible for the nested function to [[Escape analysis|escape]] the enclosing function, for example if functions are [[first class object]]s and a nested function is passed to another function or returned from the enclosing function, then a closure is created and calls to this function can access the environment of the original function. The frame of the immediately enclosing function must continue to be alive until the last referencing closure dies and non-local [[automatic variable]]s referenced in closures can therefore not be [[stack allocation|stack allocated]]. This is known as the [[funarg problem]] and is a key reason why nested functions was not implemented in some simpler languages as it significantly complicates code generation and analysis, especially when functions are nested to various levels, sharing different parts of their environment. |
|||
== |
== Attributes == |
||
An example using Pascal syntax (with [[ALGOL]], [[Modula 2]], [[Oberon (programming language)|Oberon]], [[Ada (programming language)|Ada]], etc. similar): |
|||
The [[Scope (computer science)|scope]] of a nested function is the block that contains it {{endash}} be it a function block or block within a function body. It is not visible (cannot be called by name) outside its containing block. |
|||
A nested function can use [[identifiers]] (i.e. the name of functions, variables, types, classes) declared in any enclosing block, except when they are masked by inner declarations with the same names. |
|||
A nested function can be declared within a nested function, recursively, to form a deeply nested structure. |
|||
A deeply nested function can access identifiers declared in all of its enclosing blocks, including enclosing functions. |
|||
Nested functions may in certain situations lead to the creation of a [[Closure (computer programming)|closure]]. If it is possible for the nested function to [[Escape analysis|escape]] the enclosing function, for example if functions are [[first class object]]s and a nested function is passed to another function or returned from the enclosing function, then a closure is created and calls to this function can access the environment of the original function. The frame of the immediately enclosing function must continue to be alive until the last referencing closure dies and [[non-local variable|non-local]] [[automatic variable]]s referenced in closures can therefore not be [[stack allocation|stack allocated]] in languages that allow the closure to persist beyond the lifetime of the enclosing block. This is known as the [[funarg problem]] and is a key reason why nested functions was not implemented in some simpler languages as it significantly complicates code generation and analysis, especially when functions are nested to various levels, sharing different parts of their environment. |
|||
== Value == |
|||
The nested function technology allows a [[programmer]] to write [[source code]] that includes beneficial attributes such as [[information hiding]], [[encapsulation (computer programming)|encapsulation]] and [[Decomposition (computer science)|decomposition]]. The programmer can divide a task into subtasks which are only meaningful within the context of the task such that the subtask functions are hidden from callers that are not designed to use them. |
|||
Block scoping allows functions to share the state of enclosing blocks (including enclosing functions) without passing [[Parameter (computer programming)|parameters]] or using [[global variable]]s.{{sfn|Bright|2004}} |
|||
== Uses == |
|||
=== Helper === |
|||
A nested function typically acts as a [[wrapper function|helper function]] or a [[recursion (computer science)|recursive function]] (as in the quicksort example above). |
|||
=== Control flow === |
|||
Nested functions can be used for unstructured [[control flow]], by using the return statement for general unstructured control flow. This can be used for finer-grained control than is possible with other built-in features of the language – for example, it can allow early termination of a for loop if <code>break</code> is not available, or early termination of a nested [[for loop]] if a multi-level <code>break</code> or exceptions are not available. |
|||
=== Higher-order functions === |
|||
{{Main|Higher-order function}} |
|||
In some languages, it is possible to create a nested function that accesses a set of parameters from the outer function, that is a [[Closure (computer programming)|closure]], and have that function be the outer function's return value. Thus it is possible to return a function that is set to fulfill a certain task with little or no further parameters given to it, which can increase performance quite significantly.<ref name="Higher-Order Functions and Lambdas – Kotlin Programming Language">[http://kotlinlang.org/docs/reference/inline-functions.html Higher-Order Functions and Lambdas - Kotlin Programming Language]</ref> |
|||
== Examples == |
|||
=== Simple example === |
|||
A simple example in Pascal: |
|||
<syntaxhighlight lang=pascal> |
<syntaxhighlight lang=pascal> |
||
function E(x: real): real; |
function E(x: real): real; |
||
Line 20: | Line 57: | ||
end; |
end; |
||
</syntaxhighlight> |
</syntaxhighlight> |
||
The function <code>F</code> is nested within <code>E</code>. Note that <code>E</code>'s parameter <code>x</code> is |
The function <code>F</code> is nested within <code>E</code>. Note that <code>E</code>'s parameter <code>x</code> is also visible in <code>F</code> (as <code>F</code> is a part of <code>E</code>) while both <code>x</code> and <code>y</code> are invisible outside <code>E</code> and <code>F</code> respectively. |
||
Similarly, in Standard ML: |
Similarly, in [[Standard ML]]: |
||
<syntaxhighlight lang=ocaml> |
<syntaxhighlight lang=ocaml> |
||
Line 33: | Line 70: | ||
</syntaxhighlight> |
</syntaxhighlight> |
||
In [[Haskell (programming language)|Haskell]]: |
|||
<syntaxhighlight lang=haskell> |
<syntaxhighlight lang=haskell> |
||
Line 40: | Line 77: | ||
</syntaxhighlight> |
</syntaxhighlight> |
||
In [[PL/I]]: |
|||
The same example in [[GNU Compiler Collection|GNU C]] syntax<ref>{{cite book |last1=Rothwell |first1=Trevis J. |title=The GNU C Reference Manual |date=2011 |publisher=Free Software Foundation, Inc |page=63}}</ref> (C extended with nested functions): |
|||
{{pre| |
|||
e: procedure(x) returns(float); |
|||
declare x float; |
|||
f: procedure(y) returns(float); |
|||
declare y float; |
|||
return x + y |
|||
end; |
|||
return f(3.0) + f(4.0); |
|||
end; |
|||
}} |
|||
In [[Python (programming language)|Python]]: |
|||
<syntaxhighlight lang=python> |
|||
def e(x: float) -> float: |
|||
def f(y: float) -> float: |
|||
return x + y |
|||
return f(3.0) + f(4.0) |
|||
</syntaxhighlight> |
|||
In [[GNU Compiler Collection|GNU C]]<ref>{{cite book |last1=Rothwell |first1=Trevis J. |title=The GNU C Reference Manual |date=2011 |publisher=Free Software Foundation, Inc |page=63}}</ref> {{endash}} which extends standard C with nested functions: |
|||
<syntaxhighlight lang=c> |
<syntaxhighlight lang=c> |
||
Line 53: | Line 112: | ||
</syntaxhighlight> |
</syntaxhighlight> |
||
===Quicksort=== |
=== Quicksort === |
||
A more realistic example is this implementation of [[quicksort]]:<ref> |
|||
The following is an implementation of [[quicksort]]:<ref> |
|||
[http://www.dreamincode.net/forums/topic/262883-nesting-functions-why/page__p__1530693&#entry1530693 Re: Nesting functions- Why?], [http://www.dreamincode.net/forums/user/52176-baavgai/ baavgai], 14 January 2012</ref><!-- code tweaked for readability --> |
|||
{{usurped|1=[https://archive.today/20130703055646/http://www.dreamincode.net/forums/topic/262883-nesting-functions-why/page__p__1530693&%23entry1530693 Re: Nesting functions- Why?]}}, {{usurped|1=[https://web.archive.org/web/20100405113726/http://www.dreamincode.net/forums/user/52176-baavgai/ baavgai]}}, 14 January 2012</ref><!-- code tweaked for readability --> |
|||
<syntaxhighlight lang=C> |
<syntaxhighlight lang=C> |
||
void sort(int *items, int size) { |
void sort(int *items, int size) { |
||
Line 84: | Line 144: | ||
} |
} |
||
</syntaxhighlight> |
</syntaxhighlight> |
||
Another example is the following implementation of the [[Quicksort#Hoare partition scheme|Hoare partition based quicksort]] using [[C++11#Lambda functions and expressions|C++11]] [[Anonymous function#C.2B.2B .28since C.2B.2B11.29|lambda expression syntax]]: |
|||
The following is an implementation of the [[Quicksort#Hoare partition scheme|Hoare partition based quicksort]] using [[C++11#Lambda functions and expressions|C++11]] [[Anonymous function#C.2B.2B .28since C.2B.2B11.29|lambda expression syntax]] which is an alternative technology that also allows hiding a function inside a function: |
|||
<syntaxhighlight lang=c++> |
<syntaxhighlight lang=c++> |
||
template<typename RandomAccessIterator> |
template<typename RandomAccessIterator> |
||
Line 127: | Line 189: | ||
</syntaxhighlight> |
</syntaxhighlight> |
||
== |
== Languages == |
||
Lexically nested function definitions are a form of [[information hiding]] and are useful for dividing procedural tasks into subtasks which are only meaningful locally. This avoids cluttering other parts of the program with functions and variables that are unrelated to those parts. |
|||
Notable languages supporting nested functions include: |
|||
They are typically used as helper functions or as recursive functions inside another function (as in the quicksort example above). This has the structural benefit of organizing the code, avoids polluting the scope, and also allows functions to share state easily.{{sfn|Bright|2004}} As nested function can access local variables of the enclosing function, sharing of state is possible without passing parameters to the nested function or use a [[global variable]], simplifying code. |
|||
*[[ALGOL]]-based languages such as [[ALGOL 68]], [[Simula]], [[Pascal (programming language)|Pascal]], [[Modula-2]], [[Modula-3]], [[Oberon (programming language)|Oberon]], [[PL/I]], [[Seed7]] and [[Ada (programming language)|Ada]] |
|||
In languages with nested functions, functions may normally also contain local [[Constant (programming)|constants]], and [[Data type|types]] (in addition to local [[Variable (programming)|variables]], [[parameter]]s, and functions), encapsulated and hidden in the same nested manner, at any level of depth. This may further enhance the code structuring possibilities. |
|||
===Other uses=== |
|||
====Control flow==== |
|||
Nested functions can also be used for unstructured [[control flow]], by using the return statement for general unstructured control flow. This can be used for finer-grained control than is possible with other built-in features of the language – for example, it can allow early termination of a for loop if <code>break</code> is not available, or early termination of a nested [[for loop]] if a multi-level <code>break</code> or exceptions are not available. |
|||
====Higher-order functions==== |
|||
{{main|Higher-order functions}} |
|||
As in most languages functions are valid return types, it is possible to create a nested function that accesses a set of parameters from the outer function and have that function be the outer function's return value. Thus it is possible to return a function that is set to fulfill a certain task with little or no further parameters given to it, which can increase performance quite significantly.<ref name="Higher-Order Functions and Lambdas – Kotlin Programming Language">[http://kotlinlang.org/docs/reference/inline-functions.html Higher-Order Functions and Lambdas - Kotlin Programming Language]</ref> |
|||
==Alternatives== |
|||
The main alternative to nested functions in languages that lack support for them is to place all relevant functions and variables in a separate module (file) and expose only the top-level [[wrapper function]] publicly. In C this will generally be done by using static functions for encapsulation and [[static variable]]s for communication.<ref name=cfaq>"[http://c-faq.com/misc/nestfcns.html Question 20.24: Why doesn't C have nested functions?], comp.lang.c FAQ</ref> This achieves encapsulation and sharing of state, though not the logical organization given by lexical nesting of functions, and comes at the cost of having a separate file. It is also not possible in more than a single level. |
|||
Another alternative is to share state between the functions through function parameters, most often passing references as arguments to avoid the cost of copying. In C this is generally implemented by a pointer to a structure containing the context.<ref name=cfaq /> This significantly increases the complexity of the function calls.{{sfn|Bright|2004}} |
|||
In [[PHP]] and other languages the [[anonymous function]] is the only alternative: the nested function is declared not as usual function, but by reference, as a local variable. To use local variables in the anonymous function, use [[Closure (computer science)|closure]]. |
|||
==Languages== |
|||
Well known languages supporting lexically nested functions include: |
|||
*[[ALGOL]]-based languages such as [[ALGOL 68]], [[Simula]], [[Pascal (programming language)|Pascal]], [[Modula-2]], [[Modula-3]], [[Oberon (programming language)|Oberon]], [[Seed7]] and [[Ada (programming language)|Ada]] |
|||
*Modern versions of [[Lisp (programming language)|Lisp]] (with lexical scope) such as [[Scheme (programming language)|Scheme]], and [[Common Lisp]] |
*Modern versions of [[Lisp (programming language)|Lisp]] (with lexical scope) such as [[Scheme (programming language)|Scheme]], and [[Common Lisp]] |
||
*[[ECMAScript]] ([[JavaScript]] and [[ActionScript]]) |
*[[ECMAScript]] ([[JavaScript]] and [[ActionScript]]) |
||
*[[Dart (programming language)|Dart]]<ref>{{Cite web|url=https://dart.dev/guides/language/language-tour#lexical-scope|title=A tour of the Dart language}}</ref> |
|||
*[[Scala (programming language)|Scala]] (full support) |
|||
*[[Kotlin (programming language)|Kotlin]] (local functions<ref>{{Cite web|url=https://kotlinlang.org/docs/functions.html#local-functions|title = Functions | Kotlin}}</ref>) |
|||
*[[Rust (programming language)|Rust]] |
|||
*[[Scala (programming language)|Scala]] (nested functions<ref>{{Cite web|url=https://docs.scala-lang.org/tour/nested-functions.html|title=Nested Methods}}</ref>) |
|||
*Various degrees of support in scripting languages such as [[Ruby (programming language)|Ruby]], [[Python (programming language)|Python]], [[Lua (programming language)|Lua]], [[PHP]] and [[Perl]] |
*Various degrees of support in scripting languages such as [[Ruby (programming language)|Ruby]], [[Python (programming language)|Python]], [[Lua (programming language)|Lua]], [[PHP]] and [[Perl]] |
||
*[[GNU Compiler Collection|GCC]] supports nested functions in C, as a language extension.<ref>{{cite web|url=https://gcc.gnu.org/onlinedocs/gcc/Nested-Functions.html|title=Nested Functions – Using the GNU Compiler Collection (GCC)|accessdate=2007-01-06|publisher=GNU Project}}</ref> |
*[[GNU Compiler Collection|GCC]] supports nested functions in C, as a language extension.<ref>{{cite web|url=https://gcc.gnu.org/onlinedocs/gcc/Nested-Functions.html|title=Nested Functions – Using the GNU Compiler Collection (GCC)|accessdate=2007-01-06|publisher=GNU Project}}</ref> |
||
Line 164: | Line 207: | ||
*[[MATLAB]] (full support) |
*[[MATLAB]] (full support) |
||
*[[Wolfram Language]] |
*[[Wolfram Language]] |
||
*[[Go| Golang]] (Function closures<ref>{{Cite web|url=https://go.dev/tour/moretypes/25|title=A tour of Go}}</ref>) |
|||
*[[Pawn (programming language)|Pawn]], with YSI |
|||
=== Functional languages === |
|||
===Functional languages=== |
|||
In most [[functional programming]] languages, such as Scheme, nested functions are a [[Programming idiom|common way]] of implementing [[algorithm]]s with loops in them. A simple ([[tail recursion|tail]]) [[recursion|recursive]] inner function is created, which behaves as the algorithm's main loop, while the outer function performs startup actions that only need to be done once. In more complex cases, a number of mutually recursive functions may be created as inner functions. |
In most [[functional programming]] languages, such as Scheme, nested functions are a [[Programming idiom|common way]] of implementing [[algorithm]]s with loops in them. A simple ([[tail recursion|tail]]) [[recursion|recursive]] inner function is created, which behaves as the algorithm's main loop, while the outer function performs startup actions that only need to be done once. In more complex cases, a number of mutually recursive functions may be created as inner functions. |
||
== Alternatives == |
|||
===Some languages without direct support=== |
|||
Certain languages do not have straightforward syntactic and semantic support to implement nested functions. Nevertheless, for some of them the idea of nested functions can be simulated with some degree of difficulty through the use of other language constructs. The following languages can approximate nested functions through the respective strategies: |
|||
Various alternative techniques can be used to achieve similar programming results as via nested functions. |
|||
*[[C++]] |
|||
**before C++11: allows definition of classes within classes, providing the ability to use class methods in a way similar to nested functions in '''one''' level (see [[Function object#In C and C.2B.2B|Function object in C++]]). |
|||
=== Modularity === |
|||
**since C++11: by using lambda expressions as the quicksort example above.<ref>http://www.rosettacode.org/wiki/Nested_function#C.2B.2B</ref> |
|||
*[[Eiffel (programming language)|Eiffel]] explicitly disallows nesting of routines. This is to keep the language simple, and also allows the convention of using a special variable, '''Result''', to denote the result of a (value-returning) function. |
|||
A common alternative is to leverage a language's modularity technology. Some functions are exposed for use outside of the module and some are only visible within the module. |
|||
*[[Visual Basic .NET|Visual Basic]], by using anonymous methods or lambda expressions. |
|||
*[[Java (programming language)|Java]], by using lambda expressions<ref>http://www.rosettacode.org/wiki/Nested_function#Java</ref> (see [[Anonymous function#Java|Anonymous functions in Java]]) <!--How can a lambda expression "simulate" nested scopes, in any number of levels, containing any language element (functions, variables, constants, types) ?--> (since Java 8) or through a workaround that consists in an [[anonymous class]] containing a single method. A named class declared local to a method may also be used. |
|||
In C, this can be implemented by declaring functions and variables as ''static'' to hide them from code outside the file.<ref name=cfaq>"[http://c-faq.com/misc/nestfcns.html Question 20.24: Why doesn't C have nested functions?], comp.lang.c FAQ</ref> This allows for data hiding, encapsulation and decomposition, but at a different level of [[granularity]] than with nested functions. This modularity does not support more than one level of nesting. |
|||
*[[Kotlin (programming language)|KOTLIN (Programming Language)]], Allows To Define Any Inner Fuction Inside Any Function, Easier To Implement, Easy To Understand |
|||
In [[object-oriented languages]], a class typically provides a scope in which functions and state can be hidden from consumers of the class but accessible within the class. Some languages allow classes to be nested. |
|||
=== Parameters === |
|||
To implement data hiding, functions can pass around shared data as parameters, but this increases the complexity of function calls.{{sfn|Bright|2004}} |
|||
In C, this is generally implemented by passing a pointer to a structure containing the shared data.<ref name=cfaq /> |
|||
=== Lambda === |
|||
In [[PHP]] and other languages, the [[anonymous function|lambda]] is an alternative. A function is defined in a code statement rather than declared with the usual function syntax. It has no name but is callable via a [[function reference]]. Such functions can be defined inside of a function as well as in other scopes. To use local variables in the anonymous function, use [[Closure (computer science)|closure]]. |
|||
=== Alternatives by language === |
|||
The following languages provide features that are similar to nested functions: |
|||
*[[C++]] {{endash}} classes allow for similar data hiding and encapsulation; defining a class within a class provides similar structure (see [[Function object#In C and C.2B.2B|Function object in C++]]) |
|||
*[[C++11]] and later {{endash}} via lambda expressions (see quicksort example above)<ref>{{Cite web|url=http://www.rosettacode.org/wiki/Nested_function#C.2B.2B|title = Nested function - Rosetta Code}}</ref> |
|||
*[[Eiffel (programming language)|Eiffel]] {{endash}} explicitly disallows nesting of routines to keep the language simple; does allow the convention of using a special variable, '''Result''', to denote the result of a (value-returning) function |
|||
*[[C Sharp (programming language)|C#]] and [[Visual Basic .NET|Visual Basic]] {{endash}} via lambda expressions |
|||
*[[Java (programming language)|Java]] {{endash}} since Java 8, via [[Anonymous function#Java|lambda expressions]]<ref>{{Cite web|url=http://www.rosettacode.org/wiki/Nested_function#Java|title = Nested function - Rosetta Code}}</ref><!--How can a lambda expression "simulate" nested scopes, in any number of levels, containing any language element (functions, variables, constants, types) ?-->, and in older versions, via an [[anonymous class]] containing a single method |
|||
== Implementation == |
|||
==Implementation== |
|||
{{see also|Man or boy test}} |
{{see also|Man or boy test}} |
||
Implementation of nested functions can be more involved than it may appear, as a reference to a nested function that references non-local variables creates a [[Closure (computer science)|closure]]. For this reason nested functions are not supported in some languages such as C, C++ or Java as this makes compilers more difficult to implement.<ref name=cfaq /><ref>[https://stackoverflow.com/a/1348456/2025416 answer] by Dave Vandervies, Aug 28 '09 at 17:45, to "[https://stackoverflow.com/questions/1348095/why-are-nested-functions-not-supported-by-the-c-standard Why are nested functions not supported by the C standard?]"</ref> However, some compilers do support them, as a compiler specific extension. A well known example of this is the [[GNU C]] implementation of C which shares code with compilers for languages such as Pascal, Ada and Modula. |
Implementation of nested functions can be more involved than it may appear, as a reference to a nested function that references non-local variables creates a [[Closure (computer science)|closure]]. For this reason nested functions are not supported in some languages such as C, C++ or Java as this makes compilers more difficult to implement.<ref name=cfaq /><ref>[https://stackoverflow.com/a/1348456/2025416 answer] by Dave Vandervies, Aug 28 '09 at 17:45, to "[https://stackoverflow.com/questions/1348095/why-are-nested-functions-not-supported-by-the-c-standard Why are nested functions not supported by the C standard?]"</ref> However, some compilers do support them, as a compiler specific extension. A well known example of this is the [[GNU C]] implementation of C which shares code with compilers for languages such as Pascal, Ada and Modula. |
||
===Access of non |
=== Access of non-local objects === |
||
''There are several ways to implement nested procedures in a lexically scoped language, but the classic way is as follows:'' |
|||
There are several ways to implement nested procedures in a lexically scoped language, but the classic way is as follows: |
|||
:Any [[non-local object]], X, is reached via access-links in the [[call stack|activation frames]] on the machine stack. The caller, C, assists the called procedure, P, by pushing a ''direct'' link to the ''latest'' activation of P's immediate lexical encapsulation, (P), prior to the call itself. P may then quickly find the right activation for a certain X by following a ''fixed number'' (P.depth – X.depth) of links (normally a small number). |
:Any [[non-local object]], X, is reached via access-links in the [[call stack|activation frames]] on the machine stack. The caller, C, assists the called procedure, P, by pushing a ''direct'' link to the ''latest'' activation of P's immediate lexical encapsulation, (P), prior to the call itself. P may then quickly find the right activation for a certain X by following a ''fixed number'' (P.depth – X.depth) of links (normally a small number). |
||
Line 198: | Line 269: | ||
Another way to implement nested functions that is used by some compilers is to convert ("lift") nested functions into non-nested functions (where extra, hidden, parameters replace the access links) using a process known as [[lambda lifting]] during an intermediate stage in the compilation. |
Another way to implement nested functions that is used by some compilers is to convert ("lift") nested functions into non-nested functions (where extra, hidden, parameters replace the access links) using a process known as [[lambda lifting]] during an intermediate stage in the compilation. |
||
===Functions as values=== |
=== Functions as values === |
||
In order for local functions with [[lexically scoped]] [[non-local variable|nonlocals]] to be passed as results, the language runtime code must also implicitly pass the environment (data) that the function sees inside its encapsulating function, so that it is reachable also when the current activation of the enclosing function no longer exists.<ref>Such a combination of function code and its environment is sometimes called a [[Closure (computer programming)|closure]].</ref> This means that the environment must be stored in another memory area than (the subsequently reclaimed parts of) a chronologically based execution stack, which, in turn, implies some sort of freely [[dynamic memory allocation]]. Many older Algol based languages (or dialects thereof) does therefore not allow local functions that access nonlocals to be passed as return values, or do they not allow functions as return values at all, although passing of such functions as arguments may still be possible. |
In order for local functions with [[lexically scoped]] [[non-local variable|nonlocals]] to be passed as results, the language runtime code must also implicitly pass the environment (data) that the function sees inside its encapsulating function, so that it is reachable also when the current activation of the enclosing function no longer exists.<ref>Such a combination of function code and its environment is sometimes called a [[Closure (computer programming)|closure]].</ref> This means that the environment must be stored in another memory area than (the subsequently reclaimed parts of) a chronologically based execution stack, which, in turn, implies some sort of freely [[dynamic memory allocation]]. Many older Algol based languages (or dialects thereof) does therefore not allow local functions that access nonlocals to be passed as return values, or do they not allow functions as return values at all, although passing of such functions as arguments may still be possible. |
||
===No-execute stacks=== |
=== No-execute stacks === |
||
At least one implementation of nested functions cause a loss of [[NX bit|No-execute stacks (NX stack)]]. GCC's nested function implementation calls nested functions through a [[jump instruction]] put in the machine stack at runtime. This requires the stack to be executable. |
|||
[[GNU Compiler Collection|GCC]]'s implementation of nested functions causes a loss of [[NX bit|no-execute]] [[Call stack|stacks]] (NX stacks). This implementation calls nested functions through a [[jump instruction]] placed on the machine stack at runtime. This requires the stack to be executable. No-execute stacks and nested functions are therefore mutually exclusive in GCC. If a nested function is used in the development of a program, then the NX stack is silently lost, unless GCC is called with the <code>‑Wtrampoline</code> option to alert of the condition. Software engineered using [[Software Development Security|Secure Development Lifecycle]] often do not allow the use of nested functions in this particular compiler due to the loss of NX stacks.<ref>{{cite web |
|||
Software engineered using [[Software Development Security|Secure Development Lifecycle]] often do not allow the use of nested functions in this particular compiler (GCC) due to the loss of NX Stacks.<ref>{{cite web |
|||
| url = http://www.owasp.org/index.php/C-Based_Toolchain_Hardening#GCC.2FBinutils |
| url = http://www.owasp.org/index.php/C-Based_Toolchain_Hardening#GCC.2FBinutils |
||
| title = C-Based Toolchain Hardening |
| title = C-Based Toolchain Hardening |
||
Line 215: | Line 284: | ||
}}</ref> |
}}</ref> |
||
==See also== |
== See also == |
||
*[[Call stack]] |
*[[Call stack]] |
||
*[[Closure (computer science)]] |
*[[Closure (computer science)]] |
||
*[[Function composition (computer science)]] |
|||
*[[Inner class]] |
*[[Inner class]] |
||
*[[Nesting (computing)]] |
*[[Nesting (computing)]] |
||
== |
== References == |
||
{{notelist}} |
|||
==References== |
|||
{{reflist}} |
{{reflist}} |
||
{{refbegin}} |
{{refbegin}} |
Revision as of 10:50, 27 October 2024
In computer programming, a nested function (or nested procedure or subroutine) is a named function that is defined within another, enclosing, block and is lexically scoped within the enclosing block – meaning it is only callable by name within the body of the enclosing block and can use identifiers declared in outer blocks, including outer functions. The enclosing block is typically, but not always, another function.
Programming language support for nested functions varies. With respect to structured programming languages, it is supported in some outdated languages such as ALGOL, Simula 67 and Pascal and in the commonly used JavaScript. It is commonly supported in dynamic and functional languages. However, it is not supported in some commonly used languages including standard C and C++.
Other programming technologies provide similar benefit. For example, a lambda function also allows for a function to be defined inside of a function (as well as elsewhere) and allows for similar data hiding and encapsulation. Notably, a lambda function has no name (is anonymous) and therefore cannot be called by name and has no visibility aspect.
Attributes
The scope of a nested function is the block that contains it – be it a function block or block within a function body. It is not visible (cannot be called by name) outside its containing block.
A nested function can use identifiers (i.e. the name of functions, variables, types, classes) declared in any enclosing block, except when they are masked by inner declarations with the same names.
A nested function can be declared within a nested function, recursively, to form a deeply nested structure. A deeply nested function can access identifiers declared in all of its enclosing blocks, including enclosing functions.
Nested functions may in certain situations lead to the creation of a closure. If it is possible for the nested function to escape the enclosing function, for example if functions are first class objects and a nested function is passed to another function or returned from the enclosing function, then a closure is created and calls to this function can access the environment of the original function. The frame of the immediately enclosing function must continue to be alive until the last referencing closure dies and non-local automatic variables referenced in closures can therefore not be stack allocated in languages that allow the closure to persist beyond the lifetime of the enclosing block. This is known as the funarg problem and is a key reason why nested functions was not implemented in some simpler languages as it significantly complicates code generation and analysis, especially when functions are nested to various levels, sharing different parts of their environment.
Value
The nested function technology allows a programmer to write source code that includes beneficial attributes such as information hiding, encapsulation and decomposition. The programmer can divide a task into subtasks which are only meaningful within the context of the task such that the subtask functions are hidden from callers that are not designed to use them.
Block scoping allows functions to share the state of enclosing blocks (including enclosing functions) without passing parameters or using global variables.[1]
Uses
Helper
A nested function typically acts as a helper function or a recursive function (as in the quicksort example above).
Control flow
Nested functions can be used for unstructured control flow, by using the return statement for general unstructured control flow. This can be used for finer-grained control than is possible with other built-in features of the language – for example, it can allow early termination of a for loop if break
is not available, or early termination of a nested for loop if a multi-level break
or exceptions are not available.
Higher-order functions
In some languages, it is possible to create a nested function that accesses a set of parameters from the outer function, that is a closure, and have that function be the outer function's return value. Thus it is possible to return a function that is set to fulfill a certain task with little or no further parameters given to it, which can increase performance quite significantly.[2]
Examples
Simple example
A simple example in Pascal:
function E(x: real): real;
function F(y: real): real;
begin
F := x + y
end;
begin
E := F(3) + F(4)
end;
The function F
is nested within E
. Note that E
's parameter x
is also visible in F
(as F
is a part of E
) while both x
and y
are invisible outside E
and F
respectively.
Similarly, in Standard ML:
fun e (x : real) =
let
fun f y = x+y
in
f 3 + f 4
end;
In Haskell:
e :: Float -> Float
e x = f 3 + f 4 where f y = x + y
In PL/I:
e: procedure(x) returns(float); declare x float; f: procedure(y) returns(float); declare y float; return x + y end; return f(3.0) + f(4.0); end;
In Python:
def e(x: float) -> float:
def f(y: float) -> float:
return x + y
return f(3.0) + f(4.0)
In GNU C[3] – which extends standard C with nested functions:
float E(float x)
{
float F(float y)
{
return x + y;
}
return F(3) + F(4);
}
Quicksort
The following is an implementation of quicksort:[4]
void sort(int *items, int size) {
void quickSort(int first, int last) {
void swap(int p, int q) {
int tmp = items[p];
items[p] = items[q];
items[q] = tmp;
}
int partition() {
int pivot = items[first], index = first;
swap(index, last);
for (int i = first; i < last; i++)
if (items[i] < pivot)
swap(index++, i);
swap(index, last);
return index;
}
if (first < last) {
int pivotIndex = partition();
quickSort(first, pivotIndex - 1);
quickSort(pivotIndex + 1, last);
}
}
quickSort(0, size - 1);
}
The following is an implementation of the Hoare partition based quicksort using C++11 lambda expression syntax which is an alternative technology that also allows hiding a function inside a function:
template<typename RandomAccessIterator>
auto Sort(RandomAccessIterator Begin, RandomAccessIterator End)->void {
auto Partition = [&]() {
//Hoare partition scheme
auto &Pivot = *Begin;
auto ForwardCursor = Begin;
auto BackwardCursor = End - 1;
auto PartitionPositionFound = false;
auto LocatePartitionPosition = [&]() {
while (*ForwardCursor < Pivot)
++ForwardCursor;
while (Pivot < *BackwardCursor)
--BackwardCursor;
if (ForwardCursor >= BackwardCursor)
PartitionPositionFound = true;
else
Swap(*ForwardCursor, *BackwardCursor);
};
//Trivial helper function
auto MoveOnAndTryAgain = [&]() {
++ForwardCursor;
--BackwardCursor;
};
//Brief outline of the actual partition process
while (true) {
LocatePartitionPosition();
if (PartitionPositionFound)
return BackwardCursor + 1;
else
MoveOnAndTryAgain();
}
};
//Brief outline of the quicksort algorithm
if (Begin < End - 1) {
auto PartitionPosition = Partition();
Sort(Begin, PartitionPosition);
Sort(PartitionPosition, End);
}
}
Languages
Notable languages supporting nested functions include:
- ALGOL-based languages such as ALGOL 68, Simula, Pascal, Modula-2, Modula-3, Oberon, PL/I, Seed7 and Ada
- Modern versions of Lisp (with lexical scope) such as Scheme, and Common Lisp
- ECMAScript (JavaScript and ActionScript)
- Dart[5]
- Kotlin (local functions[6])
- Rust
- Scala (nested functions[7])
- Various degrees of support in scripting languages such as Ruby, Python, Lua, PHP and Perl
- GCC supports nested functions in C, as a language extension.[8]
- C#, starting with C# 7.0
- The D language, a C-related language with nested functions.
- Fortran, starting with Fortran-90, supports a single level of nested (CONTAINed) subroutines and functions.
- MATLAB (full support)
- Wolfram Language
- Golang (Function closures[9])
Functional languages
In most functional programming languages, such as Scheme, nested functions are a common way of implementing algorithms with loops in them. A simple (tail) recursive inner function is created, which behaves as the algorithm's main loop, while the outer function performs startup actions that only need to be done once. In more complex cases, a number of mutually recursive functions may be created as inner functions.
Alternatives
Various alternative techniques can be used to achieve similar programming results as via nested functions.
Modularity
A common alternative is to leverage a language's modularity technology. Some functions are exposed for use outside of the module and some are only visible within the module.
In C, this can be implemented by declaring functions and variables as static to hide them from code outside the file.[10] This allows for data hiding, encapsulation and decomposition, but at a different level of granularity than with nested functions. This modularity does not support more than one level of nesting.
In object-oriented languages, a class typically provides a scope in which functions and state can be hidden from consumers of the class but accessible within the class. Some languages allow classes to be nested.
Parameters
To implement data hiding, functions can pass around shared data as parameters, but this increases the complexity of function calls.[1]
In C, this is generally implemented by passing a pointer to a structure containing the shared data.[10]
Lambda
In PHP and other languages, the lambda is an alternative. A function is defined in a code statement rather than declared with the usual function syntax. It has no name but is callable via a function reference. Such functions can be defined inside of a function as well as in other scopes. To use local variables in the anonymous function, use closure.
Alternatives by language
The following languages provide features that are similar to nested functions:
- C++ – classes allow for similar data hiding and encapsulation; defining a class within a class provides similar structure (see Function object in C++)
- Eiffel – explicitly disallows nesting of routines to keep the language simple; does allow the convention of using a special variable, Result, to denote the result of a (value-returning) function
- C# and Visual Basic – via lambda expressions
- Java – since Java 8, via lambda expressions[12], and in older versions, via an anonymous class containing a single method
Implementation
Implementation of nested functions can be more involved than it may appear, as a reference to a nested function that references non-local variables creates a closure. For this reason nested functions are not supported in some languages such as C, C++ or Java as this makes compilers more difficult to implement.[10][13] However, some compilers do support them, as a compiler specific extension. A well known example of this is the GNU C implementation of C which shares code with compilers for languages such as Pascal, Ada and Modula.
Access of non-local objects
There are several ways to implement nested procedures in a lexically scoped language, but the classic way is as follows:
- Any non-local object, X, is reached via access-links in the activation frames on the machine stack. The caller, C, assists the called procedure, P, by pushing a direct link to the latest activation of P's immediate lexical encapsulation, (P), prior to the call itself. P may then quickly find the right activation for a certain X by following a fixed number (P.depth – X.depth) of links (normally a small number).
- The caller creates this direct link by (itself) following C.depth – P.depth + 1 older links, leading up to the latest activation of (P), and then temporarily bridging over these with a direct link to that activation; the link later disappears together with P, whereby the older links beneath it may come into use again.
- Note that P is visible for, and may therefore be called by, C if (P) = C / (C) / ((C)) / etc.
This original method is faster than it may seem, but it is nevertheless often optimized in practical modern compilers (using displays or similar techniques).
Another way to implement nested functions that is used by some compilers is to convert ("lift") nested functions into non-nested functions (where extra, hidden, parameters replace the access links) using a process known as lambda lifting during an intermediate stage in the compilation.
Functions as values
In order for local functions with lexically scoped nonlocals to be passed as results, the language runtime code must also implicitly pass the environment (data) that the function sees inside its encapsulating function, so that it is reachable also when the current activation of the enclosing function no longer exists.[14] This means that the environment must be stored in another memory area than (the subsequently reclaimed parts of) a chronologically based execution stack, which, in turn, implies some sort of freely dynamic memory allocation. Many older Algol based languages (or dialects thereof) does therefore not allow local functions that access nonlocals to be passed as return values, or do they not allow functions as return values at all, although passing of such functions as arguments may still be possible.
No-execute stacks
GCC's implementation of nested functions causes a loss of no-execute stacks (NX stacks). This implementation calls nested functions through a jump instruction placed on the machine stack at runtime. This requires the stack to be executable. No-execute stacks and nested functions are therefore mutually exclusive in GCC. If a nested function is used in the development of a program, then the NX stack is silently lost, unless GCC is called with the ‑Wtrampoline
option to alert of the condition. Software engineered using Secure Development Lifecycle often do not allow the use of nested functions in this particular compiler due to the loss of NX stacks.[15]
See also
- Call stack
- Closure (computer science)
- Function composition (computer science)
- Inner class
- Nesting (computing)
References
- ^ a b Bright 2004.
- ^ Higher-Order Functions and Lambdas - Kotlin Programming Language
- ^ Rothwell, Trevis J. (2011). The GNU C Reference Manual. Free Software Foundation, Inc. p. 63.
- ^ Re: Nesting functions- Why?[usurped], baavgai[usurped], 14 January 2012
- ^ "A tour of the Dart language".
- ^ "Functions | Kotlin".
- ^ "Nested Methods".
- ^ "Nested Functions – Using the GNU Compiler Collection (GCC)". GNU Project. Retrieved 2007-01-06.
- ^ "A tour of Go".
- ^ a b c "Question 20.24: Why doesn't C have nested functions?, comp.lang.c FAQ
- ^ "Nested function - Rosetta Code".
- ^ "Nested function - Rosetta Code".
- ^ answer by Dave Vandervies, Aug 28 '09 at 17:45, to "Why are nested functions not supported by the C standard?"
- ^ Such a combination of function code and its environment is sometimes called a closure.
- ^ Walton, Jeffrey. "C-Based Toolchain Hardening". The Open Web Application Security Project (OWASP). Retrieved 28 February 2017.
- Bright, Walter (1 May 2004). "Nested Functions". Dr. Dobb's.
External links
- comp.lang.c FAQ: Nested Functions
- "6.4 Nested procedure and functions". FreePascal documentation.