You are here

Functional APIs in MATLAB with Self-Referential Structs and Closure Rebinding

A modern, functional approach to building extensible APIs in MATLAB without OOP boilerplate or nested function clutter.

By Arlon Arriola



Introduction

MATLAB isn't known for its flexibility in functional programming or recursive design patterns. But with a little creativity -- and an understanding of closures, struct behavior, and variable scope -- we can build a self-referential API structure that behaves like modern web or middleware architectures.

This pattern is simple, powerful, and surprisingly underused.

I'll walk through building a multi-level API using:

  • Self-referential anonymous functions
  • Structs with recursive function calls
  • A rebinding loop that stabilizes closures across nested levels

The result? A PHP-style callable API object, built entirely in vanilla MATLAB.

Why Would You Want This?

You may want to:

  • Build a modular, extensible toolbox without lots of m files
  • Simulate middleware or route handlers
  • Encapsulate logic in a structure that behaves like an object or service container
  • Avoid OOP boilerplate when you don't need inheritance or properties
  • Stay in a purely script/function-file domain (no classes)

This design mimics associative arrays + closures in PHP or object method chains in JavaScript -- using only struct and @() in MATLAB.

The Pattern: Closure-Stabilized API Struct

api = '';  % Predeclare (not strictly necessary, but good practice)
for i = 1:3
    api = struct( ...
        'a', struct( ...
            'b', struct( ...
                'f1', @(x) disp(['f1: ', x]) ...
            ), ...
            'c', struct( ...
                'f2', @(x) api.a.b.f1(['f2 (-> f1): ', x]) ...
            ), ...
            'd', struct( ...
                'f3', @(x) api.a.c.f2(['f3 (-> f2 -> f1): ', x]) ...
            ) ...
        ) ...
    );
end

Why the for loop?

Because MATLAB closures capture by reference, not by value, we need to rebuild the full struct several times to stabilize all nested references.

Each pass:

  • Rebinds api to include more complete versions of itself
  • Updates all function handles to point to the final, stable API

We only need as many iterations as the deepest level of self-reference, so for i=1:3 handles 3 levels (f3 -> f2 -> f1). You can always set it to 10 or more and never worry.

Usage

api.a.b.f1('Hello World');   % Direct call
api.a.c.f2('Hello World');   % f2 calls f1 internally
api.a.d.f3('Hello World');   % f3 -> f2 -> f1

Output:

f1: Hello World
f1: f2 (-> f1): Hello World
f1: f2 (-> f1): f3 (-> f2 -> f1): Hello World

Each level wraps the previous one -- a perfect model for chaining behavior or composing logic.

Design Inspiration

This approach mirrors functional paradigms:

ConceptEquivalent
api.a.b.f1()PHP-style associative arrays with callable values
Recursive struct rebindingFixed-point combinator / Y-combinator
@() closuresAnonymous functions with lexical scoping
Chainable APIMiddleware stacks, command patterns, services

It's like building a small internal DSL (domain-specific language) for your code.

Use Cases

  • Command router for a CLI toolbox
  • GUI event handler API (api.ui.buttons.save.click(...))
  • Dependency injection container
  • Composable simulations (api.sim.engine.step(...))
  • HTTP-style route dispatchers (api.http.get.users(...))

You could even expand this with:

  • Auto-generated docs
  • Runtime introspection
  • Dynamic module loading
  • Middleware-like pipelines

Purpose

This tiny idiom -- rebuilding a struct of self-referential anonymous functions -- opens a door to functional design in MATLAB without the rigidity of OOP or class definitions.

The simplicity is deceptive. It gives you:

  • Namespacing
  • Closure binding
  • Call chaining
  • Function encapsulation

All in one object.

Bonus: Expandability Template

for i = 1:10  % deeper loop = deeper nesting
    api = struct( ...
        'layer1', struct( ...
            'f', @(x) disp(x) ...
        ), ...
        'layer2', struct( ...
            'f', @(x) api.layer1.f(['-> ', x]) ...
        ), ...
        'layer3', struct( ...
            'f', @(x) api.layer2.f(['-> ', x]) ...
        ) ...
    );
end

Functional Multi-Line Anonymous Functions via Recursive Self-Reference

MATLAB famously limits anonymous functions to a single expression -- no sequential logic, no if/else, no multiple statements. But that restriction can be bypassed using recursive functional programming and state threading.

The Core Trick

  • Start with a recursive anonymous function
  • Use a branching function (like a custom ternary or an if_)
  • Mutate parameters via new struct on each pass
  • Final recursion returns result

Method 1: if_ Branching with Recursion

if_ = @( pred_, cond_ ) cond_{ 2 - pred_ }();  % Choose branch

makeZ = @(makeZ, x) ...
    if_(~isfield(x, 'i'), ...
        {@() makeZ(makeZ, struct('i', 1, 'x', x, 'z', zeros(5))), ...
         @() if_(x.i == 1, ...
            {@() makeZ(makeZ, struct('i', 0, 'x', x.x, ...
                'z', [x.x(1)*cos(x.x(2)) x.z(1,2:5); x.z(2:5,:)]))}, ...
            {@() [x.z(1:2,:); x.z(3,1:3) log(x.x(3)) x.z(3,5); x.z(4:5,:)]}) ...
        });

mkZ = @(x) makeZ(makeZ, x);
mkZ([2 3 4]);

Method 2: iif and recur Pattern

iif = @(varargin) varargin{2 * find([varargin{1:2:end}], 1, 'first')}();
recur = @(f, varargin) f(f, varargin{:});

makeZ_2 = @(x) recur(@(f, k) ...
    iif(~isfield(k,'i'), ...
        @() f(f, struct('i',1,'x',k,'z',zeros(5))), ...
        (isfield(k,'i') && k.i==1), ...
        @() f(f, struct('i',0,'x',k.x,'z',[k.x(1)*cos(k.x(2)) k.z(1,2:5); k.z(2:5,:)])), ...
        true, ...
        @() [k.z(1:2,:); k.z(3,1:3) log(k.x(3)) k.z(3,5); k.z(4:5,:)]) ...
    , x);

makeZ_2([2 3 4]);

Method 3: Custom ternary Operator

ternary = @(varargin) varargin{length(varargin) - varargin{1}};

makeZ_3 = @(makeZ_3, x) ...
    feval(ternary(~isfield(x,'i'), ...
        @() makeZ_3(makeZ_3, struct('i', 1, 'x', x, 'z', zeros(5))), ...
        @() feval(ternary(x.i==1, ...
            @() makeZ_3(makeZ_3, struct('i', 0, 'x', x.x, ...
                'z', [x.x(1)*cos(x.x(2)) x.z(1,2:5); x.z(2:5,:)])), ...
            @() [x.z(1:2,:); x.z(3,1:3) log(x.x(3)) x.z(3,5); x.z(4:5,:)]) ...
        ));

mkZ_3 = @(x) makeZ_3(makeZ_3, x);
mkZ_3([2 3 4]);

How This Works

  • State is threaded through each recursive call
  • Branching logic decides which transformation to apply
  • The function emulates a multi-line block

Why This Matters

This isn't just a MATLAB trick. It's a functional programming pattern, adapted to a language that doesn't quite support it. These methods:

  • Enable multi-line logic in inline definitions
  • Keep all code within a closure, no external state
  • Are reusable, composable, and embeddable in factory patterns

Also see: Is it possible to write several statements into an anonymous function?

Postscript: Naming the Pattern

Internally, I've started calling this structure the Closure-Stabilized API Blaster — a nod to both its recursive closure rebinding pattern and its power as a functional API generator in plain MATLAB. It's part of the broader Blaster family: minimal, modular tools that skip the boilerplate and go straight to the logic.

You can think of it as a small internal DSL builder, middleware router, or a MATLAB-native service container — all without writing a single classdef file.

You are here