Perl Integration

Bidirectional integration between Strada and Perl 5.

Strada provides two-way integration with Perl:

Strada Calling Perl

The perl5 module allows Strada programs to embed and interact with a Perl 5 interpreter. This gives you access to the entire CPAN ecosystem from Strada.

Basic Usage

use lib "lib";
use perl5;

func main() int {
    # Initialize Perl (required first)
    perl5::init();

    # Evaluate expressions
    my str $result = perl5::eval("2 ** 10");
    say("2^10 = " . $result);  # 1024

    # Define and call subroutines
    perl5::run("sub double { return $_[0] * 2; }");
    my str $doubled = perl5::call("double", "21");
    say("21 doubled = " . $doubled);  # 42

    # Use CPAN modules
    if (perl5::use_module("List::Util") == 1) {
        perl5::run("@nums = (1, 5, 3, 9, 2);");
        my str $max = perl5::eval("List::Util::max(@nums)");
        say("Max: " . $max);  # 9
    }

    # Cleanup
    perl5::shutdown();
    return 0;
}

Compilation

You need to link against libperl when compiling:

# Compile with Perl embedding flags
./strada myapp.strada $(perl -MExtUtils::Embed -e ccopts -e ldopts)

API Reference

Function Description
perl5::init() Initialize Perl interpreter. Returns 1 on success, 0 on failure.
perl5::shutdown() Shutdown the Perl interpreter and free resources.
perl5::is_init() Check if Perl is initialized. Returns 1 if yes, 0 if no.
perl5::eval($code) Evaluate Perl code and return the result as a string.
perl5::run($code) Execute Perl code without returning a value.
perl5::call($sub, $arg) Call a Perl subroutine with a single string argument.
perl5::call_noargs($sub) Call a Perl subroutine with no arguments.
perl5::call_list($sub, $sep) Call a sub that returns a list, join with separator.
perl5::use_module($mod) Load a Perl module (like use Module;). Returns 1 on success.
perl5::require_module($mod) Require a Perl module. Returns 1 on success.
perl5::set_var($name, $val) Set a Perl scalar variable.
perl5::get_var($name) Get the value of a Perl scalar variable.
perl5::add_inc($path) Add a path to Perl's @INC.
perl5::get_error() Get the last Perl error ($@).

Working with Variables

# Set and get Perl variables
perl5::set_var("$name", "Alice");
perl5::set_var("$age", "30");

my str $name = perl5::get_var("$name");
say("Name: " . $name);  # Alice

# Work with arrays and hashes in Perl
perl5::run("@colors = qw(red green blue);");
perl5::run("%person = (name => 'Bob', age => 25);");

my str $count = perl5::eval("scalar(@colors)");
my str $person_name = perl5::eval("$person{name}");

Using CPAN Modules

# HTTP requests with LWP
if (perl5::use_module("LWP::Simple") == 1) {
    my str $html = perl5::eval("get('http://example.com')");
    say("Got " . length($html) . " bytes");
}

# JSON parsing
if (perl5::use_module("JSON") == 1) {
    perl5::set_var("$json_str", "{\"name\":\"test\"}");
    perl5::run("$data = decode_json($json_str);");
    my str $name = perl5::eval("$data->{name}");
}

# Database access with DBI
if (perl5::use_module("DBI") == 1) {
    perl5::run("$dbh = DBI->connect('dbi:SQLite:test.db');");
    perl5::run("$sth = $dbh->prepare('SELECT * FROM users');");
    perl5::run("$sth->execute();");
    # ... process results
}

XS Module Support

XS-based Perl modules (compiled modules like POSIX, JSON::XS, DBI) require the Perl library to be preloaded:

# Find your libperl path
perl -MConfig -e 'print $Config{archlib}'

# Run with LD_PRELOAD
LD_PRELOAD=/lib/x86_64-linux-gnu/libperl.so.5.38 ./myapp

Pure Perl modules work without this workaround.

Error Handling

# Check for errors after eval
my str $result = perl5::eval("some_code()");
my str $error = perl5::get_error();

if (length($error) > 0) {
    say("Perl error: " . $error);
} else {
    say("Result: " . $result);
}

Perl Calling Strada

The Strada Perl module (v1.0) lets Perl programs load and call functions from compiled Strada shared libraries — with automatic type conversion, calls of any arity (with libffi), Strada object construction and method dispatch, and Strada exceptions surfaced as catchable Perl die. Write performance-critical code in Strada and use it idiomatically from Perl.

Setup

# Build the Perl XS module (install libffi-dev for unlimited-argument calls)
cd perl/Strada
perl Makefile.PL
make
make test
make install  # optional

Creating a Strada Library

First, write your Strada code as a library:

# mylib.strada
package mylib;

func add(int $a, int $b) int {
    return $a + $b;
}

func greet(str $name) str {
    return "Hello, " . $name . "!";
}

func sum_list(scalar $items) int {
    my int $sum = 0;
    foreach my int $x ($items) {
        $sum = $sum + $x;
    }
    return $sum;
}

Compile it as a shared library:

./strada --shared mylib.strada
# Creates mylib.so

Using from Perl

#!/usr/bin/perl
use Strada;

# Load the library
my $lib = Strada::Library->new('./mylib.so');

# Call functions using namespace syntax
my $sum = $lib->call('mylib::add', 10, 20);
print "10 + 20 = $sum\n";  # 30

my $greeting = $lib->call('mylib::greet', 'Perl');
print "$greeting\n";  # Hello, Perl!

# Pass arrays
my $total = $lib->call('mylib::sum_list', [1, 2, 3, 4, 5]);
print "Sum: $total\n";  # 15

# Unload when done
$lib->unload();

Function Naming Convention

Strada functions are exported as package_function (underscore-separated). The Perl module accepts both styles:

Strada Declaration C Name Perl Call (both work)
package foo; func bar() foo_bar 'foo_bar' or 'foo::bar'
package utils; func format() utils_format 'utils_format' or 'utils::format'
package MyLib; func process() MyLib_process 'MyLib_process' or 'MyLib::process'
Namespace syntax supported: The Perl module automatically converts :: to _, so you can use familiar Strada/Perl syntax like $lib->call('mylib::add', 1, 2).

Type Conversion

Types are automatically converted between Strada and Perl:

Strada Type Perl Type
int Integer (IV)
num Float (NV)
str String (PV)
array Array reference
hash Hash reference
undef undef
blessed object Strada::Object (dispatches methods)

Objects and Methods

A blessed Strada object returned from the runtime comes back as a Strada::Object that dispatches method calls back into Strada (instead of being flattened to a plain hashref). Construct objects with new_object and call methods directly:

my $lib = Strada::Library->new('./mylib.so');

# Construct a Strada object (sugar for $lib->call('Counter::new', ...))
my $c = $lib->new_object('Counter', count => 10, name => 'hits');

$c->increment;            # method dispatch -> 11
$c->add(5);              # method with arguments -> 16
print $c->describe, "\n";  # "hits=16"

$c->strada_class;         # "Counter" (the Strada package)
$c->isa('Counter');       # true (checks the Strada class hierarchy)

Exception Handling

A Strada throw raised inside a called function or method is re-raised as a catchable Perl exception (eval / Try::Tiny); the runtime recovers cleanly so later calls still work. A thrown object arrives in $@ as a Strada::Object.

my $ok = eval { $c->checked_add(-1); 1 };
if (!$ok) {
    print "caught: $@\n";   # "negative amount not allowed"
}

Use Cases

Access CPAN from Strada

Use any of the thousands of CPAN modules from your Strada programs:

perl5::init();

# Parse YAML
perl5::use_module("YAML");
my str $yaml = perl5::eval("YAML::Dump({name => 'test'})");

# Send email
perl5::use_module("Email::Simple");
perl5::use_module("Email::Sender::Simple");

# Date handling
perl5::use_module("DateTime");
my str $now = perl5::eval("DateTime->now->iso8601");

perl5::shutdown();

High-Performance Code from Perl

Write performance-critical code in compiled Strada, call from Perl:

#!/usr/bin/perl
use Strada;

my $lib = Strada::Library->new('./libcompute.so');

# Strada handles the heavy lifting
for my $data (@large_dataset) {
    my $result = $lib->call('compute::process', $data);
    push @results, $result;
}

$lib->unload();

Mixed Language Projects

Combine the strengths of both languages:

Directory Structure

lib/
  perl5.strada           # Strada module for calling Perl

perl/
  Strada/
    Strada.xs            # XS implementation
    Strada.pm            # Perl module
    Makefile.PL          # Build configuration
    t/
      01_basic.t         # Test suite

See Also