Sometimes, the best tool for the job involves three different tools. You might have legacy math models in Fortran, performance-critical loops in Assembly, and a clean C API to orchestrate them.

By mastering the Polyglot strategy, you're no longer forced to rewrite battle-tested legacy code or compromise on performance. You can let Fortran handle the numerics, Assembly handle the vectorization, and C handle do coordination, all while driving the whole machine from Perl. Affix::Build turns what used to be a nightmare of Makefiles and linker flags into a simple script, freeing you to choose the absolute best tool for every specific problem.

The Recipe

Here, we'll create a library called number_cruncher. It uses C as the API controller, Fortran for the calculation logic, and Assembly for a low-level incrementer. It's not a very useful example but it's a good demonstration of our task.

use v5.40;
use Affix qw[:all];
use Affix::Build;
use Config;

# 1. Initialize
# We assume we are compiling different languages into one binary
my $c = Affix::Build->new( name => 'number_cruncher' );

# 2. C Source (The Orchestrator)
# Standard C function that will be the entry point or helper
$c->add( \<<~'', lang => 'c' );
    #include <stdio.h>
    #ifdef _WIN32
    __declspec(dllexport)
    #endif
    int core_version() { return 1; }

# 3. Fortran Source (The Math Engine)
# Uses ISO C Binding to export 'fortran_add' as a standard C symbol
$c->add( \<<~'', lang => 'f90' );
    function fortran_add(a, b) bind(c, name='fortran_add')
        use iso_c_binding
        integer(c_int), value :: a, b
        integer(c_int) :: fortran_add
        fortran_add = a + b
    end function

# 4. Assembly Source (The Optimizer)
# A simple function 'asm_inc(x)' that returns x + 1.
# We must detect the CPU architecture to provide the correct instructions.
my $arm = $Config{archname} =~ /arm64|aarch64/;
$c->add( \( $arm ? <<~'' : $^O eq 'MSWin32' ? <<~'': <<~'' ), lang => $arm ? 's' : 'asm' );
        ; ARM64: Linux, macOS, Windows ARM uses standard system assembler (.s)
        .global asm_inc
        .text
        .align 2
        asm_inc:
            add w0, w0, #1  ; Increment w0 (first arg) by 1
            ret

        ; Win64 x86_64: Uses RCX for first argument
        global asm_inc
        section .text
        asm_inc:
            mov eax, ecx
            inc eax
            ret

        ; SysV x86_64: Uses RDI for first argument
        global asm_inc
        section .text
        asm_inc:
            mov eax, edi
            inc eax
            ret


# 5. Build
# The compiler detects mixed languages and switches to 'Polyglot Mode'
my $lib = $c->link;
say 'Compiled Polyglot Library: ' . $lib;

# 6. Bind
affix $lib, 'core_version', []           => Int;
affix $lib, 'fortran_add',  [ Int, Int ] => Int;
affix $lib, 'asm_inc',      [Int]        => Int;

# 7. Run
say 'Core Version: ' . core_version();
say 'Fortran Add:  ' . fortran_add( 10, 20 );
say 'ASM Inc:      ' . asm_inc(99);

Output:

Compiled Polyglot Library: C:/Users/S/AppData/Local/Temp/Fye21DPXMw/number_cruncher.dll.1
Core Version: 1
Fortran Add:  30
ASM Inc:      100

How It Works

Kitchen Reminders