Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Subroutines

Subroutines are reusable code blocks in Perl. PetaPerl implements Perl5-compatible subroutines with full support for lexical variables, closures, and signatures.

Declaration

sub greet {
    print "Hello, World!\n";
}

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

# Anonymous sub
my $sub = sub {
    my $x = shift;
    return $x * 2;
};

Arguments (@_)

Arguments are passed via the special @_ array. Each element is aliased to the caller’s variable.

sub modify {
    $_[0] = "modified";  # Modifies caller's variable
}

my $x = "original";
modify($x);
say $x;  # "modified"

Copying arguments:

sub safe_modify {
    my ($val) = @_;      # Copy, not alias
    $val = "modified";   # Does not affect caller
}

Direct access:

sub first { $_[0] }
sub second { $_[1] }

Return Values

Explicit Return

sub max {
    my ($a, $b) = @_;
    return $a if $a > $b;
    return $b;
}

Implicit Return

The last expression’s value is returned automatically.

sub square {
    my $x = shift;
    $x * $x  # Implicit return
}

Context-Sensitive Returns

sub get_data {
    if (wantarray) {
        return (1, 2, 3);      # List context
    } else {
        return "scalar data";  # Scalar context
    }
}

my @list = get_data();   # (1, 2, 3)
my $scalar = get_data(); # "scalar data"

Note: PetaPerl’s wantarray works for direct calls and most common patterns. Complex nesting may have edge cases.

Prototypes

Prototypes provide compile-time argument checking and context forcing.

sub scalar_arg ($) {
    my $x = shift;
}

sub array_arg (@) {
    my @vals = @_;
}

sub no_args () {
    return 42;
}

Common prototypes:

PrototypeMeaning
$Single scalar argument
@Array of arguments (slurpy)
%Hash (even-length list)
&Subroutine reference
*Bareword or typeglob
\$Scalar reference
\@Array reference
\%Hash reference
()No arguments (constant sub)

PetaPerl Status: Basic prototype parsing implemented. Runtime enforcement partial.

Signatures (Experimental)

Perl 5.20+ signatures provide named parameters with optional type constraints and defaults.

sub greet ($name, $greeting = "Hello") {
    say "$greeting, $name!";
}

greet("Alice");              # Hello, Alice!
greet("Bob", "Hi");          # Hi, Bob!

Slurpy parameters:

sub sum ($first, @rest) {
    my $total = $first;
    $total += $_ for @rest;
    return $total;
}

sum(1, 2, 3, 4);  # 10

PetaPerl Status: Signature parsing implemented in AST. Codegen lowers to pad allocation. Runtime parameter assignment works. Default values and slurpy params functional.

Closures

Anonymous subs capture lexical variables from enclosing scope.

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

my $c1 = make_counter();
say $c1->();  # 1
say $c1->();  # 2

my $c2 = make_counter();
say $c2->();  # 1  (independent counter)

Closure capture mechanism:

  • Parser identifies outer lexicals referenced in sub body
  • Codegen records outer_refs mapping (sub_pad_slot, outer_pad_slot)
  • pp_anoncode captures cells at instantiation time
  • pp_entersub shares captured cells into sub’s pad

Example: Closure factory

sub make_adder {
    my $offset = shift;
    return sub {
        my $x = shift;
        return $x + $offset;  # Captures $offset
    };
}

my $add5 = make_adder(5);
my $add10 = make_adder(10);

say $add5->(3);   # 8
say $add10->(3);  # 13

Each closure captures its own $offset cell. Modifications affect only that closure’s instance.

Method Calls

my $obj = Some::Class->new();
$obj->method($arg);

# Indirect object notation (discouraged)
method $obj $arg;

PetaPerl Status: Method dispatch via pp_method, pp_method_named. ISA lookup implemented. AUTOLOAD supported.

Special Subroutines

AUTOLOAD

Called when undefined sub is invoked.

package Foo;

our $AUTOLOAD;

sub AUTOLOAD {
    my $method = $AUTOLOAD;
    $method =~ s/.*:://;  # Strip package
    print "Called undefined method: $method\n";
}

Foo->undefined_method();  # Triggers AUTOLOAD

PetaPerl Status: AUTOLOAD dispatch implemented in pp_entersub. Sets $Package::AUTOLOAD correctly.

BEGIN, END, INIT, CHECK, UNITCHECK

PetaPerl Status: BEGIN blocks execute during compilation. END blocks execute at program exit. INIT, CHECK, and UNITCHECK have partial support.

Recursion

Recursive calls are supported with depth limit.

sub factorial {
    my $n = shift;
    return 1 if $n <= 1;
    return $n * factorial($n - 1);
}

Recursion limit: Default 1000 levels (configurable via PPERL_MAX_RECURSION). Prevents stack overflow.

Calling Conventions

With parentheses

foo();        # No arguments
foo($a);      # One argument
foo($a, $b);  # Multiple arguments

Without parentheses (ampersand form)

&foo;   # Pass current @_ to foo

When called as &foo (no parens), the current @_ is passed to the callee. This enables wrapper functions:

sub wrapper {
    log_call();
    goto &real_function;  # Tail call with current @_
}

Goto and Tail Calls

sub outer {
    # ... setup ...
    goto &inner;  # Replace current call frame
}

goto &sub performs tail call optimization: replaces current frame instead of adding a new one.

PetaPerl Status: goto &sub implemented in pp_goto. Tail recursion optimization functional.

Advanced Features

Constant Subs

use constant PI => 3.14159;
use constant DEBUG => 0;

# Equivalent to:
sub PI () { 3.14159 }

Constant subs have empty prototype () and return fixed value. Perl can inline them at compile time.

PetaPerl Status: use constant creates constant subs. pp_entersub returns constant value directly without entering sub.

Lvalue Subs

sub get_value : lvalue {
    $global_var;
}

get_value() = 42;  # Assigns to $global_var

PetaPerl Status: :lvalue attribute not implemented. pp_leavesublv stub exists.

State Variables

sub counter {
    state $count = 0;
    return ++$count;
}

state variables persist across calls (initialized once).

PetaPerl Status: State variables are implemented. Variables are initialized once and persist across calls.

Built-in Functions

wantarray

Returns calling context: undef (void), 0 (scalar), 1 (list).

sub context_aware {
    if (!defined wantarray) {
        say "void context";
    } elsif (wantarray) {
        say "list context";
        return (1, 2, 3);
    } else {
        say "scalar context";
        return 42;
    }
}

PetaPerl Status: Partial implementation. Works for direct calls. May fail in complex nesting.

caller

Returns information about calling stack frame.

# Scalar context: boolean (is there a caller?)
if (caller) {
    # Called from another sub
}

# List context: detailed info
my ($package, $filename, $line) = caller(0);
my ($pkg, $file, $line, $sub) = caller(1);  # One frame up

PetaPerl Status: Implemented in pp_caller. Returns (package, filename, line, subroutine, ...) in list context.

PetaPerl Implementation Notes

Op Tree Structure

Named subs register CV (Code Value) in arena:

SubDef → lower_sub() → NextState → body ops → LeaveSub
                     ↓
                 Register CV(name, start_op, pad_size, outer_refs)

Anonymous subs emit AnonCode op:

AnonSub → lower_anon_sub() → NextState → body ops → LeaveSub
                           ↓
                       AnonCode (pushes CV onto stack)

Call Sequence

  1. Caller pushes args onto stack
  2. Caller pushes mark
  3. Caller pushes CV reference
  4. EnterSub pops CV, pops mark, collects args into aliased @_
  5. New pad allocated (slot 0 = @_)
  6. Closure cells shared into pad (if any)
  7. Jump to CV’s start_op
  8. Body executes
  9. LeaveSub collects return values, restores pad
  10. Return to caller

Pad Layout

Slot 0: @_ (always, populated by EnterSub)
Slot 1+: Lexical variables in declaration order

Signature parameters become pad slots:

sub foo ($x, $y, @rest) { }

# Pad layout:
# 0: @_
# 1: $x
# 2: $y
# 3: @rest

Closure Capture

Two-stage process:

  1. Codegen: Analyze outer lexicals, record outer_refs in CV
  2. Runtime: pp_anoncode captures cells, pp_entersub shares them

Why two stages? Closure factories need per-instance capture, not per-definition.

Known Limitations

FeatureStatus
Named parametersWorking
PrototypesParsed, partial enforcement
SignaturesWorking (basic)
ClosuresWorking
RecursionWorking (1000 depth limit)
AUTOLOADWorking
Method dispatchWorking
wantarrayPartial
callerWorking
BEGIN/END blocksBEGIN works; END/INIT/CHECK partial
Attributes (:lvalue, etc.)Parsed, not enforced
goto &subWorking
State variablesBasic support

Performance

PetaPerl subs execute via the interpreter with fast-path dispatch. The JIT compiler (Cranelift) handles hot loops within subroutines but does not JIT subroutine call/return itself.

The interpreter’s function-pointer dispatch table and inline fast paths for common operations (arithmetic, comparison, assignment) minimize per-op overhead.

Closure overhead: Cell allocation per captured variable. Minimal at runtime (Arc clone).

Testing

See t/05-op/sub.t, t/05-op/closure.t for comprehensive test coverage.