Polyglot
Languages MATLAB functional
MATLAB § functional

Anonymous functions and handles

Higher-order programming in MATLAB is built around the function_handle — a first-class reference to a function, named with a leading @. Function handles can be assigned to variables, stored in cell arrays, passed as arguments, returned from functions, and used as keys into containers. The handle is the substrate; the standard library provides higher-order combinators — arrayfun, cellfun, structfun, cellfun’s 'UniformOutput' mode — for the routine cases, and the Optimization Toolbox and the ODE suite are built entirely around accepting handles to user-supplied functions.

Named-function handles

The @ prefix produces a handle to a named function:

f = @sin;
f(pi/4)                      % 0.7071...
class(f)                     % 'function_handle'

The handle is bound at construction time to whichever function is on the path: it is essentially a closure over the function name. The handle remains valid across path changes (it captures the function itself, not the name).

g = @trapezoid;
g(@cos, 0, pi, 100)          % handles are first-class arguments

Anonymous functions

The @(args) expr form constructs an anonymous function — an inline lambda whose body is a single expression:

sq = @(x) x.^2;
sq(5)                        % 25
sq([1 2 3])                  % [1 4 9]

quadratic = @(a, b, c, x) a*x.^2 + b*x + c;
quadratic(1, -3, 2, 4)       % 6

An anonymous function is, like every value in MATLAB, an immutable object. The body is restricted to a single expression; for multi-statement work, define a function (named, local, or nested).

Closures

An anonymous function captures the surrounding workspace’s variable bindings at the moment of construction:

n = 5;
add_n = @(x) x + n;
add_n(10)                    % 15

n = 100;
add_n(10)                    % still 15 — the capture was by value

The capture is by value — the closure carries its own copy of the variables. This is the simplest, least-surprising model: a closure cannot see subsequent changes to the variable. The mechanism is the cleanest way to partially apply a function:

function partial = bindFirst(f, x)
    partial = @(varargin) f(x, varargin{:});
end

add5 = bindFirst(@plus, 5);
add5(3)                       % 8

For closures that need to modify shared state, the available mechanisms are nested functions (which capture by reference) and handle classes (see Classes and OOP).

Higher-order combinators

The standard library provides several common combinators:

CombinatorApplies a functionToReturns
arrayfun(f, A)element-wisearrayarray of results
cellfun(f, C)element-wisecell arrayarray (or cell, with 'UniformOutput', false)
structfun(f, S)per fieldstructarray of results
splitapply(f, X, G)per groupgrouped dataarray of results
accumarray(idx, val, [], f)per accumulationindexed valuesarray
arrayfun(@(k) k^2, 1:5)
% [1 4 9 16 25]

cellfun(@strlength, ["alpha", "beta", "gamma"])
% [5 4 5]

cellfun(@(s) upper(s), {"alpha", "beta"}, 'UniformOutput', false)
% {'ALPHA', 'BETA'}

structfun(@(v) class(v), struct('a', 1, 'b', "x"), 'UniformOutput', false)
% struct with fields:
%   a: 'double'
%   b: 'string'

The crucial flag is 'UniformOutput'. When true (the default), every call to f is expected to return a scalar of identical class, and the results are gathered into a numeric or logical array. When false, the results are gathered into a cell array — necessary when f returns objects of varying shape or class.

The vectorised, broadcast-aware equivalents — f(x) over a numeric vector — are almost always faster than arrayfun(f, x) and should be preferred. arrayfun is the right tool when f is a scalar-only function that cannot be vectorised, or when the function is itself an anonymous lambda for which writing a vectorised form is more trouble than it is worth.

Functions as arguments

The most common use of handles is passing a function to a higher-order one. The Optimization and Differential-Equation libraries are built around this pattern:

% Root-finding: find x such that sin(x) = 1/2 near x=0.
x = fzero(@(x) sin(x) - 1/2, 0)
% 0.5236  (= pi/6)

% Unconstrained minimisation:
f = @(x) x(1)^2 + 100*(x(2) - x(1)^2)^2;       % Rosenbrock
x = fminunc(f, [0; 0]);

% ODE: solve dy/dt = -2*y, y(0) = 1
[t, y] = ode45(@(t, y) -2*y, [0 5], 1);
plot(t, y)

The pattern is general: a numerical-library function takes a handle to the user’s model, evaluates it many times at points it chooses, and returns the result. The user supplies the mathematics; the library supplies the algorithm.

Composing functions

MATLAB has no compose operator, but composition is straightforward with an anonymous lambda:

compose = @(f, g) @(x) f(g(x));
fg = compose(@sin, @exp);
fg(0)                        % sin(exp(0)) = sin(1)

The pattern is occasionally useful but is rarely written explicitly in MATLAB code: in a vectorised expression, sin(exp(x)) is the same thing, written more clearly.

Partial application

The idiom for partial application is to build an anonymous function:

% Three forms of partial application of plus(x, y) = x + y
add5 = @(y) plus(5, y);                % bind first
addy = @(x) plus(x, 5);                % bind second (same value)
adder = @(x) @(y) plus(x, y);          % curry
add5_curried = adder(5);
add5_curried(3)                         % 8

The currying form is rare in idiomatic MATLAB; the partial-binding form is common as a way to pass a “parameterised” function to a higher-order one.

Function handles in containers

Handles can be stored in cells and as struct fields, and the combination is the canonical way to dispatch by name:

ops = struct( ...
    'add',      @(x, y) x + y, ...
    'subtract', @(x, y) x - y, ...
    'multiply', @(x, y) x .* y, ...
    'divide',   @(x, y) x ./ y);

ops.add(2, 3)                  % 5
ops.multiply([1 2 3], 4)       % [4 8 12]

% Programmatic:
choose = "multiply";
ops.(choose)([1 2 3], 4)       % [4 8 12]

The pattern is the cleanest MATLAB substitute for a dictionary of functions or a polymorphic dispatch table. Since R2022b the dictionary class provides a similar facility with non-string keys.

Introspection

The function func2str(h) returns the source text of an anonymous function or the name of a named-function handle; the inverse str2func(s) converts a name to a handle. The function functions(h) returns a struct describing a handle — function name, file, type, captured workspace — and is the principal debugging tool when a handle behaves unexpectedly.

add_n = @(x) x + n;
functions(add_n)
% function_handle with values:
%     function: '@(x)x+n'
%         type: 'anonymous'
%         file: '/path/to/caller.m'
%    workspace: {1×1 struct}

The workspace field is the captured variables — a useful diagnostic when a closure produces the wrong answer because of an unexpected capture.