Anonymous Functions and Function Handles

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. Function handles and anonymous functions are old features of the language and nothing on this page needs a recent release. The snippets marked "Octave 11.3.0 output" were run in GNU Octave 11.3.0, which accepts the same syntax, and Octave’s printed layout and some message texts differ from MATLAB’s, as noted where it matters. Snippets labelled "not run here" are checked line by line against the R2026b documentation and show no invented output.

A function handle is a MATLAB data type (class function_handle) that stores an association to a function, so that the function can be passed around, stored and called later. It is the way MATLAB passes behaviour as data: the "function functions" such as fzero, integral, ode45 and fminsearch take handles, callbacks of graphical interfaces are handles, and arrayfun and cellfun apply a handle to every element. An anonymous function is a handle to a one-line expression that lives in a variable and needs no file. This page is the foundation for Integration, ODEs and Optimization, Local, Nested and Private Functions and the callbacks that appear in the graphical-interface pages. The files, signatures and workspaces that the handles point to are on Functions.

Function handles

To create a handle to a function, precede its name with @. The function can be a built-in, a function in a program file, or a local function of the current file. A handle is called with the same syntax as the function itself:

h = @sin;
h(pi/2)
class(h)
isa(h, 'function_handle')

A handle to a function that takes no inputs is called with empty parentheses; without them the statement just copies the handle and calls nothing. This is the same trap as a bare function name in a script: d = h copies, d = h() calls.

f = @() disp('hello');
f()
g = f;          % copies the handle, nothing is called

Function handles are always scalars: a handle cannot be an element of a numeric array. To keep several of them together, use a cell array or a structure; the documentation shows both, C = {@sin, @cos, @tan}; C{2}(pi) and S.a = @sin; S.a(pi/2). Chained indexing such as C{2}(pi) selects the handle first and then calls it.

A handle to a named function remembers which function it means at creation time. The function has to be in scope then (on the path, in the current folder, or a local or nested function of the current file), and MATLAB resolves the name with the same precedence rules as a normal call (the rules are on Function Precedence and Name Resolution). The documentation also says that a handle to a file-based function stores the absolute path, so it still works after the current folder changes, but a handle saved in a MAT-file becomes invalid if the file is moved or renamed: invoking an invalid handle is an error.

Anonymous functions

An anonymous function is created with h = @(arglist) expression. It has these properties, all from the documentation:

  • it consists of one executable expression; there are no statements, no assignments and no if, for or end blocks;

  • it can take any number of inputs, separated by commas, and returns one output, the value of the expression;

  • it can take no inputs (@() expression), and must then be called with ();

  • it is stored in a variable of class function_handle, needs no file, and can also be written directly as an argument, as in integral(@(x) x.^2, 0, 1).

sq = @(x) x.^2;
sq(3)
g = @(x,y) x + y;
g(2,3)
nargin(g)

Octave 11.3.0 output (the matrix layout is Octave’s):

ans = 9
ans = 5
ans = 2

One output only. If the expression calls a function that can return several outputs, the anonymous function still returns what the caller asks for: the documentation shows mygrid = @(x,y) ndgrid(-x:x/c:x, -y:y/c:y) called as [x,y] = mygrid(pi,2*pi), and the deal function can spread one value over several outputs. What an anonymous function cannot do is run several statements or assign to a variable. When the body needs more than one line, write a local function (see Handles to local functions).

Operators work elementwise only if you write them so. @(x) x^2 squares a matrix; @(x) x.^2 squares each element. Function functions such as integral call the handle with vectors, so the elementwise operators (.^, .*, ./) are normally what you want; fzero and fminsearch call it with scalars. The same rule applies to the array-based arrayfun. See Operators and Precedence.

Handles can contain handles. The documentation builds a parameterised integral by nesting: g = @(c) integral(@(x) x.^2 + c*x + 1, 0, 1) is a function of c that integrates a function of x; g(2) is 2.3333.

Captured variables: snapshot semantics

The most important property of an anonymous function is what it does with the variables in its body that are not its own inputs. When the handle is created, MATLAB looks at the current workspace, takes the values of those variables, and stores them in the handle. The handle does not see the workspace later: the documentation says the values persist in the handle even if you clear the variables, and that to supply different values you must create a new handle.

a = 2;
p = @(x) a*x;
a = 10;
p(5)            % uses the a that existed when p was created
func2str(p)
clear a
p(5)            % still works: the value is stored in the handle

Octave 11.3.0 output (first call; the text of func2str differs in spacing between Octave and MATLAB, see below):

ans = 10
ans = @(x) a * x
ans = 10

The value of a that the handle uses is the value at creation time, 2, so p(5) is 10 and not 50; clearing a afterwards changes nothing. The consequences are the pitfalls of this page:

  • Changing the parameter later has no effect. To change a captured value, rebuild the handle: p = @(x) a*x; again. This is the same rule as the documentation’s note for fzero: "to change the values of the parameters, you must create a new anonymous function".

  • Only explicit variables are captured. The body is scanned for names, so a variable that is created by eval, evalin, assignin or load and is not written in the body is not found, and invoking the handle then raises an error. The documentation tells you to avoid these functions inside anonymous functions. In the same way str2func of the text of an anonymous function (see below) captures nothing.

  • Captured arrays travel with the handle. A handle stores the values it captured, so capturing a large matrix only to look up a few values keeps all of it alive; pass the matrix as an argument, or capture only what is needed.

  • Names that are undefined at creation time are not an error until the handle is called. A typo in a function name inside the body shows up on the first call (Octave 11.3.0 reports 'undefined_thing' undefined; MATLAB raises MATLAB:UndefinedFunction, not run here).

  • The handle is saved with its captured values when it is stored with save, and restored by load; the documentation shows save myfile.mat parabola.

The introspection function functions shows what is captured (see below), and is the debugging tool when a handle returns a surprising number.

sequenceDiagram participant W as Base workspace participant H as Handle p W->>W: a = 2 W->>H: p = @(x) a*x copies a = 2 W->>W: a = 10 W->>H: p(5) H-->>W: 10, using the stored a = 2

Converting and inspecting handles: func2str, str2func, functions, feval

  • c = func2str(fh) returns the function name for a handle to a named function ('cos'), and the text of an anonymous function otherwise. MATLAB’s reference page shows the anonymous text in the compact form written without a space after the argument list; Octave prints its own spacing (@(x) a * x), so do not compare these strings across the two systems.

  • fh = str2func(str) builds a handle from a function name or from the text of an anonymous function. The documentation warns that handles made with str2func do not have access to variables outside their own workspace, nor to nested functions; and that if you give the text of an anonymous function, the handle has no access to private or local functions. Use it for names read from a configuration file, not to capture local variables.

  • s = functions(fh) returns a structure with the function name, type and file, and for an anonymous function a workspace field with the captured values (s.workspace{1}). The page says to use functions for querying and debugging only, not programmatically, because its behaviour could change in later releases.

  • feval(fun, x1, ..., xM) calls a function given by name or by handle. With a handle it is equivalent to calling the handle directly, so feval is only needed when fun is a name (a character vector or string) that you have in a variable.

q = str2func('@(x) x.^3');
q(2)
feval(@plus, 1, 2)
feval('max', [4 9 2])
f = functions(@sin)

Octave 11.3.0 output (functions prints a structure; MATLAB’s fields and display differ):

ans = 8
ans = 3
ans = 9
f =

  scalar structure containing the fields:

    function = sin
    type = simple
    file =

Parameterising function functions

The function functions expect a handle with exactly the number of inputs they supply: fzero and integral call a function of one variable. To solve a problem that has extra parameters, close over them. The documentation shows two ways: an anonymous function that uses the parameters from the workspace, and a nested function that shares its parent’s workspace. A third way, a wrapper @(x) cubicpoly(x,b,c) around a general function, is common when the function lives in a file.

The heatflow scenario has a decaying temperature mode on a plate of length L with diffusivity alpha. The amplitude of the slowest mode is exp(-alpha*(pi/L)^2*t). How long until it halves, and how does the profile look at one minute?

alpha = 1.1e-4;          % diffusivity in m^2/s
L = 0.5;                 % plate length in m
amp = @(t) exp(-alpha*(pi/L)^2*t);     % amplitude of the slowest mode
tHalf = fzero(@(t) amp(t) - 0.5, [0 1000]);
fprintf('half-amplitude time: %.2f s\n', tHalf)
alpha = 5e-4;            % changing alpha later does not change amp
fprintf('amp(100) is still %.4f\n', amp(100))
profile = @(x,t) sin(pi*x/L) .* amp(t);
x = linspace(0, L, 5);
fprintf('%.4f ', profile(x, 60)); fprintf('\n')
rate = @(t,T) -alpha*(pi/L)^2 * T;
[t, T] = ode45(rate, [0 600], 1);
fprintf('ode45 final %.3e, exact %.3e\n', T(end), exp(-alpha*(pi/L)^2*600))

Octave 11.3.0 output:

half-amplitude time: 159.61 s
amp(100) is still 0.6477
0.0000 0.5449 0.7706 0.5449 0.0000
ode45 final 7.241e-06, exact 7.185e-06

Three things are visible. The root 159.61 is found by fzero on an anonymous function that itself calls another handle (amp), which has captured alpha and L. The reassignment alpha = 5e-4 did not change amp, because its alpha was copied when it was created (the second line shows 0.6477 is unchanged), but it did change rate, which was created afterwards. And ode45 receives a handle with two inputs, the time and the state, exactly as it requires; the ODE solution at 600 s is close to the exact value (the difference is the solver’s default tolerance, ode45 is not exact). The solvers and their options are on Integration, ODEs and Optimization; MATLAB’s integral, fzero, fminsearch and ode45 are in base MATLAB and need no toolbox.

Nested functions as the alternative. A nested function shares the workspace of its parent, so a handle to it sees the current value of the parent’s variables, not a snapshot. The documentation example is a file findzero.m whose nested function poly(x) uses the parent’s b and c. It is not run here (it is a function file); the semantics differ from an anonymous function and this is why it matters:

function y = findzero(b, c, x0)
y = fzero(@poly, x0);
    function y = poly(x)
        y = x^3 + b*x + c;
    end
end

Not run here, from the MATLAB documentation page. findzero(2, 3.5, 0) returns -1.0945 there. If the parent changes b after creating @poly and before the call, the nested function sees the new value; an anonymous function does not. See Local, Nested and Private Functions.

Applying handles to arrays and cells: arrayfun and cellfun

arrayfun(fun, A) calls fun once for each element of an array, and cellfun(fun, C) once for each cell of a cell array, so that a handle replaces a loop. By default the results must be scalars of one class (UniformOutput is true) and are collected in an array. Use 'UniformOutput', false to get a cell array of results of any size, and 'ErrorHandler' to supply a handle called with the error information (and the inputs) when fun fails, so that one bad element does not stop the whole call.

v = arrayfun(@(n) n^2, 1:4)
c = cellfun(@numel, {'ab','cde',''})
d = cellfun(@(s) [s '!'], {'a','bc'}, 'UniformOutput', false)

Octave 11.3.0 output:

v =

    1    4    9   16

c =

   2   3   0

d =
{
  [1,1] = a!
  [1,2] = bc!
}

A cell array of handles can be applied in the same way: with c = {@sin, @cos, @(x) x.^2}, cellfun(@(f) f(0.5), c) evaluates every function at 0.5 (Octave: 0.4794 0.8776 0.2500). This is a pattern for a table of methods, such as the three heatflow boundary conditions as handles indexed by name in a structure.

arrayfun and cellfun are a matter of clarity, not speed: a handle is called once per element, and a vectorised expression on the whole array is normally the better choice (measure with timeit) (see Vectorization and Preallocation). The error message from a non-scalar result with UniformOutput true is a classic: the fix is to add 'UniformOutput', false (its identifier and wording vary by release; not run here).

Handles to local functions

A handle to a local function (a function defined after the main function in a file, or at the end of a script) can be created inside the file and returned or passed out, which is the way to call a local function from outside, as the documentation points out. The handle works wherever it goes, even though the local function cannot be called by name from other files. The documentation’s functionsExample returns a handle to a local and a nested function; functions reports their type and file. In a function file:

function h = stepper(scheme)
% STEPPER Return a handle to a time-stepping rule chosen by name.
switch scheme
    case "explicit"
        h = @explicitStep;
    case "damped"
        h = @(T, dt) dampedStep(T, dt, 0.9);
end
end

function T = explicitStep(T, dt)
T = T + dt;
end

function T = dampedStep(T, dt, w)
T = T + w*dt;
end

Not run here (a function file with local functions; the MATLAB file layout is the point). h = stepper("explicit"); h(1, 0.1) gives 1.1000; the handle to the local explicitStep is valid outside stepper.m, and the anonymous function keeps 0.9 as a literal. Scripts can define local functions too, since R2016b: before R2024a they had to be at the end of the script file (see Scripts and Code Sections).

Precedence. A handle to a name is resolved at creation by the normal precedence rules: a variable named sin does not become a handle to the function, and a local function with the same name as a path function wins inside the file. See Function Precedence and Name Resolution.

Overloading. When a handle is called with an object as the dominant argument, MATLAB invokes the object’s class method of the same name instead, if there is one (the documentation says so in the section on creating handles). This is how a handle such as @size called with an object of a class that defines its own size method ends up in that method.

Comparing handles

Handles to the same named function compare as equal with isequal, and a handle copied from another is equal to it. Two anonymous functions that were typed separately with identical text are different handles with their own captured data. Octave 11.3.0 gives these results (isequal of two handles that are not copies is not specified on the pages consulted for MATLAB, so verify it on your release before relying on it, not run here):

h  = @twice;  h2 = @twice;
isequal(h, h2)                    % same named function
s1 = @(x) x+1;  s2 = @(x) x+1;
isequal(s1, s2)                   % separately typed anonymous functions
s3 = s1;
isequal(s1, s3)                   % a copy

Octave 11.3.0 output: ans = 1, ans = 0 and ans = 1. A comparison of two anonymous functions is therefore not a way to test whether they compute the same thing; compare func2str text if that is what you mean, or call both on a test input.

Pitfalls

Pitfall What happens What to do

A captured variable is changed later

The handle keeps the value from creation time

Rebuild the handle, or pass the value as an argument

f = h where h takes no inputs

The assignment copies the handle; nothing is called

Write h()

@(x) x^2 given to integral

integral passes a vector; ^ is a matrix power

Write @(x) x.^2

Body needs several statements

An anonymous function holds one expression

Use a local function and a handle to it

str2func on text that mentions a workspace variable

The new handle has no access to the variable

Build the handle with @ in the code that has the variable

eval, load or assignin in the body

The variables are not captured; calling the handle fails

Name every variable in the body

functions(h) used in code

The documentation reserves it for debugging; its behaviour may change

Store the data you need explicitly

Handle saved in a MAT-file for a file function that moves

The handle becomes invalid; a call is an error

Save the data and rebuild the handle on load

Large array captured in a handle

The data is stored in the handle and copied with it

Pass it as an argument

Where older books differ

Older books pass functions to solvers as names in character vectors, call them with feval, or use inline objects; today a handle is passed and called directly (feval is only needed when you hold a name), and anonymous functions replace inline. Older texts also pass extra parameters to fzero or ode45 as additional arguments after the options structure; the current documentation parameterises with an anonymous or nested function, as above, and the solvers expect a handle with the arguments they supply.