Introduction

In this multipart series we will explore the benefits (and pitfalls) of vibe coding a Perl interface to an external (or foreign) library through a large language model. Those of you who follow me on X/Twitter (as @ChristosArgyrop and @ArgyropChristos), Bluesky , mast.hpc, mstdn.science, mstdn.social know that I have been very critical of the hype behind AI and the hallucinations of both models and the human AI influencers (informally known as botlickers in some corners of the web).

However, there are application areas of vibe coding with AI, e.g. semi-automating the task of creating API from one one language to another, in which the chatbots may actually deliver well and act as productivity boosters. In this application area, we will be leveraging the AI tools as more or less enhanced auto-complete tools that can help a developer navigate less familiar, more technical and possibly more boring aspects of the target language’s ‘guts’. If AI were to deliver in this area, then meaningless language wars can be averted, and wrong (at least in my opinion) reasons to prefer one language, i.e. the availability of some exotic library, may be avoided.

For my foray in this area, I chose to interface to the library Bit that I wrote to support text fingerprinting for some research applications. The library based on David Hanson’s Bit_T library discussed in Chapter 13 of “C Interfaces and Implementations” has been extended to incorporate additional operations on bitsets (such as counts on unions/differences/intersections of sets) and fast population counts in both CPU and GPU (with TPU implementations coming down the road). Hence, this is a test case that can illustrate the utility of Perl in using code that executes transparently in various hardware assets. Similar to Hanson’s original implementation (after all, my work is based on his!) the library interface is implemented to an Abstract Data Type in C; a crucial aspect of the implementation is to manage memory and avoid leaks without looking (at all, or as little as possible!) under the hood. For our experiments we will use Claude 3.7 Thinking Sonnet through the Github Copilot vscode interface. This is going to be a multipart series post that will be published throughout the summer. Our focus will be on interactions between me and the bot, and in particular critiquing the responses it has given me for both high level (e.g. choice of approach) and technical aspects of the project.

The Prompt

For the prompt I provided the “bit.c”, “bit.h”, makefile and the readme file of the Bit github repository as context to Claude and then I issued the following:

Look at the bit.h file that defines two abstract data types Bit_T and Bit_T_DB
and their interfaces in C. I would like to wrap these interfaces in Perl.
The build process will  consist of using the Alien package in Perl to make bit available to Perl.
Explore possible options for the creation of the interface: 
A. Creation of the bit  C library :
1) compiling bit.c and bit.h as dynamic libraries
2) compiling bit.c nd bit.h into a static library
B. Interfacing with the bit C library from Perl:
1) using SWIG i files
2) using Platypus::FFI
3) using Inline::C
4) using XS

Since you are an expert with deep knowledge of the Perl ecosystem, consider pros and cons of
each approach. and then recommend the optimal Consider lightweight options for encapsulating
the Bit_T and Bit_T_DB abstract data types as Perl objects.
Provide wrapping recommendations targetting a beginner, intermediate and experienced Perl
programmer since those may differ.  When providing recommendations , consider the dependencies
that are required for each implementation and highlight them in your analysis.
Once you have provided the recommendation, provide the implementation of the wrapper for
these 3 programmer levels. To reduce the token output (and your risk of hallucinating),
provide a full implementation of the wrapper for the beginner level and only the
changes (if any) that are required for the intermediate and advanced programmer levels.

In my prompt specification, I clearly specified that

  • linkage to the foreign library could be either as a static or a dynamic object,
  • the desired way to package the dependency for use in Perl is an Alien package
  • all four possible ways to interface to a foreign library from Perl should be considered.
  • asked it to consider the problem of wrapping from the perspective of Perl developers with different levels of expertise.

Note that I gave the bot considerable freedom over the “low” level aspects of wrapping (type of library, choice of the cross-language interface), but restricted it in terms of how I wanted the library served. Furthermore, I guided it to consider alternative viewpoints and level of expertise, providing a unique opportunity to test Claude’s representation1 of conversations about wrappers. Finally, I alerted the chatbot to consider the token-sequence length dependent risk of hallucinations and tried to limit the length of the output of the alternative implementations.

Claude’s Response

The output based on this prompt is shown below (inside the horizontal lines); the formatting was also part of the output


Perl Wrapper for Bit_T and Bit_T_DB C Library

Let me analyze the options for creating a Perl interface to the Bit_T and Bit_T_DB C library and provide implementation recommendations for programmers at different experience levels.

A. Creation of the Bit C Library

1. Dynamic Library Approach

I see the Makefile already creates a dynamic library (libbit.so) in the build directory with this rule:

$(TARGET): $(OBJ)
	$(CC) $(CFLAGS) -shared -o $@ $^ $(LDFLAGS)

Pros:

  • Runtime binding, reducing memory footprint
  • Updates to the library don’t require recompiling Perl code
  • Already implemented in your build system
  • Better for distributing the library separately from the wrapper

Cons:

  • Deployment requires ensuring the library is in the system path
  • Potential version compatibility issues

2. Static Library Approach

To compile as a static library, you would modify the Makefile:

TARGET = $(BUILD_DIR)/libbit.a

$(TARGET): $(OBJ)
	ar rcs $@ $^

Pros:

  • Self-contained distribution
  • No runtime dependency resolution
  • Potentially faster function calls
  • No path issues during deployment

Cons:

  • Larger binary size
  • Need to recompile wrapper if library changes
  • Less memory efficient if multiple programs use the library

B. Perl Interfacing Options

1. SWIG

Pros:

  • Automated wrapper generation
  • Supports multiple languages (future portability)
  • Good for complex interfaces

Cons:

  • Learning curve for SWIG interface files
  • Additional build dependency
  • Complex for beginners

2. FFI::Platypus

Pros:

  • No compilation required at installation time
  • Modern, actively maintained
  • Cleaner code than XS
  • Suitable for your library structure

Cons:

  • Requires FFI::Platypus and related modules
  • Less efficient for very complex interfaces

3. Inline::C

Pros:

  • Simple integration of C code directly in Perl
  • No separate interface files
  • Good for prototyping

Cons:

  • C compiler required at runtime
  • Less suitable for large libraries
  • Performance overhead for initialization

4. XS

Pros:

  • Standard Perl way to interface with C
  • Excellent performance
  • Tight integration with Perl’s memory management

Cons:

  • Steep learning curve
  • Verbose, low-level coding
  • More complex build process

Recommendation for Each Level

Beginner Level: FFI::Platypus with Dynamic Library

FFI::Platypus provides the easiest approach for beginners with minimal C knowledge. It works with your existing dynamic library build.

Intermediate Level: Inline::C with Dynamic Library

Inline::C offers more control for intermediate users who understand some C code but want to avoid complex build processes.

Advanced Level: XS with Static Library

XS provides the highest performance and greatest control, suitable for experienced Perl programmers.

Implementation for Beginner Level: FFI::Platypus

Let’s create the Alien::Bit module first to handle library discovery, then implement Bit::Set and Bit::Set::DB modules using FFI::Platypus.

Step 1: Create Alien::Bit module

# /media/chrisarg/Software-Dev/Bit/perl-wrapper/lib/Alien/Bit.pm
package Alien::Bit;

use strict;
use warnings;
use parent 'Alien::Base';
use File::Spec;

sub dynamic_libs {
    my $class = shift;
    
    # Try to find the library in common locations
    my @potential_paths = (
        # From the build directory
        '/media/chrisarg/Software-Dev/Bit/bit/build/libbit.so',
        
        # System paths
        '/usr/local/lib/libbit.so',
        '/usr/lib/libbit.so',
        
        # Environment variable if set
        $ENV{BIT_LIB_PATH} ? File::Spec->catfile($ENV{BIT_LIB_PATH}, 'libbit.so') : ()
    );
    
    foreach my $path (@potential_paths) {
        return $path if -f $path && -r $path;
    }
    
    die "Could not find libbit.so library. Please set BIT_LIB_PATH environment variable to its location.\n";
}

sub include_dirs {
    return (
        '/media/chrisarg/Software-Dev/Bit/bit/include',
        '/usr/local/include',
        '/usr/include',
        $ENV{BIT_INCLUDE_PATH} || ()
    );
}

1;

Step 2: Create Bit::Set module for individual bitsets

# /media/chrisarg/Software-Dev/Bit/perl-wrapper/lib/Bit/Set.pm
package Bit::Set;

use strict;
use warnings;
use FFI::Platypus;
use FFI::Platypus::Memory qw(malloc free);
use FFI::Platypus::Buffer qw(scalar_to_buffer buffer_to_scalar);
use Alien::Bit;

# Create FFI::Platypus object
my $ffi = FFI::Platypus->new(api => 1);

# Add path to our dynamic library
$ffi->lib(Alien::Bit->dynamic_libs);

# Define opaque types for our bitset pointers
$ffi->type('opaque' => 'Bit_T');
$ffi->type('opaque*' => 'Bit_T_Ptr');

# Define our Bit_T functions
$ffi->attach(Bit_new => ['int'] => 'Bit_T' => sub {
    my ($xsub, $self, $length) = @_;
    die "Length must be a positive integer" unless defined $length && $length > 0;
    my $bit_set = $xsub->($length);
    die "Failed to create bit set" unless $bit_set;
    return bless { _handle => $bit_set }, $self;
});

$ffi->attach(Bit_free => ['Bit_T_Ptr'] => 'opaque' => sub {
    my ($xsub, $self) = @_;
    my $ptr = \$self->{_handle};
    return $xsub->($ptr);
});

$ffi->attach(Bit_length => ['Bit_T'] => 'int' => sub {
    my ($xsub, $self) = @_;
    return $xsub->($self->{_handle});
});

$ffi->attach(Bit_count => ['Bit_T'] => 'int' => sub {
    my ($xsub, $self) = @_;
    return $xsub->($self->{_handle});
});

$ffi->attach(Bit_buffer_size => ['int'] => 'int');

$ffi->attach(Bit_bset => ['Bit_T', 'int'] => 'void' => sub {
    my ($xsub, $self, $index) = @_;
    die "Index must be non-negative" unless defined $index && $index >= 0;
    $xsub->($self->{_handle}, $index);
});

$ffi->attach(Bit_bclear => ['Bit_T', 'int'] => 'void' => sub {
    my ($xsub, $self, $index) = @_;
    die "Index must be non-negative" unless defined $index && $index >= 0;
    $xsub->($self->{_handle}, $index);
});

$ffi->attach(Bit_get => ['Bit_T', 'int'] => 'int' => sub {
    my ($xsub, $self, $index) = @_;
    die "Index must be non-negative" unless defined $index && $index >= 0;
    return $xsub->($self->{_handle}, $index);
});

$ffi->attach(Bit_set => ['Bit_T', 'int', 'int'] => 'void' => sub {
    my ($xsub, $self, $lo, $hi) = @_;
    die "Low index must be non-negative" unless defined $lo && $lo >= 0;
    die "High index must be greater than or equal to low index" unless defined $hi && $hi >= $lo;
    $xsub->($self->{_handle}, $lo, $hi);
});

$ffi->attach(Bit_clear => ['Bit_T', 'int', 'int'] => 'void' => sub {
    my ($xsub, $self, $lo, $hi) = @_;
    die "Low index must be non-negative" unless defined $lo && $lo >= 0;
    die "High index must be greater than or equal to low index" unless defined $hi && $hi >= $lo;
    $xsub->($self->{_handle}, $lo, $hi);
});

# Comparison operations
$ffi->attach(Bit_eq => ['Bit_T', 'Bit_T'] => 'int' => sub {
    my ($xsub, $self, $other) = @_;
    die "Other bitset must be a Bit::Set object" unless ref $other eq ref $self;
    return $xsub->($self->{_handle}, $other->{_handle});
});

# Set operations
$ffi->attach(Bit_union => ['Bit_T', 'Bit_T'] => 'Bit_T' => sub {
    my ($xsub, $self, $other) = @_;
    die "Other bitset must be a Bit::Set object" unless ref $other eq ref $self;
    my $result_handle = $xsub->($self->{_handle}, $other->{_handle});
    return bless { _handle => $result_handle }, ref $self;
});

$ffi->attach(Bit_inter => ['Bit_T', 'Bit_T'] => 'Bit_T' => sub {
    my ($xsub, $self, $other) = @_;
    die "Other bitset must be a Bit::Set object" unless ref $other eq ref $self;
    my $result_handle = $xsub->($self->{_handle}, $other->{_handle});
    return bless { _handle => $result_handle }, ref $self;
});

# Count operations
$ffi->attach(Bit_inter_count => ['Bit_T', 'Bit_T'] => 'int' => sub {
    my ($xsub, $self, $other) = @_;
    die "Other bitset must be a Bit::Set object" unless ref $other eq ref $self;
    return $xsub->($self->{_handle}, $other->{_handle});
});

# Constructor and destructor
sub new {
    my ($class, $length) = @_;
    return $class->Bit_new($length);
}

sub DESTROY {
    my ($self) = @_;
    $self->Bit_free() if defined $self->{_handle};
}

# Convenient accessor methods
sub length {
    my ($self) = @_;
    return $self->Bit_length();
}

sub count {
    my ($self) = @_;
    return $self->Bit_count();
}

sub get {
    my ($self, $index) = @_;
    return $self->Bit_get($index);
}

sub set {
    my ($self, $index) = @_;
    $self->Bit_bset($index);
    return $self;
}

sub clear {
    my ($self, $index) = @_;
    $self->Bit_bclear($index);
    return $self;
}

sub set_range {
    my ($self, $lo, $hi) = @_;
    $self->Bit_set($lo, $hi);
    return $self;
}

sub clear_range {
    my ($self, $lo, $hi) = @_;
    $self->Bit_clear($lo, $hi);
    return $self;
}

sub equals {
    my ($self, $other) = @_;
    return $self->Bit_eq($other) ? 1 : 0;
}

sub union {
    my ($self, $other) = @_;
    return $self->Bit_union($other);
}

sub intersection {
    my ($self, $other) = @_;
    return $self->Bit_inter($other);
}

sub intersection_count {
    my ($self, $other) = @_;
    return $self->Bit_inter_count($other);
}

1;

Step 3: Create Bit::Set::DB module for packed containers

# /media/chrisarg/Software-Dev/Bit/perl-wrapper/lib/Bit/Set/DB.pm
package Bit::Set::DB;

use strict;
use warnings;
use FFI::Platypus;
use FFI::Platypus::Memory qw(malloc free);
use FFI::Platypus::Buffer qw(scalar_to_buffer buffer_to_scalar);
use Bit::Set;
use Alien::Bit;

# Create FFI::Platypus object
my $ffi = FFI::Platypus->new(api => 1);

# Add path to our dynamic library
$ffi->lib(Alien::Bit->dynamic_libs);

# Define opaque types for our bitset pointers
$ffi->type('opaque' => 'Bit_T');
$ffi->type('opaque' => 'Bit_T_DB');
$ffi->type('opaque*' => 'Bit_T_DB_Ptr');

# Define struct for SETOP_COUNT_OPTS
$ffi->type('record(SETOP_COUNT_OPTS)' => [
    num_cpu_threads => 'int',
    device_id => 'int',
    upd_1st_operand => 'bool',
    upd_2nd_operand => 'bool',
    release_1st_operand => 'bool',
    release_2nd_operand => 'bool',
    release_counts => 'bool'
]);

# Define Bit_T_DB functions
$ffi->attach(BitDB_new => ['int', 'int'] => 'Bit_T_DB' => sub {
    my ($xsub, $self, $length, $num_of_bitsets) = @_;
    die "Length must be a positive integer" unless defined $length && $length > 0;
    die "Number of bitsets must be a positive integer" unless defined $num_of_bitsets && $num_of_bitsets > 0;
    my $db = $xsub->($length, $num_of_bitsets);
    die "Failed to create bitset DB" unless $db;
    return bless { _handle => $db }, $self;
});

$ffi->attach(BitDB_free => ['Bit_T_DB_Ptr'] => 'opaque' => sub {
    my ($xsub, $self) = @_;
    my $ptr = \$self->{_handle};
    return $xsub->($ptr);
});

$ffi->attach(BitDB_length => ['Bit_T_DB'] => 'int' => sub {
    my ($xsub, $self) = @_;
    return $xsub->($self->{_handle});
});

$ffi->attach(BitDB_nelem => ['Bit_T_DB'] => 'int' => sub {
    my ($xsub, $self) = @_;
    return $xsub->($self->{_handle});
});

$ffi->attach(BitDB_count_at => ['Bit_T_DB', 'int'] => 'int' => sub {
    my ($xsub, $self, $index) = @_;
    die "Index must be non-negative" unless defined $index && $index >= 0;
    return $xsub->($self->{_handle}, $index);
});

$ffi->attach(BitDB_clear => ['Bit_T_DB'] => 'void' => sub {
    my ($xsub, $self) = @_;
    $xsub->($self->{_handle});
});

$ffi->attach(BitDB_clear_at => ['Bit_T_DB', 'int'] => 'void' => sub {
    my ($xsub, $self, $index) = @_;
    die "Index must be non-negative" unless defined $index && $index >= 0;
    $xsub->($self->{_handle}, $index);
});

$ffi->attach(BitDB_get_from => ['Bit_T_DB', 'int'] => 'Bit_T' => sub {
    my ($xsub, $self, $index) = @_;
    die "Index must be non-negative" unless defined $index && $index >= 0;
    my $bit_handle = $xsub->($self->{_handle}, $index);
    return bless { _handle => $bit_handle }, 'Bit::Set';
});

$ffi->attach(BitDB_put_at => ['Bit_T_DB', 'int', 'Bit_T'] => 'void' => sub {
    my ($xsub, $self, $index, $bitset) = @_;
    die "Index must be non-negative" unless defined $index && $index >= 0;
    die "Bitset must be a Bit::Set object" unless ref $bitset eq 'Bit::Set';
    $xsub->($self->{_handle}, $index, $bitset->{_handle});
});

# CPU-specific intersection count function
$ffi->attach(BitDB_inter_count_cpu => ['Bit_T_DB', 'Bit_T_DB', 'SETOP_COUNT_OPTS'] => 'int*' => sub {
    my ($xsub, $self, $other, $opts) = @_;
    die "Other must be a Bit::Set::DB object" unless ref $other eq ref $self;
    $opts ||= { 
        num_cpu_threads => 1, 
        device_id => -1, 
        upd_1st_operand => 0, 
        upd_2nd_operand => 0,
        release_1st_operand => 0,
        release_2nd_operand => 0,
        release_counts => 0
    };
    return $xsub->($self->{_handle}, $other->{_handle}, $opts);
});

# Constructor and destructor
sub new {
    my ($class, $length, $num_of_bitsets) = @_;
    return $class->BitDB_new($length, $num_of_bitsets);
}

sub DESTROY {
    my ($self) = @_;
    $self->BitDB_free() if defined $self->{_handle};
}

# Convenient accessor methods
sub length {
    my ($self) = @_;
    return $self->BitDB_length();
}

sub num_of_bitsets {
    my ($self) = @_;
    return $self->BitDB_nelem();
}

sub count_at {
    my ($self, $index) = @_;
    return $self->BitDB_count_at($index);
}

sub clear {
    my ($self) = @_;
    $self->BitDB_clear();
    return $self;
}

sub clear_at {
    my ($self, $index) = @_;
    $self->BitDB_clear_at($index);
    return $self;
}

sub get_bitset {
    my ($self, $index) = @_;
    return $self->BitDB_get_from($index);
}

sub put_bitset {
    my ($self, $index, $bitset) = @_;
    $self->BitDB_put_at($index, $bitset);
    return $self;
}

sub intersection_count_cpu {
    my ($self, $other, $opts) = @_;
    return $self->BitDB_inter_count_cpu($other, $opts);
}

1;

Step 4: Example usage script

#!/usr/bin/perl

use strict;
use warnings;
use Bit::Set;
use Bit::Set::DB;

# Create a new bitset with 1024 bits
my $bitset1 = Bit::Set->new(1024);
my $bitset2 = Bit::Set->new(1024);

# Set some bits
$bitset1->set(42);
$bitset1->set(100);
$bitset2->set(42);
$bitset2->set(200);

# Check if a bit is set
print "Bit 42 in bitset1: ", $bitset1->get(42), "\n";
print "Bit 100 in bitset1: ", $bitset1->get(100), "\n";
print "Bit 200 in bitset1: ", $bitset1->get(200), "\n";

# Count the number of bits set
print "Number of bits set in bitset1: ", $bitset1->count(), "\n";
print "Number of bits set in bitset2: ", $bitset2->count(), "\n";

# Calculate intersection count
my $intersection_count = $bitset1->intersection_count($bitset2);
print "Intersection count: $intersection_count\n";

# Create a new bitset as the union of the two bitsets
my $union = $bitset1->union($bitset2);
print "Number of bits set in union: ", $union->count(), "\n";

# Create a BitDB with 3 bitsets of length 1024
my $db = Bit::Set::DB->new(1024, 3);

# Put our bitsets into the DB
$db->put_bitset(0, $bitset1);
$db->put_bitset(1, $bitset2);
$db->put_bitset(2, $union);

# Count bits in each bitset in the DB
print "Bits set in DB at index 0: ", $db->count_at(0), "\n";
print "Bits set in DB at index 1: ", $db->count_at(1), "\n";
print "Bits set in DB at index 2: ", $db->count_at(2), "\n";

Modifications for Intermediate Level (Inline::C)

For intermediate-level Perl programmers, here are the changes required to implement the wrapper using Inline::C:

# Bit::Set using Inline::C
package Bit::Set;

use strict;
use warnings;
use Inline C => Config =>
    BUILD_NOISY => 1,
    CLEAN_AFTER_BUILD => 0,
    LIBS => '-L/media/chrisarg/Software-Dev/Bit/bit/build -lbit',
    INC => '-I/media/chrisarg/Software-Dev/Bit/bit/include';

use Inline C => <<'END_C';
#include "bit.h"

typedef struct Bit_T* Bit_T;

// Wrapper functions
Bit_T create_bitset(int length) {
    return Bit_new(length);
}

void free_bitset(Bit_T set) {
    Bit_free(&set);
}

int get_length(Bit_T set) {
    return Bit_length(set);
}

int get_count(Bit_T set) {
    return Bit_count(set);
}

void set_bit(Bit_T set, int index) {
    Bit_bset(set, index);
}

void clear_bit(Bit_T set, int index) {
    Bit_bclear(set, index);
}

int get_bit(Bit_T set, int index) {
    return Bit_get(set, index);
}

// Add more wrapper functions as needed
END_C

# Perl OO interface
sub new {
    my ($class, $length) = @_;
    my $handle = create_bitset($length);
    return bless { _handle => $handle }, $class;
}

# Add more methods similar to the FFI version

Modifications for Advanced Level (XS)

For advanced Perl programmers, here are the key changes required to implement using XS:

# Bit::Set.xs
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "bit.h"

typedef struct Bit_T* Bit_T;
typedef struct Bit_T_DB* Bit_T_DB;

MODULE = Bit::Set    PACKAGE = Bit::Set

PROTOTYPES: DISABLE

Bit_T
Bit_new(length)
    int length

void
Bit_free(set)
    Bit_T set
    CODE:
        Bit_free(&set);

int
Bit_length(set)
    Bit_T set

int
Bit_count(set)
    Bit_T set

void
Bit_bset(set, index)
    Bit_T set
    int index

# Add more functions as needed

And update the Makefile.PL:

use ExtUtils::MakeMaker;

WriteMakefile(
    NAME              => 'Bit::Set',
    VERSION_FROM      => 'lib/Bit/Set.pm',
    LIBS              => ['-L/media/chrisarg/Software-Dev/Bit/bit/build -lbit'],
    INC               => '-I/media/chrisarg/Software-Dev/Bit/bit/include',
    OBJECT            => '$(O_FILES)',
);

Conclusion

For most users, especially beginners, the FFI::Platypus approach offers the best balance of simplicity, performance, and maintainability. It works well with your existing dynamic library build process and provides a clean, object-oriented interface to the Bit_T and Bit_T_DB types. The implementation provided covers all the core functionality while being extensible for additional features as needed.


My critique of Claude’s choices

In this first post, we will not consider the implementation(s) which will be covered in separate posts, but will restrict ourselves to Claude’s output with respect to the other questions. I give Claude bonus points for clearly identifying the niches for each potential choice:

  1. Highighting that SWIG can support wrapping for other languages)
  2. Proposing Foreign Function Interface for linking to the dynamic library for beginners.
  3. A experienced programmer would opt for XS is not a surprise at all.

However:

  • the choice of Inline for the intermediate user is head-scratching: it seems that the chatbot closed on the intermediate level of programming experience in the prompt, and the selection of the approach was driven entirely by the fact that the user could (presumably) do more stuff in C.
  • SWIG was not considered as suitable (perhaps because few people in the training databases use SWIG) for implementing at any level. Without going into the specifics of the implementation though, I’d feel comfortable opting for FFI as an initial step for largely the reasons identified by Claude. We will have more things to say about the FFI implementation in the subsequent post in this series.

I alerted the bot to the (substantial) risk of hallucinations and decreased

  1. Note, I did not use the word understanding, as I do not think that LLMs can understant: they are merely noisy statistical pattern generators that can be tasked to create rough solutions for refining.