Local, Nested and Private Functions

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the section bibliography.

Written against MATLAB R2026b. The sessions below were run in GNU Octave 11.3.0 (MATLAB code that Octave also accepts) and show Octave’s output with blank lines omitted; the texts of the error messages that MATLAB prints differ and are not run here. Local functions in scripts are described on Scripts and Code Sections; the basics of function files are on Functions.

A program file can hold more than one function, and a folder can hide helpers in a subfolder. The three forms below differ in who may call the helper and in what data it can see: a local function is visible to its own file, a nested function additionally shares the variables of the functions that contain it, and a private function is visible to the folder above its private subfolder. Keeping helpers out of the public interface is the point: the file names on the path stay few and meaningful, and a helper can be changed without breaking callers elsewhere.

Local functions

In a function file, the first function is the main function and the others are local functions (called subfunctions in older releases). Local functions can follow the main function in any order, and only the functions of the same file can call them. They are the usual way to split a task into steps.

function [T, nIter] = relaxPlate(T, tol)
% RELAXPLATE  Jacobi sweeps on the interior of T until the update is below tol.
    nIter = 0;
    delta = Inf;
    while delta > tol && nIter < 1000
        Tnew = sweep(T);
        delta = maxChange(Tnew, T);
        T = Tnew;
        nIter = nIter + 1;
    end
end

function Tnew = sweep(T)
    Tnew = T;
    Tnew(2:end-1, 2:end-1) = (T(1:end-2, 2:end-1) + T(3:end, 2:end-1) ...
        + T(2:end-1, 1:end-2) + T(2:end-1, 3:end)) / 4;
end

function d = maxChange(A, B)
    d = max(abs(A(:) - B(:)));
end
T = zeros(5); T(:,1) = 100;
[T, n] = relaxPlate(T, 1e-3);
n
round(T(3,:)*100)/100
try, sweep(T), catch err, disp(err.message), end

Octave 11.3.0 output:

n = 28
ans =
   100.0000    52.6800    25.0000     9.8200          0
'sweep' undefined near line 5, column 6

The main function relaxPlate is public: it is the name of the file. sweep and maxChange are local: calling sweep from the Command Window or from another file fails with an undefined-function error (identifier MATLAB:UndefinedFunction, wording varies by release, not run here).

Rules and properties of local functions:

  • They have their own workspaces. A local function cannot see the variables of the main function or of other local functions; everything it needs is passed as an input.

  • They take precedence over functions with the same name in other files, because MATLAB looks for local functions before other main functions. A file can therefore carry its own version of a helper while the original stays untouched elsewhere (see Function Precedence and Name Resolution).

  • end is required if any function in the file contains a nested function, and in scripts. In function files with local functions only, either style works as long as it is used consistently; use end always.

  • Help is available through the file. help relaxPlate>sweep prints the help text of the local function: the file name, a > character and the local name.

  • localfunctions returns a cell array of function handles to all local functions of the calling file. Test files written as function-based unit tests use it so that the framework can find the tests (see Unit Testing with matlab.unittest).

  • Local functions in scripts (R2016b) can be anywhere in the script since R2024a, but not inside if, for or other conditional contexts. Before R2024a they had to be at the end.

  • A local function need not validate its inputs: the caller is in the same file, so argument validation (see Argument Validation) is usually left to the main function.

Nested functions

A nested function is completely contained within another function, its parent. Its distinguishing property is that it reads and writes the variables of its parents without receiving them as inputs. That makes nested functions the tool for callbacks that share state and for functions that return a handle with data captured inside it.

function countSteps
    count = 0;
    step(); step(); step();
    fprintf("count = %d\n", count);

    function step
        count = count + 1;
    end
end

Here step increments count, which belongs to the workspace of countSteps. The variable need not appear in any argument list.

Octave 11.3.0 output:

count = 3

Rules for nested functions

  • Every function in the file needs end. A file that contains a nested function must close all of its functions with end.

  • They cannot appear inside control statements such as if, switch, for, while or try.

  • Call them by name or through a handle created with @. feval and str2func do not work with a nested function.

  • The workspace is static. All variables of a nested function and of its parents must be defined explicitly in the code. A nested function cannot call a script, eval, load or evalin to create new variables, and the parent cannot either; such code fails because the variable does not already exist in the workspace (MathWorks documents this error as "Attempt to Add Variable to a Static Workspace").

  • Argument validation (arguments blocks) is not allowed in nested functions, per the argument validation documentation.

  • A shared variable is shared only if the parent uses it. If the parent function never assigns or refers to a name that two sibling nested functions each assign, every sibling gets its own local variable with that name and they cannot interact.

  • A variable is shared whether it is assigned before or after the nested definition. The position of the nested function in the file does not matter; the parent’s workspace includes its inputs, outputs and all variables assigned anywhere in the parent.

  • Outputs of a nested function are not in the parent’s workspace unless the parent requests them (z = nestedFcn;).

Handles that carry data

When a parent returns a handle to a nested function, the handle stores not only the function name but also the values of the parent variables that the nested function uses. Each call to the parent creates a new set of such variables, so several handles from the same parent are independent. The captured variables are cleared when the last handle created by that call is cleared.

function h = makeLine(slope, offset)
    h = @line;

    function y = line(x)
        y = slope * x + offset;
    end
end
f = makeLine(2, 1);
f(10)

Octave 11.3.0 output:

ans = 21

In the heatflow scenario this is how a boundary condition can carry its parameters (a fixed temperature, a convection coefficient) into the solver as a one-argument function. Anonymous functions (see Anonymous Functions and Function Handles) do the same for a single expression, with the difference that an anonymous function captures a snapshot of the variables at creation time, while a handle to a nested function shares the live variables of its parent.

Visibility among nested functions

A nested function is visible to:

  • the function that contains it (one level up);

  • the other functions nested at the same level in the same parent;

  • the functions nested in those siblings (any lower level).

So a function cannot call a function nested more than one level below it. If A contains B and D, and C is nested in B and E in D, then A can call B and D but not C or E; B can call D (and D can call B); C can call B and D but not E. Other files cannot call any of them. The easiest way to extend the reach of a nested function is to return a handle to it, as above. Only a function that can call a nested function can create a handle to it. The diagram below shows these rules together with those for local and private functions.

Private functions

A function stored in a subfolder named private is visible only to the functions and scripts in the folder immediately above it (and to the other functions in the same private folder). It cannot be called from the Command Window or from any other folder. Do not add the private folder to the path.

The sessions below use a folder named p2, under the current folder, that contains limitTemp.m and a private subfolder with clampTemp.m. The session adds p2 (not p2/private) to the path, building the folder name with fullfile.

function y = clampTemp(x, lo, hi)
% CLAMPTEMP  Private helper: limit x to the interval [lo, hi].
    y = min(max(x, lo), hi);
end
function y = limitTemp(x)
% LIMITTEMP  Public function that uses a private helper.
    y = clampTemp(x, 0, 100);
end
addpath(fullfile(pwd, "p2"));
limitTemp([-5 50 120])
try, clampTemp(5, 0, 1), catch err, disp(err.message), end

Octave 11.3.0 output:

ans =
     0    50   100
'clampTemp' undefined near line 3, column 6

Properties of private functions:

  • They have precedence over public functions of the same name, so MATLAB finds a private test.m before a non-private test.m. A folder can carry its own version of a function while the original stays elsewhere.

  • Help is available: help private/clampTemp, when the current folder is the parent.

  • They are plain function files, with their own workspace and any number of local functions. The tools that work with files (the Editor, the debugger, which) treat them as files in a folder.

  • They cannot be tested directly from outside the parent folder, so tests exercise them through the public functions that call them.

  • Classes and namespaces have their own folder forms (@class, +namespace); see Namespaces and Class Folders.

Which function can call which

The diagram uses one folder with a public file, a second public file, a private subfolder and the command line. Solid arrows are calls that work; dashed arrows are calls that fail.

flowchart LR CL["Command Window and functions in other folders"] subgraph folder["Folder on the path"] PUB["otherFile.m"] subgraph file["File solver.m"] MAIN["main function solver"] LOC1["local function sweep"] LOC2["local function maxChange"] N1["function step nested in solver"] end subgraph priv["private subfolder"] P1["clampTemp.m"] P2["scaleTemp.m"] end end CL -->|"calls"| MAIN PUB -->|"calls"| MAIN MAIN -->|"calls"| LOC1 LOC1 -->|"calls"| LOC2 MAIN -->|"calls"| N1 N1 -->|"calls"| LOC1 PUB -->|"calls"| P1 MAIN -->|"calls"| P1 LOC1 -->|"calls"| P2 P1 -->|"calls"| P2 CL -.->|"fails"| LOC1 PUB -.->|"fails"| LOC2 CL -.->|"fails"| P1

The rows of the table summarise the same rules.

Caller Can call a local function Can call a nested function Can call a private function

Command Window, function in another folder

No

No (only through a returned handle)

No

Another file in the same folder

No

No (only through a returned handle)

Yes

Another function in the same file

Yes

If it is the parent, a sibling or a function nested below them

Yes, if the file is in the parent folder

A nested function

Yes (local functions of the file)

Yes, under the visibility rules above

Yes, if the file is in the parent folder

A function in the private folder

No (it is in another file)

No

Yes, the other functions of the same private folder

When to use each

Form Use it when Avoid it when

Local function

A helper belongs to one file and needs only the values passed in. This is the default way to break a function into steps.

Several files need the helper, or the helper is large enough to deserve its own tests and help.

Nested function

Callbacks, or a returned handle, must share state with the function that created them. App Designer apps and programmatic UIs use this form.

The state is large or the sharing is not essential. Shared variables hide data flow; passing inputs and outputs is clearer.

Private function

Several public functions in a folder share a helper that should not be public. The helper is large enough for its own file.

Only one file uses it (a local function is simpler), or the helper is part of the public interface.

Public function in its own file

Callers outside the folder need it.

The helper would clutter the path or the namespace with a name that nobody should call.

A good order of preference is: pass data explicitly with a local function, use a private function when more than one file shares it, and use a nested function only when the shared state is the point.

Pitfalls

  • Calling a local function from the command line. It is not visible; test it through the main function, or temporarily move it to its own file.

  • Sibling nested functions that assign the same name each get their own variable unless the parent also uses the name.

  • Forgetting end in a file with a nested function. A single function without end makes the file invalid.

  • Using eval, load or a script inside a nested function or its parent to create variables, which fails because the workspace is static. Load into a structure instead (S = load("plate.mat")).

  • A private function that shadows a public one. The private version wins inside the parent folder; which -all name shows the order.

  • Calling a nested function by feval("name") or str2func("name"). Use a direct call or an @ handle.

  • Large parent workspaces captured by handles. The handle keeps the variables the nested function uses alive; clear the handle when it is no longer needed.

Where older books differ

Older books call local functions subfunctions and show them only in function files; the term local function and local functions in scripts (R2016b, anywhere in the script since R2024a) are newer. They describe the private folder as it is today. Nested functions are covered thinly in older books, which tend to use global variables to share data between callbacks; nested functions or object properties are the modern alternatives (see Workspaces, Scope, Global and Persistent). Argument validation (R2019b) is available in main and local functions but not in nested functions.