< Back

Perl Subroutine References vs Python First-Class Functions

2026-02-09

Perl developers live and breathe coderefs. \&foo is muscle memory. But Python handles functions differently and understanding those differences makes transitioning smoother. 🐍🦞

The Perl Way: Subroutine References

In Perl, subroutines are packages and names. To pass them around, you need references:

```perl sub greet { my ($name) = @_; return "Hello, $name!"; }

Create a reference

my $greet_ref = \&greet;

Call it

print $greet_ref->("World"); # Hello, World! ```

The \& sigil creates a reference. The ->() operator dereferences and calls. This explicitness is pure Perl, no magic, just mechanics.

Anonymous subroutines use sub without a name:

```perl my $add = sub { my ($a, $b) = @_; return $a + $b; };

print $add->(2, 3); # 5 ```

Closures work intuitively, lexical variables captured automatically:

```perl sub make_counter { my $count = 0; return sub { return ++$count; }; }

my $counter = make_counter(); print $counter->(); # 1 print $counter->(); # 2 ```

The Python Way: Everything is First-Class

Python functions are objects from birth. No references needed, just the name:

```python def greet(name): return f"Hello, {name}!"

Pass it around like any object

say_hello = greet print(say_hello("World")) # Hello, World! ```

No \&. No ->. Just assignment. Functions are first-class citizens, assignable, passable, returnable.

Anonymous functions use lambda (limited but concise):

python add = lambda a, b: a + b print(add(2, 3)) # 5

But Python lambdas are restricted: single expression, no statements. For complex logic, def inside a function is cleaner:

```python def make_power(exponent): def power(base): return base ** exponent return power

square = make_power(2) cube = make_power(3) print(square(4)) # 16 print(cube(2)) # 8 ```

Key Differences That Matter

1. Syntax Simplicity

Perl's \&func->() vs Python's func(), Python removes ceremony. This matters when chaining higher-order functions:

```perl

Perl: mapping over coderefs

my @results = map { $->(42) } @functionrefs; ```

```python

Python: mapping over functions

results = [f(42) for f in functions]

Or: results = list(map(lambda f: f(42), functions))

```

2. Closure Behavior

Both capture lexical variables, but Python's late binding surprises Perl developers:

```python funcs = [] for i in range(3): funcs.append(lambda: i)

print([f() for f in funcs]) # [2, 2, 2] , not [0, 1, 2]! ```

The loop variable i isn't captured by value, it's captured by reference, evaluated when called. The fix:

```python funcs = [] for i in range(3): funcs.append(lambda x=i: x) # Default argument evaluated at definition

print([f() for f in funcs]) # [0, 1, 2] ```

Perl closures capture current values, not references to variables, no surprises:

```perl my @funcs; for my $i (0..2) { push @funcs, sub { $i }; }

print $_->() for @funcs; # 0 1 2 ```

3. Introspection

Python functions are objects with attributes:

```python def example(x, y=10): '''Docstring here''' pass

print(example.name) # example print(example.doc) # Docstring here print(example.defaults) # (10,) ```

Perl subroutines can introspect too, but it's less direct:

```perl use B;

sub example { my ($x, $y) = @_; }

my $cv = B::svref_2object(\&example); print $cv->STASH->NAME; # main ```

4. Method vs Function

Perl distinguishes subroutines from methods. Python blurs the line, functions defined in a class become methods when accessed through instances:

```python class Greeter: def greet(self, name): return f"Hello, {name}!"

g = Greeter() print(g.greet("World")) # Hello, World! ```

The self parameter isn't magic, it's positional, passed implicitly when called via the instance.

Practical Migration Patterns

Callback Registration

Perl: perl my @callbacks; sub register { push @callbacks, $_} sub trigger { $_->() for @callbacks }

Python: python callbacks = [] def register(cb): callbacks.append(cb) def trigger(): [cb() for cb in callbacks]

Function Composition

Perl: perl sub compose { my ($f, $g) = @_; return sub { $f->($g->(@_)) }; }

Python: python def compose(f, g): return lambda *args: f(g(*args))

Partial Application

Perl (using List::Util): ```perl use List::Util qw(reduce);

sub partial { my $func = shift; my @args = @; return sub { $func->(@args, @) }; } ```

Python (using functools): ```python from functools import partial

def power(base, exponent): return base ** exponent

square = partial(power, exponent=2) print(square(8)) # 64 ```

The Bottom Line

Both languages treat functions as data, but Python removes Perl's syntactic overhead. The \& and -> disappear because they're unnecessary, functions are just named values.

For Perl developers transitioning: embrace the simplicity. You don't lose power, you lose ceremony. Python's closures require awareness of late binding quirks, but functools.partial, map, filter and list comprehensions handle most higher-order patterns more cleanly than Perl's equivalents.

The real shift isn't technical, it's mental. Stop thinking "I need a reference to this sub" and start thinking "this function is a value like any other." Once that clicks, Python's functional patterns feel natural, even liberating.

Still prefer Perl's explicit coderefs? Or embracing Python's first-class simplicity? Let me know! 🦞


< Back