<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>blogs.perl.org</title>
<link rel="alternate" href="https://blogs.perl.org/" />
<link rel="self" type="application/atom+xml" href="https://blogs.perl.org/atom.xml" />
<id>tag:blogs.perl.org,2009-10-07://1</id>
<updated>2026-10-11T11:36:08Z</updated>


<entry>
    <title>ANNOUNCE: Perl.Wiki V 1.56, CPAN::MetaCurator V 1.33</title>
    <link rel="alternate" href="https://blogs.perl.org/users/ron_savage/2026/10/announce-perlwiki-v-156-cpanmetacurator-v-133.html" />
    <id>tag:blogs.perl.org,2026:/users/ron_savage//297.12133</id>
    <published>2026-10-11T11:32:24Z</published>
    <updated>2026-10-11T11:36:08Z</updated>
    <author>
        <name>Ron Savage</name>
        <uri>http://savage.net.au/</uri>
    </author>
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/ron_savage/">
        <![CDATA[<p>Get them at my <a href="https://savage.net.au/">Home Page's Wiki Haven.</a><br/><br />
This is the first version where ChangeLog[12] are included in the JSTree, so there are still some tiny cleanups to do...<br />
</p>]]>
        
    </content>
</entry>



<entry>
    <title>Peta::NN - Surface Detail</title>
    <link rel="alternate" href="https://blogs.perl.org/users/petamem/2026/10/petann---surface-detail.html" />
    <id>tag:blogs.perl.org,2026:/users/petamem//1658.12132</id>
    <published>2026-10-09T23:43:08Z</published>
    <updated>2026-10-10T02:13:58Z</updated>
    <author>
        <name>PetaMem</name>
        <uri>http://www.petamem.com</uri>
    </author>
    <category term="AI" scheme="http://www.sixapart.com/ns/types#category" />
    <category term="NLP" scheme="http://www.sixapart.com/ns/types#category" />
    <category term="gpu" label="gpu" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="neuralnetwork" label="neural network" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="nn" label="nn" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="pdl" label="PDL" scheme="http://www.sixapart.com/ns/types#tag" />
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/petamem/">
        <![CDATA[<p>Peta::NN is a neural network library written in Perl: models are
defined, trained, measured and shipped from a Perl script, with no
dependency beyond perl 5.36. It is not a binding to somebody else's
framework and it does not try to be PyTorch. It trains micro models -
a few thousand to a few hundred thousand weights, each answering one
narrow question about a string - and composes them: in a pipeline, or
fused into one model that runs a whole batch on the graphics card
without coming back to Perl in between. It runs on perl5 and on
pperl, on three backends, and under pperl one of them is the GPU.
Two models of 18,761 and 10,002 weights form the plural of 98.3% of
German nouns they were never shown, and of every single noun of the
core vocabulary.</p>
]]>
        <![CDATA[<h2>Why this exists</h2>

<p>PetaMem has done natural language processing in Perl for twenty-five
years, and for most of them the craft was rule sets: inflection,
language identification, diacritics, word classes. The Lingua
distributions on CPAN are the tiny public part of that. Rules have a
known weakness. They answer for what their author thought of, and the
language has more words than that.</p>

<p>A small network trained on what the rules and the lexicon say closes
that gap: it also answers for the words nobody listed. That is the
whole use case, and it is a modest one. The title is meant literally:
these models read the surface form of a word, often just its last six
characters, and nothing else.</p>

<p>Perl had no tool for this. On CPAN, <code>AI::MXNet</code> binds a framework
that has been retired, <code>AI::TensorFlow::Libtensorflow</code> runs models
trained elsewhere, the <code>AI::NeuralNet::*</code> family is old and small,
and PDL is the numeric base without a network layer on top. None of
them trains a model to a stated quality from Perl data, and none
composes models. The usual advice is to do the training in Python
and carry the result over. We did not take it.</p>

<h2>What it is not</h2>

<p>Three kinds of layer, gradients written by hand and checked against
numeric differentiation, no automatic differentiation, no tensor
library, no images, no sound, no large models. Whoever needs those has
PyTorch and Keras - for now. ;-) The library is about 7,000 lines of
Perl, documentation included, and means to stay small: three
concepts - data, a model, a chain - and one way to do a thing.</p>

<h2>A model is declared, and trained to a goal</h2>

<pre class="prettyprint lang-perl">
use Peta::NN::Data;
use Peta::NN::Model;
use Peta::NN::Chain qw(chain);

my $nouns = Peta::NN::Data->read('nouns.tsv',
    fields => [qw(singular gender plural)])->hold_out(0.2);

my $plural = Peta::NN::Model->new(
    kind  => 'edit',
    from  => 'singular',
    to    => 'plural',
    given => ['gender'],
    reads => { end => 6 },
    goal  => { unseen => 0.93 },
);
$plural->train($nouns);

print scalar $plural->predict('zeitung', gender => 'feminine');   # zeitungen
</pre>

<p>That declaration is everything the user decides: which field the
model reads, which it answers, what it is told beside the word, how
much of the word it looks at, and what it has to reach. There is no
layer list, no optimizer, no loss, no device, and no training loop.
The kind of model settles the loss. Training settles the rest: it
runs until the goal is met on nouns the model was not shown, widens
the network if it stops getting closer, and then trains a second
model from another random start to confirm that the first was not a
lucky draw.</p>

<p>An <code>edit</code> model does not generate characters. Its answer is one of
the edits it has seen - "cut two characters, add <em>en</em>" - which makes
it a classifier, and its mistakes wrong words of the right shape
(<em>Agronome</em> for <em>Agronomen</em>) and never noise.</p>

<p>The data is held to the same standard. Which records are held out
follows from a hash of a field's value, so the same noun is on the
same side on every run, every machine and every perl. And before
training, the library says what no model of that shape can learn:</p>

<pre><code>7 training pairs the model cannot tell from a pair with a
different answer; a wider window would (mutter → mütter as
magnetmutter → magnetmuttern, ...)
</code></pre>

<p><em>Mutter</em> and <em>Magnetmutter</em> agree in gender and in their last six
characters and differ in the plural. Nothing that reads six
characters gets both, so they are set aside and named, not silently
counted as failures of the training.</p>

<h2>Small, composable</h2>

<p>The philosophy in one sentence: a task that is too much for one small
model is usually several small tasks. The German dative plural is
three. Change the vowel (<em>Apfel, Äpfel</em>). Rewrite the ending (<em>Mann,
Männer</em>). Add the case (<em>Äpfeln</em>). Three models in a row are easier
to get right, and to check one by one, than one larger model that
has to learn all of it at once.</p>

<pre class="prettyprint lang-perl">
my $dative = chain(plural => $plural, case => $case);
print scalar $dative->predict('hund', gender => 'masculine',
                                      case   => 'dative');        # hunden
$dative->save('dative.chain');
</pre>

<p>A chain is itself a model. It is asked, scored, saved as one file,
loaded with one line, and can be a part of a further chain. Its
parts stay parts: one of them can be trained further, measured and
replaced alone, and the rest is untouched. Parameters travel by
name, so the chain above takes <code>gender</code> for its first part and
<code>case</code> for its second without anyone wiring them.</p>

<p>Chains are not only series. A model can judge a whole text by
pooling what it says about each word, and a model's answer can
choose which follower runs. The word-class example does both: one
model decides whether a text is Czech, German or English, and hands
each word to that language's tagger. Four models, one chain, one
file.</p>

<p>What this buys, measured on the examples in the distribution:</p>

<p><style>
table.pp { border-collapse: collapse; margin: 1em 0; }
table.pp th, table.pp td { border: 1px solid #888; padding: 3px 8px; }
table.pp td + td { text-align: right; }
table.pp.l2 td:nth-child(2) { text-align: left; }
</style></p>

<table class="pp l2" border="1" cellspacing="0">
<thead>
<tr><th>task</th><th>models</th><th>weights</th><th>not shown</th><th>core</th></tr>
</thead>
<tbody>
<tr><td>German noun, singular to plural</td><td>umlaut, ending</td><td>28,763</td><td>98.3%</td><td>100% of 9,778</td></tr>
<tr><td>and on to a plural case</td><td>+ case</td><td>29,419</td><td>98.4%</td><td>100%</td></tr>
<tr><td>plural back to singular</td><td>singular</td><td>23,783</td><td>96.2%</td><td>99.8%</td></tr>
<tr><td>Czech word classes</td><td>class-ces</td><td>130,460</td><td>93.1%</td><td>100% of 4,306</td></tr>
<tr><td>English word classes</td><td>class-eng</td><td>173,423</td><td>80.8%</td><td>100% of 3,070</td></tr>
</tbody>
</table>

<p>"Core" is the part of the vocabulary a model has to get right
without exception; a goal can say so, and training does not stop
before it holds. The 0.2% missing on the way back are plurals with
two singulars, of which only one can come back. The Czech grammar
example trains 108 such models in 87 seconds; 91 of them reach 100%
and the weakest 98.5%. The plural chain is a file of 118 kB.</p>

<p>The English row is there on purpose. 80.8% on unseen words is what a
model that reads only the spelling of an English word can know about
its class, and no amount of training will change that. The remedy is
context, which is a further model and not a larger one.</p>

<h2>Fusable</h2>

<p>A pipeline has Perl between its models: the first model's answer
comes back, Perl builds the new string, the second model gets it.
That is fine on a CPU and fatal on a GPU, where the transfer costs
more than the computation.</p>

<p>A fused chain is the same models, as they are, in one model, with
the hand-over inside it. Nothing is retrained and no weight changes.
On the CPU the fused chain agrees with the pipeline to the last bit,
answers and confidences. On the card it runs in 32-bit floats, and
over all 45,218 German nouns not one answer differs.</p>

<pre class="prettyprint lang-perl">
my $fused = Peta::NN::Chain->load('dative.chain')->on('gpu');
my @forms = $fused->predict_all(\@nouns, gender => 'neuter',
                                         case   => 'dative');
</pre>

<p>Milliseconds per string, singular to a plural case, three models, by
how many strings go through at once:</p>

<table class="pp" border="1" cellspacing="0">
<thead>
<tr><th>at once</th><th>pipeline</th><th>fused, cpu</th><th>fused, gpu</th></tr>
</thead>
<tbody>
<tr><td>1</td><td>0.197</td><td>0.176</td><td>1.52</td></tr>
<tr><td>16</td><td>0.124</td><td>0.111</td><td>0.061</td></tr>
<tr><td>256</td><td>0.199</td><td>0.185</td><td>0.011</td></tr>
<tr><td>32,768</td><td>0.170</td><td>0.136</td><td>0.016</td></tr>
</tbody>
</table>

<p>One string alone is ten times slower on the card, and from a few
hundred strings on the fused chain is ten times faster than the
pipeline. Most of what remains is Perl turning strings into numbers
and edits back into strings; the card's own share is under 0.002 ms
per string.</p>

<p>There is a third form, fused and consolidated: one model trained on
what the chain does. We tried it once - 21,865 weights for 28,828,
98.1% for 98.4% - and it is not the default, because it gives up
what the parts were for.</p>

<p>That is the bet of the project: many small models, each trained in
seconds to minutes and checked on its own, then composed, as an
alternative to training one large model - keeping what a large model
gives up, which is knowing what each part does.</p>

<h2>perl5 and pperl, three backends</h2>

<p>A backend owns the tensors and implements about a dozen operations
on whole batches. <code>plain</code> is flat Perl arrays, needs only perl, and
is bit-identical on every perl. <code>pdl</code> is the CPU workhorse. <code>gpu</code> is
one WGSL compute shader per operation on <code>Peta::WebGPU</code>, the binding
the 0.6.20 post, "The Player of Games",
introduced for the other kind of games; it needs a pperl built with
the <code>webgpu</code> feature. A saved model carries no backend and loads on
any.</p>

<p>Training the smallest model of the benchmark set (944 weights,
batches of 16), samples per second, ThinkPad P53 with a Quadro RTX
5000:</p>

<table class="pp" border="1" cellspacing="0">
<thead>
<tr><th></th><th>plain</th><th>pdl</th><th>gpu</th></tr>
</thead>
<tbody>
<tr><td>pperl</td><td>31,400-32,000</td><td>48,400-51,400</td><td>22,300-24,200</td></tr>
<tr><td>pperl <code>--no-jit</code></td><td>5,730-5,840</td><td>49,400-51,700</td><td>22,400-24,400</td></tr>
<tr><td>perl 5.44</td><td>5,230-5,430</td><td>not installed</td><td>none</td></tr>
<tr><td>perl 5.42</td><td>4,420-4,570</td><td>38,600-40,100</td><td>none</td></tr>
</tbody>
</table>

<p>Two readings. In plain Perl the JIT is worth a factor of six: the
same nested loops over Perl arrays, unchanged source. And the card
loses. A training step on the GPU costs about a millisecond whatever
is in it, so for a model this small the CPU is the faster engine,
and it stays that way until the model or the batch grows:</p>

<table class="pp" border="1" cellspacing="0">
<thead>
<tr><th>weights</th><th>batch 16: pdl</th><th>gpu</th><th>batch 128: pdl</th><th>gpu</th><th>batch 512: pdl</th><th>gpu</th></tr>
</thead>
<tbody>
<tr><td>944</td><td>56,000</td><td>17,200</td><td>160,400</td><td>123,400</td><td>229,900</td><td>306,000</td></tr>
<tr><td>4,136</td><td>35,900</td><td>15,700</td><td>54,500</td><td>86,300</td><td>60,500</td><td>226,600</td></tr>
<tr><td>19,080</td><td>14,800</td><td>14,500</td><td>22,800</td><td>104,700</td><td>24,400</td><td>386,300</td></tr>
<tr><td>37,768</td><td>8,600</td><td>14,500</td><td>12,800</td><td>109,200</td><td>13,300</td><td>615,500</td></tr>
</tbody>
</table>

<p>At batches of 16 the two meet at about 20,000 weights, at 128 at
about 2,000, and at 512 the card is ahead for every model, 46 times
at the bottom right. Further up, at 1.6 million weights, a training
step takes 31-38 ms on the card and 1.5 s on PDL.</p>

<p>Nobody should have to carry that table in their head, so a model
that is not told where to train finds out: when it is trained, the library times a few
steps on every backend this perl and this machine have, and goes on
where a step is fastest. It assumes nothing about the order. A
graphics card may not be there, PDL may not be installed, perl5 has
no JIT, and each of the three wins somewhere.</p>

<h2>Keeping the card busy</h2>

<p>The first GPU run of the word-class example took 42 minutes, with
the card idle most of the time: a second of training, then six
seconds of one CPU core checking the model. It takes 4 minutes now.
What brought it there was no faster shader but fewer crossings: the
check of an epoch runs on the card, through a fused model of that
one model, and the samples go up once and stay there, a step picking
its batch on the card.</p>

<p>One thing that looked right and was not: gathering the embedding
gradient in one pass per table row is a sixteenth of the work and
ran five times slower. A hundred long loops do not fill a card;
sixteen hundred short ones do.</p>

<h2>Try it</h2>

<p>Peta::NN is on CPAN, with the German noun data and the three trained
chains in the distribution, so the getting-started page runs as
printed:</p>

<pre><code>cpanm Peta::NN
</code></pre>

<p>Every block of code in the documentation is run by a test, and its
output is what the page shows. The suite is 774 tests on pperl; on
a perl without PDL or without a card it skips what needs them and
passes on the plain backend alone.</p>

<p>The caveats, as always. It is early and there is no API promise yet.
A model asked about something outside its world answers anyway: the
three-language identifier shown French picks one of its three, sure
of itself. And these are models of spelling; what needs a sentence
to decide is not in them yet.</p>

<p><strong>Do not take our word for any number in this post.</strong> The German
noun example ships with its data and trains on your perl, on
whatever backend you have, in a few minutes.</p>

<ul>
<li>Richard C. Jelinek, PetaMem s.r.o.</li>
</ul>
]]>
    </content>
</entry>



<entry>
    <title>Minion: The Silent Engine That Turns Perl Into a Modern Architecture Platform</title>
    <link rel="alternate" href="https://blogs.perl.org/users/danimera/2026/10/minion-the-silent-engine-that-turns-perl-into-a-modern-architecture-platform.html" />
    <id>tag:blogs.perl.org,2026:/users/danimera//4791.12130</id>
    <published>2026-10-06T01:16:09Z</published>
    <updated>2026-10-06T01:18:23Z</updated>
    <author>
        <name>Dani Otelch</name>
        <uri>https://www.kreatibox.com.quarantine</uri>
    </author>
    <category term="minion" label="minion" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="perl" label="perl" scheme="http://www.sixapart.com/ns/types#tag" />
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/danimera/">
        <![CDATA[<p><br />
By GoPerl — Architecture, Perl, Go & Node.js for companies that need reliable and scalable systems.</p>

<p></p>

<p>In today’s modern systems, especially in web development, new stacks and new developers grow up with the idea of async/await—in other words, asynchronous systems. Many adopt the concept without fully understanding what lies behind it. The simplest goal is clear: make your application feel fast and responsive, without leaving users waiting.</p>

<p>Let’s take this into the enterprise world, where speed, scalability and resilience define technological competitiveness. One concept that immediately enters the conversation is job queues. Behind every high‑performance system, there is always a subsystem responsible for processing heavy tasks without blocking the user experience.</p>

<p>Within the Perl ecosystem, that subsystem has a name: Minion.</p>

<p>Minion provides a modern job queue with SQL persistence, real asynchronous execution and a simple distributed model. This makes it a more robust and easier‑to‑operate alternative compared to Redis‑based solutions like Sidekiq or BullMQ.</p>

<p>Key Concepts<br />
Job Queue  <br />
A job queue is a system that schedules, stores and executes tasks asynchronously—outside the main flow of the application.</p>

<p>Distributed System  <br />
Distributed means work is shared across multiple processes or machines, all cooperating to process the queue.</p>

<p>Scalable System  <br />
Scalable means it can grow without breaking and without forcing you to rewrite your architecture.</p>

<p>Minion is a modern system designed to run background tasks in an asynchronous, reliable and distributed way. It’s not “just another plugin”—it’s an architectural component that allows Mojolicious applications to operate with engineering standards comparable to Node.js, Go or Ruby on Rails.</p>

<p>What Minion Brings<br />
Real SQL persistence (PostgreSQL, MySQL, SQLite)</p>

<p>Distributed workers</p>

<p>Automatic retries</p>

<p>Native dashboard (a big plus)</p>

<p>Direct integration with Mojolicious</p>

<p>This makes it an ideal solution for companies seeking to reduce operational complexity without sacrificing scalability.</p>

<p>Architectural Patterns Where Minion Shines<br />
Minion is not simply “a job queue”. It’s an enabler of modern architecture:</p>

<p>1. CQRS (Command & Query Responsibility Segregation)<br />
Heavy commands are delegated to Minion, while queries remain fast and lightweight. Perfect for reports, document generation and intensive processes.</p>

<p>2. Event‑Driven Architecture (EDA)<br />
Business events can trigger one or multiple jobs: “user registered”, “payment confirmed”, “file processed”. Minion acts as an event processor and resilience mechanism.</p>

<p>3. Microservices with Asynchronous Tasks<br />
Minion works as an internal queue for each service, avoiding heavy infrastructure like Kafka or RabbitMQ.</p>

<p>4. Saga Pattern (Distributed Transactions)<br />
Break complex processes into independent steps with automatic compensations if something fails.</p>

<p>5. Batch Processing<br />
Massive data processing, migrations, calculations and external API synchronizations.</p>

<p>6. Task Orchestration (Workflows)<br />
Enterprise pipelines without external tools like Airflow.</p>

<p>Why Minion Outperforms Other Alternatives<br />
Using a tool correctly often provides advantages over something “bigger” or more popular. This isn’t about which one is better—it’s about the benefits Minion offers compared to Sidekiq, BullMQ, Kafka or RabbitMQ.</p>

<p>Minion provides:</p>

<p>More robust SQL persistence</p>

<p>Fewer external dependencies</p>

<p>Integrated dashboard</p>

<p>Native Mojolicious integration</p>

<p>Lower operational complexity</p>

<p>Cleaner architecture for small and medium teams</p>

<p>Minion doesn’t compete with Kafka in massive throughput. It competes in simplicity, reliability and speed of implementation.</p>

<p>Implementing Minion is straightforward, and everything comes in a single package with no external dependencies.</p>

<p>Strategic Advantage for Companies<br />
Adopting Minion means:</p>

<p>Faster responses for users</p>

<p>Heavy processes isolated and controlled</p>

<p>Better fault tolerance</p>

<p>Horizontal scalability without rewriting the application</p>

<p>Less infrastructure to maintain</p>

<p>A clear path to modernize Perl systems without expensive migrations</p>

<p>In an environment where efficiency and resilience matter, Minion turns Perl into a competitive platform for modern architectures.</p>

<p>If you already have legacy Perl systems, Minion can modernize them without rewriting code—just by adding a new architectural layer, you gain a modern, optimized and scalable system.</p>

<p>Let’s look at a complete example using Minion from scratch.</p>

<p>Installing and Using Minion in a Mojolicious Project (Step by Step)<br />
1. Install Perl and Mojolicious<br />
You need Perl and the Mojolicious framework installed before using Minion.</p>

<p>On Windows 11, use WSL</p>

<p>On Linux, Perl is usually already installed</p>

<p>Check Perl: perl -v</p>

<p>Install Mojolicious: cpanm Mojolicious</p>

<p>Install Minion: cpanm Minion</p>

<p>Install SQLite: cpanm DBD::SQLite</p>

<p>2. Create a Mojolicious Project<br />
Generate the base structure of your web application.</p>

<p>Run: mojo generate app MyApp</p>

<p>Enter the project: cd MyApp</p>

<p>Verify lib/MyApp.pm and script/my_app exist</p>

<p>3. Configure Minion in the Application<br />
Enable Minion and define the SQL backend.</p>

<p>Open lib/MyApp.pm</p>

<p>Add:</p>

<p>perl<br />
$self->plugin('Minion' => { Pg => 'postgresql://user:pass@localhost/db' });<br />
Or use SQLite for testing:</p>

<p>perl<br />
$self->plugin('Minion' => { SQLite => 'myapp.db' });<br />
4. Create a Sample Job<br />
Define a task to be executed by workers.</p>

<p>perl<br />
$self->minion->add_task(send_email => sub ($job, $address) {<br />
  sleep 2;<br />
  $job->finish("Email sent to $address");<br />
});<br />
5. Start the Mojolicious Server<br />
Run the application to test the endpoint.</p>

<p>morbo script/my_app</p>

<p>Open: http://localhost:3000/example/queue</p>

<p>6. Run Minion Workers<br />
Workers process jobs in the background.</p>

<p>In another terminal: perl script/my_app minion worker</p>

<p>Run as many workers as needed</p>

<p>You’ll see logs like: Job send_email finished</p>

<p>7. Open the Minion Dashboard<br />
Monitor jobs, workers, errors and retries.</p>

<p>Visit: http://localhost:3000/minion</p>

<p>8. Test Failures and Retries<br />
Simulate errors to understand system resilience.</p>

<p>Modify the job: die "Error";</p>

<p>Enqueue a job and watch Minion retry automatically</p>

<p>9. Scale Workers<br />
Increase processing capacity by running multiple workers.</p>

<p>Open several terminals and run: minion worker</p>

<p>Each worker will pick different jobs</p>

<p>This demonstrates horizontal scalability</p>

<p>You can test everything with a simple POST request to:<br />
http://localhost:3000/send_email</p>]]>
        
    </content>
</entry>



<entry>
    <title>This week in PSC (238) | 2026-10-05</title>
    <link rel="alternate" href="https://blogs.perl.org/users/psc/2026/10/this-week-in-psc-238-2026-10-05.html" />
    <id>tag:blogs.perl.org,2026:/users/psc//4112.12129</id>
    <published>2026-10-05T20:38:48Z</published>
    <updated>2026-10-05T21:04:41Z</updated>
    <author>
        <name>Perl Steering Council</name>
        
    </author>
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/psc/">
        <![CDATA[<p>The three of us discussed:</p>

<ul>
<li>There is overall agreement that <code>@INC</code> handling should be improved, but there are still outstanding questions and issues that overlap with build modules, installer tools, and so on. Plan is to publish a partial document so far as a draft PR and get others to comment further.</li>
<li>The recent change to improve the accuracy of <code>caller</code> reported line numbers in expressions involving anonymous subroutines (<a href="github.com/Perl/perl5/pull/24396">Pull #24396</a>) has caused more BBC fallout than might have been expected. So far it doesn&#8217;t need any immediate response from PSC but we continue to keep an eye on it.</li>
</ul>

<p>[<a href="https://www.nntp.perl.org/group/perl.perl5.porters/;msgid=20261005164026.117c761f@vel.leo">P5P posting of this summary</a>]</p>
]]>
        

    </content>
</entry>



<entry>
    <title>Perl documentation (including POD) with Markdown and Docbook</title>
    <link rel="alternate" href="https://blogs.perl.org/users/aspeer/2026/10/perl-documentation-including-pod-with-markdown-and-docbook.html" />
    <id>tag:blogs.perl.org,2026:/users/aspeer//4880.12128</id>
    <published>2026-10-05T18:53:54Z</published>
    <updated>2026-10-05T19:21:19Z</updated>
    <author>
        <name>Andrew Speer</name>
        
    </author>
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/aspeer/">
        <![CDATA[<h1>Introduction</h1>

<p>I have uploaded to CPAN some tooling modules which allow Perl documentation to be authored as Markdown files - or Docbook XML articles - then merged into source files as POD. They can also then be published as HTML to static sites such as Github pages or Cloudflare Workers.</p>

<p>TL;DR here is an example of the end result - <a href="https://aspeer.github.io/pm-Cloudflare-API/">documention for the Cloudflare::API module</a> created from the <code>doc/</code> directory in the <a href="https://github.com/aspeer/pm-Cloudflare-API">GitHub repository</a> and POD for the command-line tool created from a <a href="https://github.com/aspeer/pm-Cloudflare-API/blob/main/bin/cloudflare-api.md">Markdown file</a> in the same repository. </p>

<h2>Perl Documentation</h2>

<p>Documentation can be tedious - despite knowing how important good documentation is, and that it can make or break open-source software. Nothing is worse than trying to run "<code>man Some::Module</code>" (or "<code>perldoc Some::Manual</code>") and getting nothing back.</p>

<p>POD is the "linga franca" of the Perl world for a good reason - it has stood the test of time, is completely integrated into the Perl ecosystem and is an excellent format for creating manual pages for Perl modules and scripts.</p>

<p>However arguably it doesn't translate well into the modern world of GitHub and other revision control systems, where Markdown is natively rendered by front-end browsers - and it's difficult for users to parse POD from the source code of your Perl file if they just want to understand what it does.</p>

<h2>Markdown as POD</h2>

<p>I have been using Markdown (and Docbook - more on that later) for documentation in Perl projects for a while now, and have released some tooling CPAN modules that allow Markdown sidecar files to be merged into their source equivalent as POD.</p>

<p>What is a Markdown sidecar file ? It's simply the same name as the source file with a <code>.md</code> extension appended. </p>

<p>Here is what a typical Perl distribution source repository may look like when using Markdown sidecar documentation:</p>

<pre><code>Example-Client/
├── Makefile.PL
├── doc/
│   ├── example-client.xml
│   ├── example-client.md
│   └── images/
│       └── request-flow.svg
├── lib/
│   └── Example/
│       ├── Client.pm
│       └── Client.pm.md
└── bin/
    ├── example-client
    └── example-client.md
</code></pre>

<p>Ignoring the <code>doc/</code> directory for the moment (more on that later) we can see that in the <code>lib/</code> and <code>bin/</code> directories each file has an associated sidecar companion file with a <code>.md</code> extension.</p>

<p>Using the CPAN <code>Markdown::Pod::Embed</code> module we can:</p>

<pre><code># Install the module
cpanm Markdown::Pod::Embed

# Makes the markpod command available
markpod --inplace --nobackup --recursive lib bin
</code></pre>

<p>And all Markdown in the companion sidecar files will be converted to POD and appended to their source files. </p>

<p><strong>WARNING: Any existing POD in the source files will be replaced !</strong></p>

<h2>Makefile targets for POD merge</h2>

<p>Running the <code>markpod</code> command is a bit cumbersome. If you install the supporting CPAN MakeMaker extension module <code>ASPEER::MakeMaker::Markdown::Pod</code> can run <code>make doc</code> as a Makefile target:</p>

<pre><code># Install the module
cpanm ASPEER::MakeMaker::Markdown::Pod

# Build Makefile with Markdown::Pod::Embed extension
perl -MASPEER::MakeMaker::Markdown::Pod Makefile.PL

# Make the documentation
make doc
</code></pre>

<p>This will convert all module and script sidecar files to POD and embed them into their original source files.</p>

<p>Why use the author namespace ? In short I did want to trample over the top level MakeMaker namespace, nor did I want to claim the ExtUtils::Markdown::Pod namespace.</p>

<p>As a bonus if "<code>make doc</code>" detects a <code>README.md</code> file in the distribution root it will convert it (assuming <code>pandoc</code> is installed) to a plain text <code>README</code> file.</p>

<h2>What about other documentation ?</h2>

<p>Sometimes a <code>README.md</code> or "<code>perldoc Module::Name</code>" isn't enough. You've got a raft of information you want to convey about how your module works, examples, screenshots, code demos etc.</p>

<p>Simple enough. Put it in the <code>doc/</code> folder as Markdown and it will be available to anyone browsing the Github repository in a format which is reasonably viewable. </p>

<p>Usually that is fairly good, but it would be nice to publish it to Github pages - or another static site - in a simple way. This is where the CPAN module <code>Markdown::Publish</code> comes it. It will convert Markdown files in <code>doc/</code> to static HTML via publishing engines such as Mkdocs Material, Docusaurus, Vitepress and Astro Starlight and push them to either Github Pages or (if you have an account) Cloudflare Worker Pages.</p>

<p>The <code>Markdown::Publish</code> module will split your Markdown into pages/sections at the heading boundary and sluggify them for navigation.</p>

<p>Assuming you have the directory layout above (and all system prerequisites such as Mkdocs Material installed):</p>

<pre><code># Install the module
cpanm Markdown::Publish

# Build the site
markdown-publish build

# Preview locally
markdown-publish serve

# Push to Github pages
markdown-publish gh-push
</code></pre>

<h2>Makefile targets for Publishing</h2>

<p>In the same way there is a module to create Makefile targets for the POD merge, there is one for Markdown publishing:</p>

<pre><code># Install the module
cpanm ASPEER::MakeMaker::Markdown::Publish

# Build Makefile with Markdown::Publish extension
perl -MASPEER::MakeMaker::Markdown::Publish Makefile.PL

# Make the documentation and push to Github pages
make publish_gh-push
</code></pre>

<h2>Docbook</h2>

<p>Sometimes Markdown isn't sufficient for documentation, or using it for large documents is unwieldy. Docbook (specifically a Docbook Article) can be a good format for technical writing, and provides a wider semantic context for material in the article vs Markdown. </p>

<p>Docbook can be eaiser to edit with tools like the <a href="https://xmlmind.com/xmleditor/">XXE XMLMind</a> Visual Docbook editor (sadly going out of support in 2027 but still available free for non-commercial use).</p>

<p>The CPAN <code>Docbook::Convert</code> module can convert Docbook articles to Markdown, which is then published through the standard toolchain.</p>

<p>Assuming the <code>Docbook::Convert</code> module is installed (along with required system binaries such as <code>pandoc</code> ) the <code>MakeMaker::Markdown::Pod</code> helper module will convert any Docbook articles to Markdown, allow publishing.</p>

<h2>Examples</h2>

<p>You can see and example of the <code>Markdown::Publish</code> module using several of these techniques itself. The Markdown::Publish <a href="https://github.com/aspeer/pm-Markdown-Publish">Github repository</a> contains a <code>doc/</code> directory with a Docbook XML file which has been converted to Markdown. That Markdown is then published to the modules <a href="https://aspeer.github.io/pm-Markdown-Publish/">Github pages</a> location as documentation.</p>

<p>You can also see the Markdown sidecar files in the <a href="https://github.com/aspeer/pm-Markdown-Publish/tree/main/bin">bin/</a> directory and the embedded POD in the <a href="https://github.com/aspeer/pm-Markdown-Publish/blob/main/bin/markdown-publish">markdown-publish</a> script (at the bottom of the file)</p>

<p>These modules are also used to publish the <a href="https://webdyne.org/00_introduction/">WebDyne documentation</a> from <a href="https://github.com/aspeer/WebDyne">WebDyne GitHub repository</a> and the <a href="https://aspeer.github.io/pm-Cloudflare-API/">Cloudflare::API</a> documentation from the <a href="https://github.com/aspeer/pm-Cloudflare-API">Cloudflare::API GitHub repository</a>.</p>

<h2>AI Disclaimer</h2>

<p>Many of these modules predate AI and are human coded. The Docbook::Convert and Markdown::Pod::Embed are largely human but have AI bugfixes incorporated. The ASPEER::MakeMaker family of modules are almost all human coded. The Markdown::Publish module was mostly human coded for MkDocs publishing, but then AI was  tasked with with adding support for other publishing engines and decoupling from MkDocs specifically.</p>
]]>
        

    </content>
</entry>



<entry>
    <title>Still saving CPU cycles 32 years later: Rediscovering the Schwartzian Transform in Flutter</title>
    <link rel="alternate" href="https://blogs.perl.org/users/randal_l_schwartz/2026/10/still-saving-cpu-cycles-32-years-later-rediscovering-the-schwartzian-transform-in-flutter.html" />
    <id>tag:blogs.perl.org,2026:/users/randal_l_schwartz//61.12127</id>
    <published>2026-10-02T23:56:10Z</published>
    <updated>2026-10-02T23:59:24Z</updated>
    <author>
        <name>Randal L. Schwartz</name>
        <uri>http://www.stonehenge.com/merlyn/	</uri>
    </author>
    <category term="algorithms" label="algorithms" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="dart" label="dart" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="flutter" label="flutter" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="performance" label="performance" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="perl" label="perl" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="schwartziantransform" label="schwartzian transform" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="usenet" label="usenet" scheme="http://www.sixapart.com/ns/types#tag" />
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/randal_l_schwartz/">
        <![CDATA[<p>A few days ago, an article by Flutter developer Hammad Tariq caught my eye: <em>"Rendering 10,000 Machine State Changes on a Timeline Without Freezing Flutter"</em>.</p>

<p>It was a solid piece of profiling detective work. Hammad was building a high-density timeline view in Flutter representing 10,000 machine state transitions. But every time the dataset loaded, the mobile UI stuttered and dropped dozens of frames.</p>

<p>Under a section aptly titled <strong>"The Original Sin"</strong>, the author identified the culprit:</p>

<pre><code>sorted.sort((a, b) =&gt; DateTime.parse(a.start).compareTo(DateTime.parse(b.start)));</code></pre>

<p>Because sorting makes O(N log N) comparisons, sorting 10,000 items was triggering over <strong>215,000 redundant calls</strong> to <code>DateTime.parse()</code> right inside the comparison callback, blowing past the 16ms frame budget and freezing the screen.</p>

<p>To fix the frame freeze, Hammad introduced a clever optimization: map each item to a temporary helper object holding the pre-computed timestamp, sort the list using that cached value, and unwrap it:</p>

<pre><code>// Step 1: Map to a wrapper holding the pre-computed sort key
final parsed = widget.activity.map(_ParsedActivity.new).toList();

<p>// Step 2: Sort using the cheap, cached key<br />
parsed.sort((a, b) =&gt; a.startUtc.compareTo(b.startUtc));</p>

<p>// Step 3: Unwrap and process down the pipeline</code></pre></p>

<p>The UI freeze disappeared. The timeline ran smoothly.</p>

<p>When I read that section, I couldn't help but grin. Watching an engineer independently discover an algorithmic optimization from first principles is pure joy--especially when it's the exact <strong>Map &rarr; Sort &rarr; Map</strong> idiom I posted to Usenet (<code>comp.lang.perl</code>) back in 1994, which Tom Christiansen dubbed the <strong>Schwartzian Transform</strong>!</p>

<p>I dropped a friendly comment on the article letting them know their optimization has a 32-year history. And then I wrote up a full deep dive exploring how modern Dart 3 language features--specifically <strong>Records</strong> and <strong>Extension Methods</strong>--bring the idiom full circle, turning it into a type-safe, zero-boilerplate one-liner that runs 13x faster than naive sort.</p>

<p>I also dive into why Dart's official <code>package:collection</code> <code>sortedBy()</code> method doesn't cache keys either (and still burns 127,000 redundant evaluations), and link to the classic <a href="https://www.perl.com/article/the-history-of-the-schwartzian-transform/">History of the Schwartzian Transform on Perl.com</a>.</p>

<p>Whether you know me from the Camel and Llama books or my recent work as a Flutter GDE, it's deeply satisfying to see a 1994 Usenet idiom still saving CPU cycles on a 120Hz smartphone display in 2026.</p>

<p><strong>Read the full article with benchmarks and code on DEV.to:</strong><br />
<a href="https://dev.to/gde/rediscovering-the-schwartzian-transform-why-i-had-to-comment-on-a-flutter-performance-article-30l0">https://dev.to/gde/rediscovering-the-schwartzian-transform-why-i-had-to-comment-on-a-flutter-performance-article-30l0</a></p>
]]>
        
    </content>
</entry>



<entry>
    <title>pperl 0.6.21 - addendum</title>
    <link rel="alternate" href="https://blogs.perl.org/users/petamem/2026/10/pperl-0621---addendum.html" />
    <id>tag:blogs.perl.org,2026:/users/petamem//1658.12126</id>
    <published>2026-10-02T02:52:23Z</published>
    <updated>2026-10-02T02:56:34Z</updated>
    <author>
        <name>PetaMem</name>
        <uri>http://www.petamem.com</uri>
    </author>
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/petamem/">
        <![CDATA[<p>This blog entry has no release attached to it. 0.6.21 is an internal
version, not for release. In the four days since 0.6.20 a few things
happened, but this post is not about new things. It is about old
things the 0.6.20 post forgot, about how a project moving at this
pace should announce anything at all, and about a question that
deserves an answer in public: who pperl is for, at a time when you
can ask an AI to port your Perl script to the language of your choice
and be done.</p>
]]>
        <![CDATA[<h2>What the last post left out</h2>

<p>"The Player of Games" covered the Benchmarks Game scoreboard,
Memoize, the GPU binding and four bullet points. The two months it
reported on contained more than that. The following landed between
0.6.8 and 0.6.20, is in the binary you can download, and was not
mentioned:</p>

<ul>
<li><strong>The JIT rebuild is complete.</strong> The restart announced in
<a href="https://blogs.perl.org/users/petamem/2026/08/pperl-0612---lets-play-a-game.html">Shall we play a game?</a>
landed, and the previous engine was deleted, about 19,700 lines of
it. There is one JIT in the tree.</li>
<li><strong>Encode</strong> went from 21 encodings to all 124 of the upstream
distribution: the single-byte tables generated from the Unicode
.ucm data, UTF-16/32 and UCS-2, the 21 multi-byte CJK encodings,
ISO-2022, UTF-7 and the MIME encoded-word codecs.</li>
<li><strong>Storable</strong> writes and reads perl5's format, byte for byte,
including hooks, code references, regexps and the pre-0.7 streams.
A file stored by perl5 retrieves on pperl and the other way round.</li>
<li><strong>POSIX, Fcntl and Socket</strong> are complete against upstream's export
lists: locking, <code>sigaction</code>, the termios constants, the socket
option packers, <code>sockaddr_un</code>.</li>
<li><strong>DBD::SQLite and DBD::MariaDB</strong> joined the drivers of the 0.6.8
post, with DBD::mysql now the compatibility layer over the latter.
DBD::Pg gained arrays, COPY, savepoints and asynchronous queries.
Peta::FFI gained closures, which is what SQLite's user-defined
functions run on.</li>
<li><strong>Coro 6.57</strong> runs, the whole stack: EV 4.37 with libev 4.33 and
Event 1.28 underneath, AnyEvent and Guard beside it.</li>
<li><strong>HTTP::Parser::XS and Time::Piece</strong> are native.</li>
<li><strong>The command line</strong>: the program is read from stdin the three ways
perl5 reads it, the <code>-d</code> family reaches the debugger, <code>-s</code>, <code>-T</code>,
<code>-t</code>, <code>-W</code> and <code>-X</code> behave as perl5's do, an assignment to <code>$0</code>
rewrites the process title, and <code>--cpan</code> installs modules.</li>
<li><strong>The distributions' own test suites</strong> run inside our harness: 28
modules, 440 files, each compared against perl 5.44 running the
same file.</li>
</ul>

<p>The four days since 0.6.20 added 147 commits, among them Net::SSLeay
with IO::Socket::SSL, the DBM family and the parallelizer on the new
JIT (mandelbrot 10.85s -> 4.2s wall time). They belong to the next
release post, if it remembers them.</p>

<h2>The compression problem</h2>

<p>The list above is in a different form from our usual posts, on
purpose.</p>

<p>0.6.20 was 1,374 commits and about 454,000 lines of code change.
The post announcing it was 12 kB, which is under nine bytes per
commit. No prose compresses that far. What the post did instead was
select: one topic in depth, two more in a few paragraphs, and the
rest dropped. The selection followed what we had been working on in
the last weeks before the release, and whatever was finished in
August was no longer in view in late September. That is how a
complete Encode goes missing.</p>

<p>There are three ways out, as far as we can see.</p>

<p><strong>Less prose, more changelog.</strong> The nine items above take about
2 kB, a sixth of the last post. It is the efficient form, and it is
substantially drier. A reader learns that Storable is byte-compatible and does not
learn why it was not before, what the bump arena under binary-trees
has to prove, or why asynchronous compilation lost. Those are the
parts we would want to read ourselves.</p>

<p><strong>More posts, smaller ones.</strong> A post per topic, when the topic is
finished, as
<a href="https://blogs.perl.org/users/petamem/2026/08/pperl-doing-a-rejit.html">Doing a REJIT</a>
was. Nothing gets forgotten that way, because nothing waits for a
release. The cost is on the reader's side: at the current pace this
would be a post every few days, on a blog shared with other people.</p>

<p><strong>Both, in different places.</strong> The complete record goes into a
changelog that ships with each release and lives on the website,
written when the work lands and not reconstructed afterwards. The
blog post no longer tries to be that record. It links to it, and
spends its 12 kB on the two or three things that are worth an
explanation.</p>

<p>We lean towards the third. It keeps the prose where prose earns its
space, and it removes the selection problem, since leaving something
out of the post no longer means leaving it out. This post is the
experiment: the section above is the changelog form, this section is
the other one. If you read these posts and prefer one to the other,
the comment field is open.</p>

<h2>Who is this for, when AI can port your script?</h2>

<p>A language model can transcribe code: small programs, medium ones,
and with enough patience large ones. Whoever needs a Perl program to
exist in another language can have that today, and nothing in this
project argues against it.</p>

<p>What a language model cannot do is miracles. Give the best one
available the task of writing a Perl entry for the Benchmarks Game
that is valid under the Game's rules and beats the fastest node.js
entry, and on perl5 it will not deliver, today or in the foreseeable
future. On n-body the Game lists the fastest Perl entry at about
seven minutes and node.js under nine seconds, and no arrangement of
Perl source closes a gap of that size, whoever or whatever writes
it. A top Formula 1 driver does not get a Lada Niva to 300 km/h
either, and nobody concludes that the driver is the problem.</p>

<p>The scoreboard of the last post is the same statement from the other
side. Those ten programs are Perl, they run on perl5 as well, and on
pperl nine of them are ahead of the fastest scripted entry and the
tenth is level with it. The source did not become cleverer in
between; the car changed.</p>

<p>That is what pperl is: an option for Perl. Where the requirement
reads "do X with Perl" and X is out of perl5's reach, X may be within
pperl's. X can be speed, as above. It can be ease of use: a database
script that connects without a driver build, a C compiler or an XS
toolchain on the machine. It can be functionality: a GPU from Perl,
or a hash whose integer-key access compiles to a probe in a register.
Where perl5 reaches X, perl5 remains a perfectly good answer, and
where no Perl is required at all, a port may be the shorter way.
pperl widens the set of things for which Perl can be the answer, and
a language model, however good, can only work inside that set.</p>

<ul>
<li>Richard C. Jelinek, PetaMem s.r.o.</li>
</ul>
]]>
    </content>
</entry>



<entry>
    <title>Dancer2 2.2.2 Released</title>
    <link rel="alternate" href="https://blogs.perl.org/users/jason_a_crome/2026/09/dancer2-222-released.html" />
    <id>tag:blogs.perl.org,2026:/users/jason_a_crome//712.12124</id>
    <published>2026-09-29T04:48:16Z</published>
    <updated>2026-09-29T04:51:16Z</updated>
    <author>
        <name>Jason A. Crome</name>
        <uri>https://cromedome.net</uri>
    </author>
    <category term="dancer" label="dancer" scheme="http://www.sixapart.com/ns/types#tag" />
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/jason_a_crome/">
        <![CDATA[<p>(yes, that's a lot of twos!)</p>

<p>Dancer2 2.2.2 is now headed to <a href="https://metacpan.org/pod/Dancer2">CPAN</a>. This is a small bug fix release.</p>

<h2>Bug Fixes</h2>

<ul>
<li>Fixed a crash when using class-method serializers (<a href="https://github.com/PerlDancer/Dancer2/issues/1837">GH #1837</a>)</li>
<li>Fixed test failures in <code>gen.t</code> affecting CPAN Testers and Debian builds (<a href="https://github.com/PerlDancer/Dancer2/issues/1843">GH #1843</a>)</li>
</ul>

<h2>Documentation</h2>

<ul>
<li>Updated a contributor's name in the documentation (<a href="https://github.com/PerlDancer/Dancer2/pull/1844">PR #1844</a>)</li>
</ul>

<p>There are no security fixes, enhancements, or deprecations in this release.</p>

<p>Thanks to David Precious and Nova Patch for their contributions.</p>

<p>As always, bug reports and pull requests are welcome on <a href="https://github.com/PerlDancer/Dancer2">GitHub</a>.</p>

<p>Jason, on behalf of the Dancer Core Team</p>
]]>
        

    </content>
</entry>



<entry>
    <title>This week in PSC (237) | 2026-09-28</title>
    <link rel="alternate" href="https://blogs.perl.org/users/psc/2026/09/this-week-in-psc-237-2026-09-28.html" />
    <id>tag:blogs.perl.org,2026:/users/psc//4112.12123</id>
    <published>2026-09-28T20:30:34Z</published>
    <updated>2026-09-28T21:47:44Z</updated>
    <author>
        <name>Perl Steering Council</name>
        
    </author>
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/psc/">
        <![CDATA[<p>All three of us (plus Graham&#8217;s cat) met as usual.</p>

<ul>
<li>Discussions about some case-matching syntax ideas continue on the mailing list. PSC is following along but for now there does not seem to be a need to come to an official response.</li>
<li>From this it sounds like finding a better solution to controlling Perl&#8217;s autovivification (especially in deep hash accesses) is a useful goal and can be pursued independently. Potentially addressed by optional chaining (<a href="https://github.com/Perl/PPCs/blob/main/ppcs/ppc0021-optional-chaining-operator.md">PPC0021</a>).</li>
<li>Adding a <code>builtin::rand_bytes</code> function as per <a href="https://github.com/Perl/perl5/pull/24722
">PR24722</a> sounds worthwhile. There are portability concerns that still need to be addressed on the subject of platform-dependent entropy sources (such as on VMS).</li>
</ul>

<p>[<a href="https://www.nntp.perl.org/group/perl.perl5.porters/;msgid=20260928163140.3f8b8d8a@vel.leo">P5P posting of this summary</a>]</p>
]]>
        

    </content>
</entry>



<entry>
    <title>ANNOUNCE: Perl.Wiki V 1.54, Mojo.Wiki V 1.26</title>
    <link rel="alternate" href="https://blogs.perl.org/users/ron_savage/2026/09/announce-perlwiki-v-154-mojowiki-v-126.html" />
    <id>tag:blogs.perl.org,2026:/users/ron_savage//297.12122</id>
    <published>2026-09-28T11:59:46Z</published>
    <updated>2026-09-28T12:08:19Z</updated>
    <author>
        <name>Ron Savage</name>
        <uri>http://savage.net.au/</uri>
    </author>
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/ron_savage/">
        <![CDATA[<p>From my <a href="https://savage.net.au/">Wiki Haven</a> you can now download:<br />
<ul><br />
    <li>Perl.Wiki V 1.55</li><br />
    <li>cpan.metacurator.tree.html V 1.32</li><br />
    <li>Mojo.Wiki V 1.26</li><br />
</ul><br />
The basic purpose of these is to deal with the upgrade of TiddlyWiki from V 4 to V5.<br /><br />
Up to V 4, CamelCase words were treated as links to be clicked.<br /><br />
From V 5. all links to be clicked must be in double brackets: [[...]].<br /><br />
I have updated Perl.Wiki & Mojo.Wiki to use [[...]] and will progressively upload updated versions of the other wikis to match.<br />
</p>]]>
        
    </content>
</entry>



<entry>
    <title>Simultaneous Solutions (GLAM part 1)</title>
    <link rel="alternate" href="https://blogs.perl.org/users/saif/2026/09/simultaneous-solutions.html" />
    <id>tag:blogs.perl.org,2026:/users/saif//3152.12116</id>
    <published>2026-09-28T02:49:16Z</published>
    <updated>2026-09-26T18:41:32Z</updated>
    <author>
        <name>Saif</name>
        <uri>https://github.com/saiftynet</uri>
    </author>
    <category term="games" label="Games" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="glam" label="GLAM" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="glfw" label="GLFW" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="sdl" label="SDL" scheme="http://www.sixapart.com/ns/types#tag" />
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/saif/">
        <![CDATA[<h3>New York</h3>

<p>Last week I went to visit my teen nephew and niece in New York.  A wonderful city of contrasts, where opulence blends with poverty, with shops selling a margherita for 2 bucks a slice next to a place offering margaritas at 20 bucks a sip, and streets full of people either with pots of cash, or those blowing cash on pot.  Likewise my nephew, (let&#8217;s call him Nathan) and my niece (pseudonymised here to Norma), present equally contrasting mindsets and approaches to life, yet manage to live semi-harmoniously in this subtly harsh environment.</p>

<p>Norma is an impossibly adorable individual, well mannered and courteous while being confident and sharp; were it not for a rather intense interest in the lives of serial killers and their victims, you&#8217;d imagine her as a Disney princess.  Nathan on the other hand is a man who prefers solitude, gaming and basketball.  While startling everyone  now and then with his wit and intelligence, he is incredibly self-contained. I can imaging him walking down the corridors of school, leaning back at a gravity-defying angle, looking left and right, but never straight ahead, head tilted back but eyes downwards, enabling him to look down his nose at people much taller than him.  While being devoted to each other in secret, they would never agree on anything, and never admit that the other has a point however similar it was to their own, deprecating common interest and amplifying minor differences.</p>

<p>If only one could rationalise their disagreements, prove that their by-and-large on the same side, then surely  I would have missed out the entertainment their mini-battles provided.  Indeed, while there is an inward joy when you see your sibling&#8217;s kids make their parents pull their hair out (as an uncle I am obliged to encourage this kind of behaviour), harmony between the two would indeed be a novel alternative experience in my sister&#8217;s household. </p>

<p>Such is the rational for the conflicts that plague the globe today, and indeed the world of Graphical Games programming. </p>

<h3>Graphical Coding</h3>

<p>I would like to be able to program some games.  But I understand graphics so little.  As an intermittent coder, each example one comes across appears to have paradigms which make perfect sense yet remain incompatible with each other.  One day I am offered one magical example, the next, another day another one from a different book of incomprehensible incantations, both blending <em>actual</em> Game Logic with the alien hieroglyphics representing their respective backends.  Hence, until now, I have mainly developed for the terminal, finding no easy entry into the land of graphics. Games require interaction as well as graphics.  Examples available typically intertwine <em>Game Logic</em>, <em>Interaction Handling</em>, <em>The Graphical outputs</em>, <em>The Game Loop</em> within the code, the entanglement further complicating the journey into learning.</p>

<p>By some chance, one Redditor <a href="https://www.reddit.com/user/First_Ad8230/">u/FirstAd8230</a> posted a <a href="https://www.reddit.com/r/perl/comments/1vixjm1/tsodings_rope_in_perl/">GLFW/OpenGL demo</a>.  Within <em>two hours</em> <a href="https://metacpan.org/author/SANKO">SANKO</a> or <a href="https://www.reddit.com/user/s_throwaway_r/">u/s<em>throwaway</em>r</a> posted an <a href="https://github.com/Perl-SDL3/SDL3.pm/blob/main/eg/basics/rope.pl">SDL3 equivalent</a> yielding and <em>identical output</em>, down to the look and interaction purely because he had difficulty in installing GLFW on his setup. This was the <a href="https://www.britishmuseum.org/blog/everything-you-ever-wanted-know-about-rosetta-stone">Rosetta Stone</a> I was looking for.  Both bits of code had identical Game Logic. Treating these like one would treat simultaneous equations, one is able to extract the common code which represent the game engine easily.  The residue then are backend specific code handling the other components mentioned above; still looking complicated but now manageable as it is possible to extract each call to the backends from the game logic.</p>

<p>From this, The <a href="https://github.com/saiftynet/GLAM">Graphics Layer Abstraction Module</a>  or GLAM is born.  A tool that transforms the complexity of the backends into simple calls, that can use either backend simply by changing one line of code.  The simple demo above had all the components to make many possible games and utilities, but to add simplicity one has to have a tool that abstracts away the residual complexity altogether.  How?  GLAM offers two modules for now (GLAM::OpenGL and GLAM::SDL).  These modules provide 1) A means to capture key board and mouse events that can change Game State 2)  A means to represent the Game State graphically using only one capability (the ability to draw a triangle) to render all graphical output 3) A way to start and exit the Game Loop. </p>

<p><img alt="GLAM.png" src="https://blogs.perl.org/users/saif/GLAM.png" width="1129" height="663" class="mt-image-none" style="" /></p>

<p>What is the benefit? Well the simplicity allows the occasional developer, like myself, developer to concentrate on the Game Logic, without remembering complex construction of graphical tools. It further permits those with difficulties in installing one backend for what ever reason, to easily swap in another (currently only two, but more imminently possible).  The Rope Demo and the two different iterations with similar outputs, has all the parts required to make quite complex interactive tools.  One can easily <a href="https://github.com/saiftynet/GLAM/blob/main/Examples/rope-GLAM.pl">replicate this</a> using GLAM.  </p>

<p><video controls width=350 alt="rope-GLAM" src="https://github.com/user-attachments/assets/cf62e45c-cc51-46f2-8c4b-91c00df854fb"></video></p>

<p>A <a href="https://en.wikipedia.org/wiki/Carrom">carrom board game</a>, a South Asian game played on a boards with counters and pockets can be <a href="https://github.com/saiftynet/GLAM/blob/main/Examples/Carrom-GLAM.pl">simulated</a> relatively easily.  To see how to create similar games using GLAM see Part 2.</p>

<p><video controls width=350 alt="Carrom-GLAM" src="https://github.com/user-attachments/assets/bd2d5db4-7608-48e8-b3b6-8ac3ca7940f3"></video></p>

<h3>Conclusion</h3>

<p>By converting the backend specific, complex code into generic, agnostic code the developer can concentrate on the goals he has rather than the paradigm his principles and platforms force him to follow. The results is code for each backend that looks nearly identical and the differences hidden away.</p>

<p>As I witness Nathan and Norma, so similar in so many ways and yet so completely different in others, go through their daily conflicts with the remote, the best spot on the sofa, or who walks the dog, I see all our children; complex individuals we are able to love dearly despite their diversity,  future leaders of industry, the world and a source of intense pride in parents (and uncles).  Harmony would be a possible illusion if we could abstract out the differences.  But failing that, one hopes one day they are blessed with children just like themselves.</p>
]]>
        

    </content>
</entry>



<entry>
    <title>pperl 0.6.20 - The Player of Games</title>
    <link rel="alternate" href="https://blogs.perl.org/users/petamem/2026/09/pperl-0620---the-player-of-games.html" />
    <id>tag:blogs.perl.org,2026:/users/petamem//1658.12118</id>
    <published>2026-09-27T18:13:55Z</published>
    <updated>2026-09-27T18:55:04Z</updated>
    <author>
        <name>PetaMem</name>
        <uri>http://www.petamem.com</uri>
    </author>
    <category term="clbg" label="clbg" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="memoize" label="memoize" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="pperl" label="pperl" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="wgpu" label="wgpu" scheme="http://www.sixapart.com/ns/types#tag" />
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/petamem/">
        <![CDATA[<p>pperl 0.6.20 is released, and readers of the two previous posts -
<a href="https://blogs.perl.org/users/petamem/2026/08/pperl-0612---lets-play-a-game.html">Shall we play a game?</a>
and <a href="https://blogs.perl.org/users/petamem/2026/08/pperl-doing-a-rejit.html">Doing a REJIT</a> -
know what that means. This post carries the Benchmarks Game
scoreboard. It also covers a native Memoize whose cache hit is
compiled into the caller, a GPU binding on the wgpu crate for the
other kind of games, and a few smaller items.</p>
]]>
        <![CDATA[<h2>The scoreboard</h2>

<p>The house rules are those of the first post, and every program stays
within the algorithms the Benchmarks Game accepts in some language.
All of ours are single-threaded.</p>

<p>CPU seconds at the contest sizes, every output byte-identical to the
pole's. The pole is the fastest scripted-language entry, node.js
unless noted, pinned to four physical cores as on the Benchmarks Game
machine, which has no hyperthreading. The unpinned column is given
for reference: there node's threaded programs spread onto hyperthread
siblings and read differently from day to day. Ratios are pole/pperl,
so above 1 pperl is faster.</p>

<p><style>
table.pp { border-collapse: collapse; margin: 1em 0; }
table.pp th, table.pp td { border: 1px solid #888; padding: 3px 8px; }
table.pp td + td { text-align: right; }
table.pp.l2 td:nth-child(2) { text-align: left; }
</style></p>

<table class="pp" border="1" cellspacing="0">
<thead>
<tr><th>benchmark</th><th>pperl</th><th>pole/pperl, pinned</th><th>unpinned</th></tr>
</thead>
<tbody>
<tr><td>pidigits 10,000</td><td>1.12 s</td><td>7.33x</td><td>7.21x</td></tr>
<tr><td>binary-trees 21</td><td>4.51 s</td><td>3.12x</td><td>6.41x</td></tr>
<tr><td>fasta 25M</td><td>3.47 s</td><td>1.88x</td><td>2.06x</td></tr>
<tr><td>reverse-complement 25M</td><td>0.87 s</td><td>1.53x (python3)</td><td>1.48x</td></tr>
<tr><td>spectral-norm 5,500</td><td>2.19 s</td><td>1.26x</td><td>1.26x</td></tr>
<tr><td>mandelbrot 16,000</td><td>12.13 s</td><td>1.20x</td><td>1.57x</td></tr>
<tr><td>regex-redux 5M, Peta::PCRE2</td><td>3.09 s</td><td>1.07x (python3)</td><td>1.25x</td></tr>
<tr><td>fannkuch-redux 12</td><td>36.69 s</td><td>1.02x</td><td>1.57x</td></tr>
<tr><td>n-body 50M</td><td>5.87 s</td><td>1.01x</td><td>1.03x</td></tr>
<tr><td>k-nucleotide 25M, Peta::Dict</td><td>38.98 s</td><td>0.99x</td><td>0.99x</td></tr>
</tbody>
</table>

<p>k-nucleotide's 0.99x is against the pole's recorded time; timed back
to back with node, pperl took 37.92 s and node 40.42 to 42.39 s.</p>

<p><strong>binary-trees</strong> went from 26.1 s to 4.5 s in one day. When one
compiled sub builds a tree and the next call in the same statement
only walks it, no Perl code can ever see that tree, so the JIT builds
it in a bump arena, one small record per node, and frees it with a
single store. Every node is still built and walked, and the program
is unchanged; any failure discards the arena and re-runs the
statement interpreted. It is the freedom V8 takes with objects that
do not escape.</p>

<p><strong>mandelbrot</strong> went from 31.1 s to 19.4 s once four pixels are
computed side by side in SIMD vectors, and to 12.1 s once an
interval-arithmetic proof, built on the loop's own escape test,
replaces the per-result overflow checks with one guard per group of
pixels.</p>

<p><strong>fasta and fannkuch-redux</strong> were ordinary compiler work. fasta's
generator ran in doubles because a constant carried a floating-point
value from an earlier division, where perl5 itself takes the integer
path. fannkuch-redux now processes permutations in pairs, as the C
and Lua entries do, and a range proof removes the bounds and overflow
checks from its swap loop.</p>

<p><strong>regex-redux</strong> is won with whatever gun is available, here
Peta::PCRE2, a native binding to libpcre2 and the same means the
fastest Python entry uses. The ReJIT gun announced in
<a href="https://blogs.perl.org/users/petamem/2026/08/pperl-doing-a-rejit.html">Doing a REJIT</a>
is not quite there: it ships in this release, on by default
(<code>--no-regex-jit</code> turns it off), and takes the built-in engine from
8,853M to 1,703M instructions on that post's measurement input. That
is 5.2x faster than the stock regex engine, against the 3-5x the post
predicted; at contest size the built-in engine runs at 0.88x of the
pole.</p>

<p><strong>k-nucleotide</strong> took a new gun. The published Perl entry counts
k-mers as strings in a hash and stops at 0.63x however well the loop
compiles, because the time goes into string hashing. node packs each
k-mer into a 2-bit-per-base integer and counts in a <code>Map</code>, and the
rules allow us the same algorithm. <code>Peta::Dict</code> is that dictionary
for Perl: <code>tie my %h, 'Peta::Dict'</code> binds a hash to an open-addressed
table whose integer-key accesses the JIT compiles to an in-register
probe, the shape that also carries Memoize below. <code>$h{5}</code> is still
<code>$h{"5"}</code>, and on perl5 the same source runs on a pure-Perl fallback
with identical output.</p>

<p><strong>mandelbrot through PDL</strong> was the other gun we had to test, and it is
the slower one: at N=2000 pperl with its native PDL retires 14.7G
instructions, perl5 with XS PDL 19.0G, the JIT-compiled scalar loop
4.9G. A vector formulation pays all 50 iterations for every pixel,
where the scalar loop leaves at the escape.</p>

<p><strong>JIT latency</strong> is a bonus on top. A small loop run briefly measures
compile time rather than compiled code, and there pperl is far ahead:
141M instructions against node's 1,597M on a 120x120 mandelbrot, and
any short loop, synthetic or not, shows the same. V8 spends its
longer compile on more optimisation, which is what the table above
measures. We aim for both: short compile latency and compiled
substrate efficiency.</p>

<h2>A dead end: asynchronous compilation</h2>

<p>On short runs the JIT compile is a large share of the run time, so we
built the usual answer: hand the hot loop to a compiler thread and
keep interpreting until the code is ready. It worked, passed the JIT
suites, and was reverted, because it never won. At mandelbrot 16,000
the asynchronous build took 141 s against 131 s compiling
synchronously, and no smaller size won either.</p>

<p>The result is counterintuitive only at first. While the compile runs,
the only useful work the program has is the very loop being compiled,
so the choice is between stalling and interpreting that loop. Both
spend the compile's wall time; the iterations interpreted meanwhile
are iterations the compiled code would have run many times faster,
and polling for the finished code costs a little on top. The compile
stall was a cost to shrink and not time to fill, since compiled code
is so much faster that one wants it as soon as possible. Asynchronous
compilation pays where the program has other compiled code to run in
the meantime, which a single dominant hot loop never has.</p>

<h2>Memoize, faster</h2>

<p><code>Memoize</code> is now a native module, and the JIT knows it. <code>memoize('f')</code>
installs a memo CV in place of <code>f</code>; a compiled caller probes the
cache in-register and only leaves compiled code on a miss, so a hit
costs about 42 instructions per call and never builds an argument
list. Source is unchanged, <code>use Memoize; memoize('fib');</code> as ever.</p>

<p>Hit cost against perl 5.44 with CPAN Memoize, final PGO build:</p>

<table class="pp l2" border="1" cellspacing="0">
<thead>
<tr><th>function</th><th>arity</th><th>speedup</th></tr>
</thead>
<tbody>
<tr><td>fib</td><td>1</td><td>63.5x</td></tr>
<tr><td>dist</td><td>2</td><td>51.7x</td></tr>
<tr><td>ackermann(2,k)</td><td>2, recursive</td><td>62.9x</td></tr>
<tr><td>Tak</td><td>3, recursive</td><td>36.7x</td></tr>
</tbody>
</table>

<p>The geometric mean is 52.5x. The compiled path covers numeric
arguments of arity 1 to 4, including nested calls, calls in return
position, self-recursion and calls from outside the function. A
miss-dominated run, ackermann computed once, gains 7.5x, which is
the algorithm and not the cache. Non-numeric arguments take the
ordinary path and behave as CPAN Memoize does, warnings included,
and deep memoized recursion stays compiled with perl's recursion
warning intact. A register cache for string keys is specified and
deferred, since the payoff is small today.</p>

<p>One divergence is deliberate. CPAN Memoize keys its cache on
<code>join chr(28), @_</code>, and a number stringifies to 15 significant
digits, so the arguments 0.30000000000000004 and 0.3, two different
doubles, both become the key <code>"0.3"</code>: once <code>f(0.3)</code> is cached,
<code>f(0.30000000000000004)</code> returns that result, which is the wrong
answer for its argument. The compiled path keys on the raw bits of
each double, which are exactly what the pure callee receives, so a
hit always returns what the call would have computed. The reverse
case is harmless: <code>1</code> and <code>"1.0"</code> are two Memoize entries and one
here, and the numeric body cannot tell them apart. We do not emulate
the collision.</p>

<h2>Perl and 3D graphics</h2>

<p>The other kind of games needs a window, a renderer and a GPU.</p>

<p><code>Peta::WebGPU</code> is a native binding to the Rust crate wgpu, which
drives Vulkan, Metal, DX12 and GL behind the WebGPU API. It computes
and it draws: adapters and limits as the card reports them, buffers,
WGSL shaders, compute dispatch and read-back, and render pipelines
and render passes into a surface taken from an SDL2 window.
Validation errors, which wgpu reports out of band, arrive as Perl
exceptions carrying wgpu's own text, and a panic inside the crate
becomes a croak instead of taking the interpreter down. A PDL ndarray
uploads straight into a GPU buffer without a pack step. The module
sits behind the cargo feature <code>webgpu</code>, off by default, since it
links a GPU stack into the binary.</p>

<pre><code>my $win  = SDL2::Window-&gt;new(title =&gt; 'triangle', w =&gt; 800, h =&gt; 600);
my $gpu  = Peta::WebGPU::Device-&gt;new(window =&gt; $win);
my $surf = $gpu-&gt;surface;

my $frame = $surf-&gt;next_frame or next;
$gpu-&gt;render_pass($frame, clear =&gt; [0.1, 0.2, 0.3, 1])
    -&gt;pipeline($pipe)-&gt;vertices($verts)-&gt;draw(3)-&gt;end;
$frame-&gt;present;
</code></pre>

<p>Underneath it the 2D substrate has become real: native bindings to
SDL2 (renderer, surfaces, SDL2::Image), GD, Cairo and Imager. The
documentation follows the code. The 2D graphics guide has been
re-anchored on SDL2, is available in all 41 locales, and closes with
a capstone that is a complete small game: paddle, ball, score,
keyboard control and a steady frame rate, in plain Perl on the SDL2
binding. The new 3D graphics guide runs from foundations and a
software rasteriser through shading, meshes, model formats, modelling
operations and scene graphs to the GPU, and closes with three
capstones: a world generator, a modeller and a 3D game. Every example
was run before its prose was written. Both guides are at
<a href="https://perl.petamem.com/docs/eng/">https://perl.petamem.com/docs/eng/</a>.</p>

<h2>Other tidbits</h2>

<ul>
<li>With <code>--no-jit</code>, the plain interpreter is now about 20% faster
than perl 5.44. The JIT numbers above are on top of that and not a
substitute for it.</li>
<li>PDL is measured against perl5 running PDL's own test suite, and
the native tree went from twelve mismatches to none. Exports now
come from the sub-modules as upstream arranges them, the broadcast
dimension engine is wired in, <code>xchg</code>, <code>transpose</code> and <code>dummy</code> are
views as upstream's are, and <code>glue</code>/<code>append</code> on a lazy slice no
longer abort.</li>
<li>Loading <code>POSIX</code>, <code>PDL::Lite</code>, <code>PDL::LiteF</code> or <code>PDL::MatrixOps</code>
under <code>-w</code> printed hundreds of redefinition warnings perl5 does
not print; they are gone.</li>
<li>A loop that cannot repay its compile time is no longer compiled,
which removes a 4-8x penalty on short-running scripts.</li>
</ul>

<h2>Statistics</h2>

<p>pperl 0.6.20 represents approximately 2 months of development since
0.6.8 and contains approximately 3,743,000 lines of changes across
42,000 files from 2 authors.</p>

<p>Excluding auto-generated files, documentation, assets, vendored
modules and tooling, there were approximately 454,000 lines of
changes to 1,700 .rs, .pl, .pm and .t files.</p>

<p>Get 0.6.20 here: <a href="https://perl.petamem.com/">https://perl.petamem.com/</a></p>

<ul>
<li>Richard C. Jelinek, PetaMem s.r.o.</li>
</ul>
]]>
    </content>
</entry>



<entry>
    <title>This week in PSC (236) | 2026-09-22</title>
    <link rel="alternate" href="https://blogs.perl.org/users/psc/2026/09/this-week-in-psc-236-2026-09-22.html" />
    <id>tag:blogs.perl.org,2026:/users/psc//4112.12120</id>
    <published>2026-09-22T20:40:04Z</published>
    <updated>2026-09-22T20:43:51Z</updated>
    <author>
        <name>Perl Steering Council</name>
        
    </author>
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/psc/">
        <![CDATA[<p>All three of us met.</p>

<ul>
<li>Talked about the two &#8220;match case&#8221; proposals. The two seem to cover different intents, so it&#8217;s not clear that either just replaces the other. The destructuring concept is nice in principle but needs further discussion. We will let discussions continue on the mailing list for now.</li>
<li>The PrePPC about <code>@INC</code> dirs seems good enough to progress to a full PPC document for further discussion of details; in particular specifics about non-UNIX operating systems.</li>
<li>We want to find a more coherent concept for &#8220;hooking core functionality&#8221; and possibly the <code>%{^HOOKS}</code> hash in particular, before considering such possibilities as a replacable random number generator. E.g. do <code>$SIG{__WARN__}</code> and <code>$SIG{__DIE__}</code> belong in here?</li>
<li>Specifically on providing a hook for generating random data, it feels like the <code>rand()</code> (and <code>srand()</code>) function interface provided by Perl is itself a limitation for getting good behaviour. This might need looking into before simply replacing a better source of randomness.</li>
</ul>

<p>[<a href="https://www.nntp.perl.org/group/perl.perl5.porters/;msgid=20260922164115.23ba49b8@vel.leo">P5P posting of this summary</a>]</p>
]]>
        

    </content>
</entry>



<entry>
    <title>A NetAddr::IP Trial Release</title>
    <link rel="alternate" href="https://blogs.perl.org/users/dean/2026/09/a-netaddrip-trial-release.html" />
    <id>tag:blogs.perl.org,2026:/users/dean//558.12119</id>
    <published>2026-09-22T03:08:53Z</published>
    <updated>2026-09-22T03:17:50Z</updated>
    <author>
        <name>Dean</name>
        <uri>https://www.facebook.com/groups/perlprogrammers/</uri>
    </author>
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/dean/">
        
        <![CDATA[<p>After years of quiet, <a href="https://metacpan.org/pod/NetAddr::IP">NetAddr::IP</a> has a new maintainer and a new trial release. If you use this module for IPv4/IPv6 address manipulation, here's what you need to know.</p>

<h3>Breaking Changes</h3>

<p>There are three breaking changes in 4.080_01. Before you reach for the pitchforks, they're all cases where the old behaviour was a bug, not a feature:</p>

<ul>
<li><strong><code>re()</code> on IPv6 objects in IPv4 space no longer emits a broken regex</strong> (<a href="https://github.com/cpan-authors/perl-NetAddr-IP/issues/34">GH#34</a>). Previously it returned a regex that couldn't match anything useful.</li>
<li><strong><code>re6()</code> now returns a non-capturing group <code>(?:...)</code></strong> (<a href="https://github.com/cpan-authors/perl-NetAddr-IP/issues/50">GH#50</a>). The old capturing group was almost never what the caller wanted.</li>
<li><strong><code>new(undef)</code> returns <code>undef</code> instead of the default route</strong> (<a href="https://github.com/cpan-authors/perl-NetAddr-IP/issues/29">GH#29</a>). Passing undef to a constructor should not silently give you <code>0.0.0.0/0</code>.</li>
</ul>

<p>All three are cases where the documented or sensible behaviour was different from what the code actually did. If your code relied on the old behaviour, it was already broken -- you just hadn't noticed yet.</p>

<h3>Deprecation</h3>

<p>The <code>:rfc3021</code> export tag is deprecated (<a href="https://github.com/cpan-authors/perl-NetAddr-IP/issues/26">GH#26</a>). <code>hostenum</code>/<code>hostenumref</code> now return 2 hosts for /31 and /127 unconditionally, matching the RFC. The dead <code>$rfc3021</code> variable has been removed (<a href="https://github.com/cpan-authors/perl-NetAddr-IP/issues/63">GH#63</a>).</p>

<h3>Bug Fixes (Selected)</h3>

<p>The release includes fixes for a number of long-standing bugs. Some highlights:</p>

<ul>
<li><code>coalesce</code> now counts subnet addresses, not usable hosts (<a href="https://github.com/cpan-authors/perl-NetAddr-IP/issues/37">GH#37</a>)</li>
<li><code>coalesce</code> works on subclass objects (<a href="https://github.com/cpan-authors/perl-NetAddr-IP/issues/38">GH#38</a>)</li>
<li><code>coalesce</code> validates its arguments and croaks on bad input (<a href="https://github.com/cpan-authors/perl-NetAddr-IP/issues/40">GH#40</a>)</li>
<li><code>new6()</code> prefers AAAA over A for dual-stack hostnames (<a href="https://rt.cpan.org/Public/Bug/Display.html?id=119858">RT#119858</a>, <a href="https://github.com/cpan-authors/perl-NetAddr-IP/issues/32">GH#32</a>)</li>
<li><code>new6()</code> returns compatible form for IPv4-only hosts (<a href="https://github.com/cpan-authors/perl-NetAddr-IP/issues/64">GH#64</a>)</li>
<li><code>:nofqdn</code> import tag actually works now (<a href="https://rt.cpan.org/Public/Bug/Display.html?id=111301">RT#111301</a>, <a href="https://github.com/cpan-authors/perl-NetAddr-IP/issues/1">GH#1</a>)</li>
<li><code>canon</code>/<code>short</code> compress the first equal-length zero run per RFC 5952 (<a href="https://github.com/cpan-authors/perl-NetAddr-IP/issues/13">GH#13</a>)</li>
</ul>

<p>Plus neg/abs overloads (<a href="https://github.com/cpan-authors/perl-NetAddr-IP/issues/67">GH#67</a>), bracket notation fixes (<a href="https://github.com/cpan-authors/perl-NetAddr-IP/issues/68">GH#68</a>), and many more. See the <a href="https://metacpan.org/changes/distribution/NetAddr-IP">full Changes</a> for the complete list.</p>

<h3>Test Suite Modernisation</h3>

<p>The entire test suite -- 157 files, nearly 3,000 tests -- has been converted from bespoke TAP output to <a href="https://metacpan.org/pod/Test2::V1">Test2::V1</a>. The tests now run cleanly on Perl 5.14 through 5.42 on Linux and macOS (Intel and ARM). The XS and pure-Perl code paths are both covered in CI.</p>

<h3>Try It Out</h3>

<p>This is a trial release. Install it with:</p>

<pre style="background-color: rgb(39, 40, 34); color: rgb(248, 248, 242); padding: 1em; border-radius: 5px; overflow-x: auto; line-height: 1.4;"><code style="font-family: &quot;Fira Code&quot;, &quot;Source Code Pro&quot;, Consolas, monospace; font-size: 0.9em; background-color: rgba(0, 0, 0, 0); padding: 0; border-radius: 3px; color: inherit;">cpanm DJZORT/NetAddr-IP-4.080_01.tar.gz</code></pre>

<p>or clone the repo:</p>

<pre style="background-color: rgb(39, 40, 34); color: rgb(248, 248, 242); padding: 1em; border-radius: 5px; overflow-x: auto; line-height: 1.4;"><code style="font-family: &quot;Fira Code&quot;, &quot;Source Code Pro&quot;, Consolas, monospace; font-size: 0.9em; background-color: rgba(0, 0, 0, 0); padding: 0; border-radius: 3px; color: inherit;">git clone https://github.com/cpan-authors/perl-NetAddr-IP.git
cd perl-NetAddr-IP
dzil test</code></pre>

<p>If you find regressions, <a href="https://github.com/cpan-authors/perl-NetAddr-IP/issues">open an issue on GitHub</a>. The more smoke reports, the better the stable release will be.</p>
]]>
    </content>
</entry>



<entry>
    <title>Dancer2 2.2.1 released (security)</title>
    <link rel="alternate" href="https://blogs.perl.org/users/jason_a_crome/2026/09/dancer2-221-released-security.html" />
    <id>tag:blogs.perl.org,2026:/users/jason_a_crome//712.12117</id>
    <published>2026-09-19T06:18:18Z</published>
    <updated>2026-09-19T06:20:06Z</updated>
    <author>
        <name>Jason A. Crome</name>
        <uri>https://cromedome.net</uri>
    </author>
    <category term="dancer" label="dancer" scheme="http://www.sixapart.com/ns/types#tag" />
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/jason_a_crome/">
        <![CDATA[<p>Dancer2 2.2.1 is now on CPAN. This is a security release; upgrading is recommended.</p>

<p><code>Dancer2::Serializer::YAML</code> handed request bodies straight to <code>YAML::Load</code>. A body tagged <code>!!perl/hash:Some::Class</code> could therefore instantiate an arbitrary blessed object, and <code>!!perl/code</code> could ask for a string eval. Both
  are now refused explicitly rather than left to YAML.pm's defaults.</p>

<p>Note that you can be exposed without having configured YAML yourself: <code>Serializer::Mutable</code> maps both <code>text/x-yaml</code> and <code>text/html</code> to this serializer.</p>

<p>Two packaging notes for this release: the minimum <code>YAML</code> version is now 1.30, and Dancer2 now declares the minimum <code>CLI::Osprey</code> it needs (0.09).</p>

<p>Thanks to David Precious for the YAML work.</p>
]]>
        

    </content>
</entry>



<entry>
    <title>Dancer2 2.2.0 released; security updates PLEASE READ</title>
    <link rel="alternate" href="https://blogs.perl.org/users/jason_a_crome/2026/09/dancer2-220-released-security-updates-please-read.html" />
    <id>tag:blogs.perl.org,2026:/users/jason_a_crome//712.12115</id>
    <published>2026-09-16T17:13:34Z</published>
    <updated>2026-09-16T17:15:03Z</updated>
    <author>
        <name>Jason A. Crome</name>
        <uri>https://cromedome.net</uri>
    </author>
    <category term="dancer" label="dancer" scheme="http://www.sixapart.com/ns/types#tag" />
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/jason_a_crome/">
        <![CDATA[<p>We have just released Dancer2 2.2.0. It is a maintenance release containing a number of bug fixes, several important security updates, and one breaking change (see below).</p>

<p>First and foremost, please read the following security announcements; if you are running affected versions of Dancer2, you should plan to update immediately:</p>

<ul>
<li><a href="https://github.com/PerlDancer/Dancer2/security/advisories/GHSA-6xw8-v24c-m783">GHSA-6xw8-v24c-m783</a> (Severity: High; Versions affected: &lt;= 2.1.0)</li>
<li><a href="https://github.com/PerlDancer/Dancer2/security/advisories/GHSA-v527-r4px-7vx7">GHSA-v527-r4px-7vx7</a> (Severity: Moderate; Versions affected: >= 2.0.0, &lt;= 2.1.0)</li>
</ul>

<p>A recap of all changes follows.</p>

<h3>Security fixes</h3>

<ul>
<li><strong>Path traversal in static file serving.</strong> <code>Dancer2::Handler::File</code> could serve files from outside <code>public_dir</code>. (<a href="https://github.com/PerlDancer/Dancer2/security/advisories/GHSA-6xw8-v24c-m783">GHSA-6xw8-v24c-m783</a>)</li>
<li><strong>Hook exception handling.</strong> If an <code>on_hook_exception</code> handler halted, the route that the hook refused could still run. (<a href="https://github.com/PerlDancer/Dancer2/security/advisories/GHSA-v527-r4px-7vx7">GHSA-v527-r4px-7vx7</a>)</li>
<li><strong>Header injection.</strong> CR and LF are now stripped from response header names, as they already were from header values.</li>
<li><strong>AutoPage</strong> no longer serves a layout as a page on case-insensitive filesystems.</li>
<li><strong>Session IDs</strong> are now always generated with <code>Crypt::URandom</code>, and <code>validate_id</code> rejects invalid session IDs more strictly.</li>
</ul>

<h3>Breaking changes</h3>

<ul>
<li>The <code>Data::Dumper</code> serializer has been removed from core, along with the <code>from_dumper</code> and <code>to_dumper</code> keywords. If your app uses them, make sure to download
<a href="https://metacpan.org/pod/Dancer2::Serializer::Dumper">Dancer2::Serializer::Dumper</a> from CPAN.</li>
</ul>

<h3>Bug fixes</h3>

<ul>
<li><code>send_file</code> now sends the correct error codes.</li>
<li>The <code>path()</code> and <code>dirname()</code> DSL keywords no longer drop their first argument.</li>
<li><code>Serializer::JSON</code> handles UTF-8 correctly for readonly values and no longer recurses endlessly into blessed objects.</li>
<li><code>Serializer::Mutable</code> ignores content type parameters such as <code>charset</code> when choosing a format.</li>
<li>Each response content assignment is now encoded, not just the first.</li>
<li><code>uri_for_route</code> accepts a route parameter of <code>0</code> and gives a clearer error for an empty one.</li>
<li>Hooks are compiled only once, however many times <code>to_app</code> is called.</li>
<li>A NUL byte in a static file request no longer produces a warning on every request.</li>
<li>App root directory detection has been fixed.</li>
<li>Several <code>dancer2 gen</code> fixes:
<ul>
<li><code>-g</code> and <code>-r</code> no longer die after writing the app.</li>
<li>The app directory is named after the dashed distribution name.</li>
<li>The pattern added to <code>MANIFEST.SKIP</code> is relative and matches correctly.</li>
</ul></li>
</ul>

<h3>Documentation</h3>

<ul>
<li>The header precedence documented for <code>Serializer::Mutable</code> now matches what it actually does.</li>
<li><code>%D</code> has been removed from the documented <code>log_format</code> characters, since it was never implemented.</li>
</ul>

<h3>Thanks</h3>

<p>Thanks to everyone who contributed to this release, especially:</p>

<ul>
<li><strong>Anton Lundin</strong>, for fixing the error codes in <code>send_file</code></li>
<li><strong>Mike Weisenborn</strong>, for the <code>path()</code>/<code>dirname()</code> fix</li>
<li><strong>Curtis "Ovid" Poe</strong>, for surfacing many of these issues with <a href="https://curtispoe.org/paad/">PAAD</a></li>
<li><strong>David Precious</strong> (bigpresh) and <strong>Russell Jenkins</strong> (veryrusty) for running with these items and seeing them through to completion</li>
</ul>

<p>Cheers,</p>

<p>Jason/CromeDome</p>
]]>
        

    </content>
</entry>



<entry>
    <title>ANNOUNCE: Perl.Wiki V 1.54, etc</title>
    <link rel="alternate" href="https://blogs.perl.org/users/ron_savage/2026/09/announce-perlwiki-v-154-etc.html" />
    <id>tag:blogs.perl.org,2026:/users/ron_savage//297.12114</id>
    <published>2026-09-15T08:06:26Z</published>
    <updated>2026-09-15T08:09:16Z</updated>
    <author>
        <name>Ron Savage</name>
        <uri>http://savage.net.au/</uri>
    </author>
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/ron_savage/">
        <![CDATA[<p>From my <a href="https://savage.net.au/">Wiki Haven</a> you can now download:<br />
<ul><br />
	<li>Perl.Wiki.html V 1.54</li><br />
	<li>cpan.metacurator.tree.html V 1.31</li><br />
	<li>Mojo Wiki V 1.25</li><br />
</ul></p>]]>
        
    </content>
</entry>



<entry>
    <title>Two Optimisation Tasks in the Weekly Challenge 391</title>
    <link rel="alternate" href="https://blogs.perl.org/users/e_choroba/2026/09/two-optimisation-tasks-in-the-weekly-challenge-391.html" />
    <id>tag:blogs.perl.org,2026:/users/e_choroba//1749.12113</id>
    <published>2026-09-14T21:31:53Z</published>
    <updated>2026-09-15T20:49:19Z</updated>
    <author>
        <name>E. Choroba</name>
        <uri>http://www.perlmonks.org/?node_id=832495</uri>
    </author>
    <category term="perl-weekly-challenge" scheme="http://www.sixapart.com/ns/types#category" />
    <category term="benchmark" label="benchmark" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="challenge" label="challenge" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="optimisation" label="optimisation" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="perlweeklychallenge" label="perl weekly challenge" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="sequence" label="sequence" scheme="http://www.sixapart.com/ns/types#tag" />
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/e_choroba/">
        <![CDATA[        <h2>Array Median</h2>

        <p>We are given two sorted arrays, our task is to merge them and return the median. Let’s start with the naive approach: just let Perl sort the list and select the median.</p>

        <pre><code>#!/usr/bin/perl
use warnings;
use strict;
use experimental qw( signatures );

sub array_median_naive($arr1, $arr2) {
    my @m = sort { $a <=> $b } @$arr1, @$arr2;
    return unless @m;

    return @m % 2 ? $m[ $#m / 2 ]
                  : ($m[ $#m / 2 ] + $m[ @m / 2 ]) / 2
}</code></pre>

]]>
        <![CDATA[        <p>My initial idea was to create a sliding window of size 2. Let the window slide over the merging arrays, stop at the middle and report the median. Unfortunately, the code doesn’t stay elegant. We only populate the <code>@m</code> array when at the right position, before reaching the middle we just adjust the indices into the two merging arrays.</p>

        <pre><code>use POSIX qw{ ceil };

sub array_median_sliding($arr1, $arr2) {
    my $pos = (@$arr1 + @$arr2 - 1) / 2;
    return if $pos < 0;

    my $size = 2 - (@$arr1 + @$arr2) % 2;

    my @indices = (0, 0);
    my @m;
    for my $step (0 .. ceil($pos)) {
        if ($indices[0] > $#$arr1) {
            push @m, $arr2->[ $indices[1] ]
                if $step >= ceil($pos) - 2;
            ++$indices[1];
        } elsif ($indices[1] > $#$arr2
                 || $arr1->[ $indices[0] ] <= $arr2->[ $indices[1] ]
        ) {
            push @m, $arr1->[ $indices[0] ]
                if $step >= ceil($pos) - 2;
            ++$indices[0];
        } else {
            push @m, $arr2->[ $indices[1] ]
                if $step >= ceil($pos) - 2;
            ++$indices[1];
        }
    }
    return $size == 1 ? $m[-1] : ($m[-1] + $m[-2]) / 2
}</code></pre>

        <p>Optimisation usually complicates code. What’s surprising here is that the sliding window is more than twice slower than the naive implementation! On the other hand, it’s memory efficient: it only needs to store two elements from the array, and a couple variables to handle the sliding window. If the two arrays are really large and merging them into memory isn’t possible, this is the algorithm to reach for.</p>

        <p>After some attempts, I was able to optimise the naive approach, though. The trick is not to store the sorted list in an array, but instead just use a list slice to select the needed elements:</p>

        <pre><code>sub array_median_opt($arr1, $arr2) {
    my $size = @$arr1 + @$arr2;
    return if 0 == $size;

    my @m = (sort { $a <=> $b } @$arr1, @$arr2)
            [ $size % 2 ? $size / 2
                        : $size / 2 - 1, $size / 2 ];
    return @m == 1 ? $m[0] : ($m[0] + $m[1]) / 2
}</code></pre>

        <p>You can see the actual benchmark code in my <a href="https://github.com/choroba/perlweeklychallenge-club/tree/master/challenge-391/e-choroba/perl/ch-1.pl">GitHub repository</a>.</p>

        <pre>         Rate sliding   naive     opt
sliding 111/s      --    -62%    -87%
naive   292/s    163%      --    -66%
opt     869/s    684%    198%      --</pre>

        <h2>Arrange Box</h2>

        <p>We are given an array of boxes, each box is represented by an anonymous array with two elements, representing the box’s width and depth. Our task is to find the maximum number of boxes that we can stack into each other: a box must be smaller in both dimensions to fit into another box.</p>

        <p>Here, the examples are too simple. Let’s have a look at one of them:</p>

        <pre>Sort by width ascending: ([10, 20], [12, 18], [15, 10], [16, 25], [20, 30])
Extract depths: (20, 18, 10, 25, 30)
[15, 10] -> [16, 25] -> [20, 30]</pre>

        <p>The result is just the last three elements of the sorted list of the boxes. But in a general case, we can have boxes in between. Once we select a box, we rule out boxes with a greater width but smaller depth.</p>

        <p>Let’s modify the input slightly:</p>

        <pre>[10, 20], [15, 10], [16, 18], [17, 25], [20, 30]</pre>

        <p>We can’t use the first box, because it rules out boxes number two and three, leading to only 3 boxes stacked in each other. Starting from the second box, though, allows us to stack 4 boxes. And you can imagine this can happen many times in the sequence. In fact, this seems almost like a <i>longest common subsequence</i> problem, which is <b>NP-hard</b>.</p>

        <p>Fortunately, it’s not that bad. I proceeded in steps: in each step, I took all the possible sequences of a given length, and tried to extend them by one box.</p>

        <pre><code>sub arrange_box(@boxes) {
    my @s = map $_->[1],
        sort { $a->[0] <=> $b->[0] || $b->[1] <=> $a->[1] }
        @boxes;
    my %paths;
    @paths{0 .. $#s} = ();
    my $max_length = 0;
    my $length = 0;
    my $change = 1;
    while ($change) {
        ++$length;
        undef $change;
        for my $path (keys %paths) {
            delete $paths{$path};
            $max_length = $length if $length > $max_length;
            my $last = (split ' ', $path)[-1];
            for my $next ($last + 1 .. $#s) {
                next if exists $paths{"$path $next"}
                     || $s[$next] <= $s[$last];

                undef $paths{"$path $next"};
                $change = 1;
            }
        }
    }
    return $max_length
}</code></pre>

        <p>Note that we only work with the depths (see the <code>map</code> at the beginning of the subroutine). We know that widths are already sorted, and if there are several boxes of the same width, they are sorted by their depth in descending order, so we’ll never try two boxes of the same width in the same sequence: <code>$s[$following]</code> is never greater than <code>$s[$box]</code> for the boxes of the same $width.</p>

        <p>For a randomly generated list of 100 boxes, the number of possible sequences of length 8 can easily reach 2.5&times;10<sup>6</sup> (try adding <code>warn scalar keys %path;</code> at the beginning of the <code>while</code> loop). It’s slow and consumes a lot of memory.</p> 

        <p>Fortunately, we don’t really need to remember the whole sequences. We are interested in their lengths—and in each step, we build all the sequences of the same length. The only information we actually need is the last element of each sequence. This leads to the following code:</p>

        <pre><code>sub arrange_box(@boxes) {
    my @s = map $_->[1],
        sort { $a->[0] <=> $b->[0] || $b->[1] <=> $a->[1] }
        @boxes;
    my %last;
    @last{0 .. $#s} = ();
    my $length = 0;
    while (keys %last) {
        my %next;
        ++$length;
        for my $box (keys %last) {
            for my $following ($box + 1 .. $#s) {
                undef $next{$following} if $s[$box] < $s[$following];
            }
        }
        %last = %next;
    }
    return $length
}</code></pre>

        <p>There’s never more last elements of all the sequences than the number of boxes. Finding the solution for a list of 1000 boxes takes about a second on my old machine.</p>]]>
    </content>
</entry>



<entry>
    <title>This week in PSC (235) | 2026-09-14</title>
    <link rel="alternate" href="https://blogs.perl.org/users/psc/2026/09/this-week-in-psc-235-2026-09-14.html" />
    <id>tag:blogs.perl.org,2026:/users/psc//4112.12112</id>
    <published>2026-09-14T21:02:00Z</published>
    <updated>2026-09-14T21:09:15Z</updated>
    <author>
        <name>Perl Steering Council</name>
        
    </author>
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/psc/">
        <![CDATA[<p>The first meeting of the newly-elected group.</p>

<p>We did some initial handover tasks (github groups, mailing lists)</p>

<p>We intend to do some point releases for 5.44 and 5.42 to include some security fixes and build issue bugfixes. We will take suggestions on what other fixes to be included.</p>

<p>We began discussing ideas on what it would look like to support the idea of <code>local::lib</code> and similar techniques better by perl core itself, handling perl version upgrades and so on.</p>

<p>We discussed improving the dual-life module release process to make it easier and prevent mistakes.</p>

<p>[<a href="https://www.nntp.perl.org/group/perl.perl5.porters/;msgid=20260914170339.59e29ec8@vel.leo">P5P posting of this summary</a>]</p>
]]>
        

    </content>
</entry>



<entry>
    <title>The Lightning Talks of the German Perl Workshop 2026 are online</title>
    <link rel="alternate" href="https://blogs.perl.org/users/max_maischein/2026/09/the-lightning-talks-of-the-german-perl-workshop-2026-are-online.html" />
    <id>tag:blogs.perl.org,2026:/users/max_maischein//227.12111</id>
    <published>2026-09-13T11:11:49Z</published>
    <updated>2026-09-13T11:14:00Z</updated>
    <author>
        <name>Max Maischein</name>
        <uri>https://corion.net</uri>
    </author>
    <category term="conferences" scheme="http://www.sixapart.com/ns/types#category" />
    <category term="gpw2026" label="gpw2026" scheme="http://www.sixapart.com/ns/types#tag" />
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/max_maischein/">
        <![CDATA[<p>Thanks to some crafty editing by Lee, the lightning talks are also <a href="https://youtu.be/3h4vib-HU_E">online on Youtube</a>.</p>

<p>Thanks again to all the past speakers!</p>

<p>Many conference speakers got their start with a lightning talk - maybe on your next conference you want to give a lightning talk too?</p>

<p>These presentations are up to 5 minutes long - the ideal way to introduce your favorite module, TV series, or really just about any topic.</p>]]>
        
    </content>
</entry>



<entry>
    <title>Adventures with Fortran</title>
    <link rel="alternate" href="https://blogs.perl.org/users/ed_j/2026/09/adventures-with-fortran.html" />
    <id>tag:blogs.perl.org,2026:/users/ed_j//2482.12110</id>
    <published>2026-09-12T21:10:54Z</published>
    <updated>2026-09-12T21:27:36Z</updated>
    <author>
        <name>Mohawk</name>
        <uri>https://metacpan.org/author/ETJ</uri>
    </author>
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/ed_j/">
        <![CDATA[<p>If you do scientific and/or numerical computing, you probably use or at least know about <a href="https://en.wikipedia.org/wiki/Basic_Linear_Algebra_Subprograms">BLAS</a>, and <a href="https://en.wikipedia.org/wiki/LAPACK">LAPACK</a>. If you've done much programming with them, you may have passed in data that it cannot handle, and then you'll probably know about <code>xerbla_</code>.</p>

<p><code>xerbla_</code> is the error-handler in those two libraries, and by default it terminates the whole process. To do otherwise you have to provide a replacement routine, and have the linker for your program insert that replacement, which the library will then use instead. This is increasingly hard or impossible to do, including on macOS, Windows, and static libraries. There is now an <a href="https://github.com/Reference-LAPACK/lapack/pull/1407">open pull-request</a> in the reference versions of those libraries that will permanently solve this, by allowing client programs to just pass in a new handler, which will then be used. No linker stuff involved.</p>

<p>Implementing this involved figuring out how to achieve this in Fortran. As a test-bed, I made a directory with this simple, no-nonsense <code>Makefile</code>:</p>

<pre>
FC = gfortran
FFLAGS = -g

.f90.o:
        $(FC) $(FFLAGS) -c $< -o $@
.f90:
        $(FC) $(FFLAGS) $< -o $@ $(LDFLAGS)
.SUFFIXES:
.SUFFIXES: .f90 .o

all: callbacks
</pre>

<p>The first try was using somewhat modern Fortran, with a module:</p>

<pre>
module xerbla_callbacks
  implicit none
  private
  public :: active_callback, xerbla_interface
  procedure(xerbla_interface), pointer :: active_callback => null()
  interface
    subroutine xerbla_interface(srname, info)
      character*(*), intent(in) :: srname
      integer, intent(in) :: info
    end subroutine
  end interface
end module xerbla_callbacks

subroutine set_xerbla(cb)
  use xerbla_callbacks
  implicit none
  procedure() :: cb
  active_callback => cb
end subroutine set_xerbla

subroutine get_xerbla(cb_ret)
  use xerbla_callbacks
  procedure(xerbla_interface), pointer :: cb_ret
  cb_ret => active_callback
end subroutine get_xerbla

subroutine xerbla(srname, info)
  use xerbla_callbacks
  character*(*), intent(in) :: srname
  integer, intent(in) :: info
  if (associated(active_callback)) then
    call active_callback(srname, info)
  else
    print *, 'I am the main xerbla'
  end if
end subroutine

subroutine xerbla_replacement(srname, info)
  character*(*), intent(in) :: srname
  integer, intent(in) :: info
  print *, 'I am the replacement xerbla'
end subroutine

program hello
  use xerbla_callbacks
  implicit none
  external xerbla_replacement, set_xerbla, xerbla
  procedure(), pointer :: already_cb
  interface
    subroutine get_xerbla(cb_ret)
      procedure(), pointer :: cb_ret
    end subroutine
  end interface
  call xerbla('Hello, World!', 5)
  call get_xerbla(already_cb)
  call set_xerbla(xerbla_replacement)
  call xerbla('Hello, World!', 5)
  call set_xerbla(already_cb)
  call xerbla('Hello, World!', 5)
end program hello
</pre>

<p>This didn't work in LAPACK because it has an automatic translation facility to turn its source files into versions that suffix routines with <code>_64</code> that cannot handle modules well. Using an older idiom that allows stored, persistent data was necessary, using the <code>ENTRY</code> keyword.</p>

<pre>
subroutine xerbla(srname, info)
  character*(*), intent(in) :: srname
  integer, intent(in) :: info
  procedure(xerbla_interface), pointer :: active_callback => null(), cb_ret
  procedure(xerbla_interface) :: cb
  abstract interface
    subroutine xerbla_interface(srname, info)
      character*(*), intent(in) :: srname
      integer, intent(in) :: info
    end subroutine
  end interface
  if (associated(active_callback)) then
    call active_callback(srname, info)
  else
    print *, 'I am the main xerbla'
  end if
  return
  entry set_xerbla(cb)
    active_callback => cb
  return
  entry get_xerbla(cb_ret)
    cb_ret => active_callback
  return
end subroutine

subroutine xerbla_replacement(srname, info)
  character*(*), intent(in) :: srname
  integer, intent(in) :: info
  print *, 'I am the replacement xerbla'
end subroutine

program hello
  implicit none
  external xerbla_replacement, set_xerbla, xerbla
  procedure(), pointer :: already_cb
  interface
    subroutine get_xerbla(cb_ret)
      procedure(), pointer :: cb_ret
    end subroutine
  end interface
  call xerbla('Hello, World!', 5)
  call get_xerbla(already_cb)
  call set_xerbla(xerbla_replacement)
  call xerbla('Hello, World!', 5)
  call set_xerbla(already_cb)
  call xerbla('Hello, World!', 5)
end program hello
</pre>

<p>Amazingly, this actually all works, and when the C interfaces for these things are figured out, this facility will become available. That will solve a lot of tricky problems, especially for those who make dynamic-language bindings for BLAS etc, but also for those using it on macOS, Windows, etc.</p>
]]>
        

    </content>
</entry>



<entry>
    <title>Running Perl/PAGI applications on Cloudflare Workers via WebAssembly</title>
    <link rel="alternate" href="https://blogs.perl.org/users/aspeer/2026/09/running-perlpagi-applications-on-cloudflare-workers-via-webassembly.html" />
    <id>tag:blogs.perl.org,2026:/users/aspeer//4880.12109</id>
    <published>2026-09-12T07:22:15Z</published>
    <updated>2026-09-12T07:26:35Z</updated>
    <author>
        <name>Andrew Speer</name>
        
    </author>
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/aspeer/">
        <![CDATA[<h2>Background</h2>

<p>I've been developing the WebDyne framework to make creation of Perl-based web applications easier (for me anyway). Most of the apps I write now use John Napiorkowski's excellent PAGI design, as it supports modern asynchronous web services such as SSE and WebSockets.</p>

<p>One of the challenges has always been: "where do I host them?"</p>

<p>They run fine in Docker containers, and the demo website currently runs on Sevalla in a relatively cheap container. I could also run them in an AWS Lambda environment, but with either option I have to pick a region, and &mdash; especially with full Docker containers &mdash; hosting multiple sites can be relatively expensive for "small" applications.</p>

<h2>Cloudflare Workers and WASM</h2>

<p>Cloudflare Workers have always looked attractive. For a starting price of $5/month on the compute and storage plans you get a fair bit of "bang for your buck", with about 8 CPU-hours/month for Worker isolates (basically Lambda functions), with the added bonus that they are &mdash; or at least can be &mdash; "Cloud Edge": they spin up close to the consumer on demand, but you are charged for actual usage rather than the number of Workers that are running.</p>

<p>Obviously prices go up with consumption, but you can get a fair bit done with the starting price.</p>

<p>Couple that with access to storage services such as D1 (SQLite-compatible database), KV storage and R2 (S3-compatible object storage), and things start looking reasonably attractive.</p>

<p>But, like almost everywhere today, Perl is a second-class citizen. Cloudflare Workers primarily target JavaScript, TypeScript and Python, with languages such as Rust supported through WebAssembly.</p>

<p>But importantly, Workers can run WebAssembly (WASM).</p>

<h2>Enter zeroperl</h2>

<p>So, WebAssembly. How does that work with Perl?</p>

<p>Back in 2025 Andrew Sampson debuted <a href="https://github.com/6over3/zeroperl">zeroperl</a> via a <a href="https://andrews.substack.com/p/zeroperl-sandboxed-perl-with-webassembly">blog post</a>. It was a port of Perl to WebAssembly.</p>

<p>So maybe that could be adapted to support Perl with services such as Cloudflare Workers?</p>

<p>Long story short, that's what I have attempted to do.</p>

<p>I have made some modifications to the original zeroperl to add PAGI support and added a harness which lets it run as a Cloudflare Worker.</p>

<p>The resulting Worker loads a persistent Perl interpreter compiled to WASM, then bridges Cloudflare's JavaScript request/event model into Perl. HTTP requests are presented to Perl as PAGI <code>$scope</code>, <code>$receive</code> and <code>$send</code> interfaces, which means existing PAGI applications can run with relatively little or no modification.</p>

<p>The same bridge also supports asynchronous PAGI features such as WebSockets.</p>

<h2>Demos</h2>

<p>Demo, you say?</p>

<p>Firstly, before you look at them, let's get the AI bit out of the way. WebDyne is human written, with a few AI bug fixes. The demo apps below are about half and half &mdash; I'm pretty crap at UI design, so the Perl code is mostly human but they are skinned by AI.</p>

<p>With that out of the way, some demos:</p>

<ul>
<li><p>Firstly, a <a href="https://pagi-time-wasm.andrew-speer.workers.dev">vanilla PAGI app</a>, a basic "Hello World" example that dynamically returns the Cloudflare Worker server time. The <a href="https://github.com/aspeer/pagi-time-wasm">GitHub repo</a> contains the source.</p></li>

<p><li><p>If you just want to see the basics and how to get started very simply with a WebDyne::PAGI app, have a look at <a href="https://psp-webdyne-fortune-wasm.andrew-speer.workers.dev">Perl Fortune</a>. The <a href="https://github.com/aspeer/psp-WebDyne-Fortune-wasm">GitHub repo</a> gives simple instructions on how to build, test and deploy it.</p></li></p>

<p><li><p>A more fully fledged demo showcases WebSocket support: terminal-based emulation allowing you to play the Scott Adams text adventures through a browser. Play it <a href="https://psp-scott-adams-games-wasm.andrew-speer.workers.dev">here</a>, with the <a href="https://github.com/aspeer/psp-scott-adams-games-wasm">GitHub repo here</a>.</p></li></p>

<p><li><p>A mock <a href="https://psp-wine-cellar-demo.andrew-speer.workers.dev">Wine Cellar demo</a> talks to Cloudflare D1 and also uses KV and R2 object storage. The <a href="https://github.com/aspeer/psp-wine-cellar-demo">GitHub repo is here</a>.</p></li><br />
</ul></p>

<h2>More info and limitations</h2>

<p>I've attempted to make the instructions as clear as possible, but feedback and bug reports are welcome.</p>

<p>The demos attempt to showcase some of the features of WebDyne, including support for <a href="https://pagi-time-wasm.andrew-speer.workers.dev">htmx</a>, so that there is minimal JavaScript &mdash; HTML is swapped in and out rather than built via JavaScript &mdash; and the ability for Perl calls to return data as embedded JSON, which can then be read by JavaScript charting libraries etc.</p>

<p>There are limitations.</p>

<p>Pure-Perl CPAN modules are relatively straightforward to bundle; XS modules generally need to be compiled specifically for WASM or replaced with Pure-Perl equivalents. This isn't intended to make every existing Perl application magically run unchanged on Workers.</p>

<p>I've packaged the build/runtime tooling as <code>@webdyne/webdyne-zeroperl</code> on npm, with some sister packages for Cloudflare such as <code>@webdyne/webdyne-cloudflare</code>.</p>

<p>The idea is that an application repository contains the Perl application and its dependencies, while the packages handle building the WASM bundle and the Cloudflare/Wrangler integration.</p>

<p>Cloudflare does offer a free tier that supports Workers, but the limitations on size and startup time make it impractical to fit the current zeroperl/WebDyne bundle within the free-tier limits.</p>

<h2>What about just running Perl in the browser?</h2>

<p>And one more, not entirely serious, demonstration.</p>

<p>If we've gone to the trouble of compiling Perl and WebDyne to WASM, there's no particular reason the WASM has to execute inside a Cloudflare Worker. A browser can instantiate it too.</p>

<p>Might as well cut out the middle man and just download zeroperl to the browser and run the WASM right on the user's machine to serve HTML, right?</p>

<p>So, if you are prepared to download 4+ MB of WASM, you can run a WebDyne app directly from this <a href="https://aspeer.github.io/psp-WebDyne-Fortune-wasm-browser/">GitHub Pages demo</a>. The <a href="https://github.com/aspeer/psp-WebDyne-Fortune-wasm-browser">GitHub repo is here</a>.</p>

<p>I don't see a lot of demand for doing it &mdash; but hey, soon 4 MB might count as a small download.</p>

<h2>Links</h2>

<p>Some more links:</p>

<ul>
<li><a href="https://github.com/aspeer/zeroperl">zeroperl fork for WebDyne</a></li>
<li><a href="https://github.com/aspeer/zeroperl-browser">zeroperl wrapper for browser loading</a></li>
<li><a href="https://webdyne.org">WebDyne reference</a></li>
<li><a href="https://github.com/aspeer/pm-WebDyne-Cloudflare">WebDyne Cloudflare bindings</a></li>
</ul>]]>
        
    </content>
</entry>



<entry>
    <title>The videos of the German Perl Workshop 2026 are online</title>
    <link rel="alternate" href="https://blogs.perl.org/users/max_maischein/2026/09/the-videos-of-the-german-perl-workshop-2026-are-online.html" />
    <id>tag:blogs.perl.org,2026:/users/max_maischein//227.12108</id>
    <published>2026-09-06T18:21:02Z</published>
    <updated>2026-09-06T18:22:27Z</updated>
    <author>
        <name>Max Maischein</name>
        <uri>https://corion.net</uri>
    </author>
    <category term="conferences" scheme="http://www.sixapart.com/ns/types#category" />
    <category term="gpw2026" label="gpw2026" scheme="http://www.sixapart.com/ns/types#tag" />
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/max_maischein/">
        <![CDATA[<p>The videos are now online on <a href="https://www.youtube.com/playlist?list=PLbIkWLBwJREQ">Youtube</a>.</p>

<p>Thanks again to all people presenting their projects and insights!</p>

<p>Much of the credit for the video production goes to Lee Johnson for recording<br />
all of them during the event. The postproduction happened thanks to <tt>ffmpeg</tt>.</p>]]>
        
    </content>
</entry>



<entry>
    <title>AmberDB - An Embedded NoSQL Database Engine for Perl</title>
    <link rel="alternate" href="https://blogs.perl.org/users/mcetin/2026/08/amberdb---an-embedded-nosql-database-engine-for-perl.html" />
    <id>tag:blogs.perl.org,2026:/users/mcetin//4881.12107</id>
    <published>2026-09-01T03:53:48Z</published>
    <updated>2026-09-02T13:38:58Z</updated>
    <author>
        <name>Maruf Çetin</name>
        
    </author>
    <category term="Database" scheme="http://www.sixapart.com/ns/types#category" />
    <category term="NoSQL" scheme="http://www.sixapart.com/ns/types#category" />
    <category term="Pure Perl" scheme="http://www.sixapart.com/ns/types#category" />
    <category term="amberdb" label="AmberDB" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="database" label="Database" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="db_file" label="DB_File" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="fulltextsearch" label="Full-Text Search" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="nosql" label="NoSQL" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="perl" label="Perl" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="schema" label="Schema" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="transactions" label="Transactions" scheme="http://www.sixapart.com/ns/types#tag" />
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/mcetin/">
        <![CDATA[<p><b>AmberDB</b> is an embedded, schema-driven NoSQL database engine
for Perl, designed for applications that need structured records,
fast indexed queries, full-text search, transactions, and a simple
deployment model.</p>
<p>It is built on top of Berkeley DB through <font face="Liberation Mono, monospace">DB_File</font>,
but AmberDB provides a much higher-level database API rather than
exposing the underlying key-value store directly.</p>
<h2 class="western">Why AmberDB?</h2>
<p>AmberDB brings several capabilities together in a single Perl
database engine:</p>
<ul><li>a simple Perl-native CRUD API</li><li>schemaless and schema-driven data structures</li><li>indexed matching and filtering</li><li>full-text search</li><li>sorting and faceted queries</li><li>relational and repeated data blocks</li><li>multi-table transactions</li><li>concurrency control</li><li>high-throughput batch operations</li><li>portable database archives and restore tools</li></ul>
<p>The result is a database where operations that often require
additional layers, services, or complex query logic can be handled
directly by the database itself.</p>
<p>For example, a Perl application can insert, read, modify, search,
and delete records using a small and consistent API, while the schema
controls validation and indexing behind the scenes.</p>
<p>AmberDB is built around a straightforward principle:</p>
<p><b>The database should make common application data operations
simple, fast, and predictable.</b></p>
<h2 class="western">A database operation can be this simple</h2>
<p>After creating an AmberDB instance, inserting a record is
straightforward:</p>
<pre class="western" style="background: #e1e1e1; margin-bottom: 0.5cm">use AmberDB;<br />
<br />
my $db = AmberDB-&gt;new(
    cfg  =&gt; { user =&gt; 'admin', language =&gt; 'en' },
    path =&gt; { dbase_dir =&gt; './dbstore' }
);<br />
my @product = (
    0,
    "Wireless Headphones",
    149.99,
    "Electronics",
    1
);<br />
my $id = $db-&gt;insert_id(
    "products",   # table
    @product      # record
);</pre><p>
Reading the record back:</p>
<pre class="western" style="background: #e1e1e1; margin-bottom: 0.5cm">my @product = $db-&gt;read_id("products", $id);</pre><p>
Modify it:</p>
<pre class="western" style="background: #e1e1e1; margin-bottom: 0.5cm">$product[2] = 129.99;<br />
$db-&gt;modify_id(
    "products",
    @product
);</pre><p>
And delete it:</p>
<pre class="western" style="background: #e1e1e1; margin-bottom: 0.5cm">$db-&gt;delete_id("products", $id);</pre><p>
That's the basic CRUD API.</p>
<p>There is no SQL query to construct, no ORM required, and no
separate query language between the Perl application and the
database.</p>
<p>For many applications, database access can therefore look very
similar to ordinary Perl data manipulation.</p>
<h2 class="western">Using schemas for advanced applications</h2>
<p>AmberDB works even without a schema definition. However, schemas
can significantly increase the engine's capabilities by defining the
structure of a table and how its data should be indexed and searched.</p>
<p>There is no separate indexing system to configure. Once the schema
is defined, AmberDB automatically builds and maintains the required
indexes.</p>
<p>For example:</p>
<pre class="western" style="background: #e1e1e1; margin-bottom: 0.5cm">{
    name         =&gt; "Table Name",
    record_index =&gt; 1,              # readall
    search_block =&gt; [2,4,5,8],      # blocks to search
    match_block  =&gt; [1,3,8],        # blocks to match
    sort_block   =&gt; [1,3,5,7,10],   # blocks to sort
}</pre><p>
AmberDB automatically creates inverted indexes for the blocks defined
by the table schema. These indexes provide very fast search and
filtering operations, even with large datasets.</p>
<h2 class="western">Indexed reads instead of scanning records</h2><p>AmberDB is designed to perform much of the work when data is
inserted or indexes are rebuilt, rather than repeatedly doing
expensive work during queries.</p><p>This makes operations such as filtering, sorting, pagination, and
searching large datasets possible without requiring an external
search server.</p>
<p>For example:</p>
<pre class="western" style="background: #e1e1e1; margin-bottom: 0.5cm">my ($total, @products) = $db-&gt;search_table(
    "products",
    "wireless headphones",
    start =&gt; 0,
    limit =&gt; 20
);</pre><p>
The same database can therefore provide both ordinary record access
and full-text search.</p>
<h2 class="western">AmberDB does not join</h2>
<p>A record can contain or be linked to relational data from other
tables. For example, an order record might contain customer
information, payment information, shipping and delivery information,
various dates, and a collection of order items containing the product
number, name, price at the time of sale, applied discount, and other
information.</p>
<p>AmberDB processes these structures as part of the record model and
uses custom indexes to make them easy to query.</p>
<p>This can be useful when the application's natural model is
something like:</p>
<pre class="western" style="background: #e1e1e1; margin-bottom: 0.5cm">Order<br />
 ├── Customer
 ├── Address
 ├── Dates [ as array ]
 ├── Payment
 └── Items
      ├── Product
      ├── Quantity
      └── Price</pre><p>
Instead of reconstructing the object from multiple tables through
joins, the application can work with the structure directly, in a way
that is much closer to Perl's own data model.</p>
<h2 class="western">ACID Transactions</h2>
<p>Although AmberDB is a performance-oriented NoSQL engine, it
provides multi-table ACID transactions using an undo journal and
Strict Two-Phase Locking (Strict 2PL).</p>
<p>AmberDB also supports concurrent access using OS-level file
locking, making it suitable for multi-process Perl applications.</p>
<p>The <font face="Liberation Mono, monospace">transact</font> API is
simple to use, while the mechanisms behind it provide atomicity,
consistency, isolation, and durability across related operations.</p>
<pre class="western" style="background: #e1e1e1; margin-bottom: 0.5cm">$db-&gt;transact_start();<br />
# update order<br /># update customer account
# update stock
# insert payment record<br />
$db-&gt;transact_end();</pre><p>
If an error occurs, the transaction can be rolled back, including the
corresponding index changes.</p>
<p>The undo journal also provides recovery after an unexpected
process or system failure.</p>
<h2 class="western">Batch operations for large imports</h2>
<p>Transactional record operations and bulk data loading are
intentionally treated as different workloads.</p>
<p>For ETL, CSV imports, migrations, and large datasets, AmberDB
provides batch methods such as:</p>
<pre class="western" style="background: #e1e1e1; margin-bottom: 0.5cm">$db-&gt;insert_list("products", @records);
$db-&gt;modify_list("products", @records);
$db-&gt;delete_list("products", @ids);</pre><p>
For example, when you insert a list of a thousand records using
<font face="Liberation Mono, monospace">insert_list</font>, AmberDB
creates the records and their indexes as a batch operation.</p>
<p>Similarly, a large price list can be modified in a single batch
operation.</p>
<p>This makes batch methods particularly useful for imports,
migrations, synchronization jobs, and other high-volume data
operations.</p>
<h2 class="western">Portable backups</h2>
<p>AmberDB also includes native database tools for backup and
restore.</p>
<p>A database can be exported to a portable <font face="Liberation Mono, monospace">.amberdb</font>
archive containing the authoritative database state and schema.
Integrity can be verified using SHA-256, and derived indexes can be
rebuilt during restore.</p><p>This makes it possible to move or archive a complete database
without requiring a separate database server.</p><h2 class="western">Getting started</h2>
<p>AmberDB 5.22.0 is available on CPAN:</p>
<pre class="western" style="background: #e1e1e1; margin-bottom: 0.5cm">cpanm AmberDB</pre><p>
The project is released under the Artistic License 2.0.</p>
<p>The source code, documentation, and examples are available on
MetaCPAN and GitHub.</p>
<p>If you are a Perl developer working with a significant amount of
structured data, give AmberDB a try.</p>
<p><br />
<br />

</p>
<br />]]>
        
    </content>
</entry>



<entry>
    <title>pperl: Doing a REJIT</title>
    <link rel="alternate" href="https://blogs.perl.org/users/petamem/2026/08/pperl-doing-a-rejit.html" />
    <id>tag:blogs.perl.org,2026:/users/petamem//1658.12106</id>
    <published>2026-08-29T16:33:39Z</published>
    <updated>2026-08-29T16:54:54Z</updated>
    <author>
        <name>PetaMem</name>
        <uri>http://www.petamem.com</uri>
    </author>
    <category term="clbg" label="clbg" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="pperl" label="pperl" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="regexredux" label="regex-redux" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="rejit" label="rejit" scheme="http://www.sixapart.com/ns/types#tag" />
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/petamem/">
        <![CDATA[<p>The previous post - <a href="https://blogs.perl.org/users/petamem/2026/08/pperl-0612---lets-play-a-game.html">pperl 0.6.12+ - Shall we play a
game?</a> -
closed with a promise: the next release will compete in every
benchmark of the Computer Language Benchmarks Game with the best
scripted language entry there. The current build already takes several
rows outright; the tally belongs to the release post. This one is
about a row where the promise cannot be kept with the guns we have. On
regex-redux the opponent is V8's Irregexp, a compiler that turns
regular expressions into machine code, and no amount of work on the
Perl JIT will close that gap, because the time is not spent in Perl
ops at all. The answer is a second compiler: ReJIT.</p>
]]>
        <![CDATA[<h2>The promise, and the scoreboard so far</h2>

<p>The promise stands as written: every Game benchmark, against the
best scripted entry, with a general-purpose JIT rather than one
tailored to benchmark code. Several rows already go to the current
build; publishing the numbers before the release they belong to
would repeat the mistake of printing tables that age in our favour,
so they wait. What does not wait is the row that dictated new
machinery.</p>

<h2>regex-redux: outgunned at home</h2>

<p>regex-redux is the Game's regular-expression benchmark: nine
alternation patterns counted over a DNA sequence with <code>//g</code>, five
IUB-code substitutions with <code>s///g</code>, essentially all of it inside
the regex engine. Regular expressions are the domain Perl defined;
this should be a home game.</p>

<p>It is not. pperl runs the workload through its regex engine - the
Rust reimplementation of perl5's backtracker - at perl5 parity:
roughly nine billion instructions on our measurement input, for
pperl and perl5 alike. node retires 1.33 billion on the same input.
That is a factor of seven, in instruction count, before anyone
talks about clock speed.</p>

<p>The reason node is there is Irregexp, V8's regex compiler. It is a
backtracker, like perl's engine - leftmost-greedy, capture
semantics and all - but it does not interpret a pattern, it
compiles the pattern to native code: character classes become
mask-and-compare instructions, backtracking becomes a pop and an
indirect jump, and each subject encoding gets its own code object.
Against that, an interpreter dispatching per character through a
switch is structurally behind, however tight the switch.</p>

<p>And this is the point where the existing JIT cannot help. The pperl
JIT compiles Perl ops; on this row over 99% of the instructions
retire inside a single op, the match itself. An engine gap cannot
be closed from the op side. Hence the second compiler.</p>

<h2>ReJIT</h2>

<p>ReJIT compiles regular expressions to machine code, and the design
decision worth explaining is what it compiles. There are three
architectures in the field. Irregexp compiles its own pattern graph.
RE2-class engines (rust-regex, V8's linear mode) run
guaranteed-linear automata over a restricted semantics. PCRE2's JIT
does neither: it compiles the interpreter's own compiled-pattern
bytecode, arm by arm, and falls back to the interpreter for
whatever it declines.</p>

<p>ReJIT takes the PCRE2 shape. perl's regex compiler already does the
hard front-end work - parsing, optimization, trie construction with
Aho-Corasick start classes, anchoring and substring analysis - and
its output, the regnode program, is the specification of what a
match must do. ReJIT compiles that program to native code through
the same Cranelift backend the op JIT uses, and splices in behind
the engine's public boundary: patterns it admits get compiled code,
patterns it declines keep the stock engine, permanently and at zero
per-match cost. Compiling the engine's own program rather than
reinventing a pattern representation is what makes fidelity a
construction property instead of a test-suite aspiration.</p>

<p>Irregexp still gets mined - for mechanics, not architecture: the
heap-grown backtrack stack of code addresses, captures as a
register array saved and restored through that stack, one compiled
body per subject encoding, compilation deferred until a pattern is
demonstrably hot.</p>

<p>What we will not ship is an RE2-style linear engine, for two
reasons. First, perl already ships the tier such an engine would
occupy: the substring and start-class presearch machinery in front
of the matcher is a prefilter, and duplicating it buys nothing.
Second, a breadth-first engine cannot express the backtracking-order
phenotype - which branch wins, what <code>$1</code> holds under alternation,
what <code>(*SKIP)</code> and its relatives do - and in Perl that phenotype is
observable behaviour, not an implementation detail.</p>

<h2>Where the instructions go</h2>

<p>We took a census of the row before designing the tier. 83% of the
instructions are the count loop, 16% the substitutions, 1%
everything else. The interesting split is inside the count loop.</p>

<p>perl's optimizer builds its trie - and the Aho-Corasick scan that
makes trie'd alternations fast - only when every branch of the
alternation starts with a literal. Five of the nine count patterns
qualify and run comparatively fast. The other four start a branch
with a character class (<code>[cgt]gggtaaa|tttaccc[acg]</code> and friends),
get no trie and no start class, and are scanned branch by branch
from every position. Those four cost 64% of the count phase - about
55% of the entire benchmark.</p>

<p>So the single biggest target is exactly the shape perl's optimizer
refuses to optimize: a class-headed alternation, compiled into a
machine-code scan loop. The trie'd scan is the second target, at
roughly a quarter of the row. All fifteen patterns of the benchmark
sit inside the first compiled subset - no captures, no lookaround,
no backreferences anywhere in the row.</p>

<p>Captures are in that first subset anyway. In our 1,844-file test
corpus, <code>$1</code>-style extraction outnumbers backreferences 268 to 14;
a captureless tier would compile the counting workloads and decline
nearly every extracting one, which is the majority real-world shape
of <code>=~</code>.</p>

<h2>Semantics are not negotiable</h2>

<p>The house rules from the previous post apply unchanged: every gap
is fixed as a general construct or not at all, and the semantics
are perl's, byte for byte.</p>

<p>One example of why compiled-regex prior art cannot simply be
copied. In <code>/(?:(a)|(b))+/</code> matched against <code>"ab"</code>, perl leaves
<code>$1</code> holding <code>"a"</code> - a quantified group retains the last successful
value of its inner captures across iterations. ECMAScript clears
them at each iteration entry, so JavaScript reports capture 1
undefined, and Irregexp implements exactly that. Copying its
register-clearing verbatim would be a wrong-match bug by
construction. The engines agree on almost everything; the tier is
built for the places they do not.</p>

<p>The enforcement is mechanical, not stylistic. In test mode every
compiled match is re-run through the stock engine and compared -
not the verdict alone, but the full capture state, since a
divergence there stays invisible until <code>$'</code> or <code>@-</code> is read much
later. perl's own regression corpus runs under that differential
mode, including the 2,210-case table it maintains for the engine
and the variants of it that force the trie optimization off and on.
And the perl-specific machinery that does not compile - <code>(?{...})</code>
code blocks, pattern recursion, locale-dependent folding - is
declined at admission and stays on the stock engine, which remains
the universal fallback.</p>

<h2>Staging</h2>

<p>ReJIT lands in stages: the instrumentation and the differential oracle
first, then the scan-loop tier described above, then quantifier depth,
then the utf8 axis. For the scan-loop tier alone our prediction on the
regex-redux row is a 3-5x reduction in instruction count - printed
here so the release post can be checked against it.</p>

<p>As always: no dates. This is to update status: the promise from the
previous post now has its missing weapon under construction.</p>

<ul>
<li>Richard C. Jelinek, PetaMem s.r.o.</li>
</ul>
]]>
    </content>
</entry>



<entry>
    <title>Announcing the London Perl &amp; Raku Workshop 2026</title>
    <link rel="alternate" href="https://blogs.perl.org/users/lpw/2026/08/announcing-the-london-perl-raku-workshop-2026.html" />
    <id>tag:blogs.perl.org,2026:/users/lpw//3050.12105</id>
    <published>2026-08-28T14:01:55Z</published>
    <updated>2026-08-28T14:10:38Z</updated>
    <author>
        <name>London Perl Workshop</name>
        <uri>https://act.yapc.eu/lpw2026/</uri>
    </author>
    <category term="lpw" label="lpw" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="lpw2026" label="lpw2026" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="perl" label="perl" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="raku" label="raku" scheme="http://www.sixapart.com/ns/types#tag" />
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/lpw/">
        <![CDATA[<p>The London Perl & Raku Workshop is back again this year!</p>

<p><a href="https://act.yapc.eu/lpw2026">https://act.yapc.eu/lpw2026</a></p>

<p>WHEN: Saturday 21 November 2026<br />
WHERE: The Café at Zoopla, The Cooperage, 5 Copper Row, London SE1 2LH</p>

<p>Registrations and talk submissions are now open. As on previous years, we welcome talks for anything and everything people think might interest the Perl and Raku communities, even if it's not specifically about those languages.</p>

<p>We have a large room for the main track, and a breakout room for ~15 people that is available for smaller sessions, BOFs, hackathons, etc. Submissions for use of that room is also open.</p>

<p>We would like to encourage people to register in advance, to make it easier to access the venue and to give us an idea of numbers.</p>

<p>Looking forward to seeing you all, and the crazy things you've come up with.<br />
</p>]]>
        
    </content>
</entry>



<entry>
    <title>CPerl mode for Emacs is now on GNU ELPA</title>
    <link rel="alternate" href="https://blogs.perl.org/users/haj/2026/08/cperl-mode-for-emacs-is-now-on-gnu-elpa.html" />
    <id>tag:blogs.perl.org,2026:/users/haj//3206.12104</id>
    <published>2026-08-27T02:58:11Z</published>
    <updated>2026-08-27T03:41:14Z</updated>
    <author>
        <name>haj</name>
        
    </author>
    <category term="emacs" label="Emacs" scheme="http://www.sixapart.com/ns/types#tag" />
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/haj/">
        <![CDATA[<p>Since almost forever, <a href="https://www.gnu.org/software/emacs/">Emacs</a> supports editing Perl sources with CPerl mode.  As of now, CPerl mode is not only shipped with Emacs, but also available on <a href="https://elpa.gnu.org/packages/cperl-mode.html">GNU ELPA</a>.  ELPA stands for Emacs Lisp Package Archive, it is for Emacs what CPAN is for Perl.  This means that to use the newest CPerl mode, you do not have to wait for the next Emacs version: If you use
<code>M-x list-packages</code>, you should see cperl-mode in the list and can install it if you have Emacs 27 or newer.</p>

<p>The current version of CPerl mode supports Perl syntax up to Perl 5.44.</p>

<p>For bug reports and change requests, you can use <code>M-x report-emacs-bug</code> from within Emacs (be sure to add "cperl-mode" to the subject), or create an issue on my <a href="https://github.com/HaraldJoerg/cperl-mode">GitHub mirror</a>.  Development will not happen on GitHub, though, this happens in the <a href="https://cgit.git.savannah.gnu.org/cgit/emacs.git/">GNU Emacs repository at savannah</a>.</p>

<p>Happy hacking!</p>

<p>-- haj, your friendly CPerl mode maintainer</p>
]]>
        

    </content>
</entry>



<entry>
    <title>PerlOnJava 5.44.1 released</title>
    <link rel="alternate" href="https://blogs.perl.org/users/flavio_s_glock/2026/08/perlonjava-5441-released.html" />
    <id>tag:blogs.perl.org,2026:/users/flavio_s_glock//524.12103</id>
    <published>2026-08-25T15:46:24Z</published>
    <updated>2026-08-25T15:49:04Z</updated>
    <author>
        <name>Flávio S. Glock</name>
        
    </author>
    <category term="Java" scheme="http://www.sixapart.com/ns/types#category" />
    <category term="PerlOnJava" scheme="http://www.sixapart.com/ns/types#category" />
    <category term="java" label="Java" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="perlonjava" label="perlonjava" scheme="http://www.sixapart.com/ns/types#tag" />
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/flavio_s_glock/">
        <![CDATA[<p>I released PerlOnJava 5.44.1 yesterday:</p>

<p><a href="https://github.com/fglock/PerlOnJava/releases/tag/v5.44.1">https://github.com/fglock/PerlOnJava/releases/tag/v5.44.1</a></p>

<p>This is a big milestone, with the regex implementation, ithreads, taint mode, and async/await now complete. </p>
]]>
        

    </content>
</entry>



<entry>
    <title>ANNOUNCE: Perl.Wiki V1.53, Mojo.Wiki V1.24</title>
    <link rel="alternate" href="https://blogs.perl.org/users/ron_savage/2026/08/announce-perlwiki-v153-mojowiki-v124.html" />
    <id>tag:blogs.perl.org,2026:/users/ron_savage//297.12102</id>
    <published>2026-08-25T11:29:04Z</published>
    <updated>2026-08-25T11:30:50Z</updated>
    <author>
        <name>Ron Savage</name>
        <uri>http://savage.net.au/</uri>
    </author>
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/ron_savage/">
        <![CDATA[<p>As usual, get them and cpan.metacurator.tree.html from my<br />
<a href="https://savage.net.au/index.html">Wiki Haven</a>.<br />
</p>]]>
        
    </content>
</entry>



<entry>
    <title>Is Perlmonks Dead?</title>
    <link rel="alternate" href="https://blogs.perl.org/users/silent11/2026/08/is-perlmonks-dead.html" />
    <id>tag:blogs.perl.org,2026:/users/silent11//49.12101</id>
    <published>2026-08-23T18:30:57Z</published>
    <updated>2026-08-23T18:41:42Z</updated>
    <author>
        <name>silent11</name>
        <uri>http://twitter.com/willwillis</uri>
    </author>
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/silent11/">
        <![CDATA[<p>Every 8-12'ish months I get sentimental/nostalgic over the good 'old days. I'll head over to CPAN and look at the <a href="https://metacpan.org/recent">latest releases</a> investigate a few projects, maybe even make a contribution. I'll then head over to Perlmonks to see what's going on.</p>

<p>Well yesterday, and still today all I see is a fastly "Opps, something went wrong" screen. Is Perlmonks dead? The title of the page is "Client Challenge" so I hope that it's some fastly issue and not that the plug has been pulled on PM.</p>]]>
        
    </content>
</entry>



<entry>
    <title>WebDyne 3.0 released -- now with PAGI, Server-Sent Events and WebSockets</title>
    <link rel="alternate" href="https://blogs.perl.org/users/aspeer/2026/08/ive-released-webdyne-30-an.html" />
    <id>tag:blogs.perl.org,2026:/users/aspeer//4880.12100</id>
    <published>2026-08-22T06:47:43Z</published>
    <updated>2026-08-22T07:00:05Z</updated>
    <author>
        <name>Andrew Speer</name>
        
    </author>
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/aspeer/">
        <![CDATA[<p>I’ve released WebDyne 3.0, an update to the Perl-based dynamic HTML engine I have created.</p>

<p>The biggest addition in 3.0 is support for PAGI, alongside the existing PSGI and Apache/mod_perl backends.</p>

<p>PAGI support brings an asynchronous/event-oriented interface to WebDyne and allows WebDyne applications to handle more than conventional HTTP request/response traffic:</p>

<ul>
	<li><strong>Server-Sent Events (SSE) </strong>for streaming events from Perl applications to browsers</li>
	<li><strong>WebSocket connections</strong> for bidirectional, long-lived connections</li>
	<li>Normal asynchronous <strong>HTTP requests</strong></li>
	<li>PAGI application <strong>lifespan startup/shutdown events</strong></li>
</ul>
WebDyne’s PAGI implementation has dedicated handlers for HTTP, SSE, WebSocket and lifespan scopes, while exposing the PAGI request environment through the normal WebDyne request interface. This means existing WebDyne concepts can be used while taking advantage of event-driven servers.

<p>WebDyne continues to support HTMX, JSON and API concepts in .psp pages.</p>

<p>There’s also a new webdyne.pagi runner, so getting a PAGI-backed WebDyne application running is straightforward:</p>

<pre>
cpanm Task::WebDyne::PAGI
webdyne.pagi --test
</pre>

<p>or serve a directory of .psp files:</p>

<pre>
webdyne.pagi /var/www/html
</pre>

<p>PAGI support is also built into the WebDyne Docker images.</p>

<p>If unfamiliar with WebDyne, it’s (yet another) Perl dynamic HTML engine that’s been around in various forms for quite a while. It lets you embed Perl in HTML/PSP documents, with compiled-page caching, reusable template blocks and separation of Perl code into modules when applications grow beyond simple pages.</p>

<p>3.0 continues to support PSGI/Plack/Starman and Apache/mod_perl, so PAGI is an additional runtime rather than a replacement for the existing ones.</p>

<p>PAGI gives Perl/WebDyne a clean route into applications that need live server → browser updates or persistent bidirectional connections, without having to bolt a separate WebSocket/SSE service onto the application.</p>

<p>Documentation and examples:</p>

<p><a href="https://webdyne.org">https://webdyne.org/</a></p>

<p>See the "Advanced Usage" section for examples of SSE or WebSockets code.</p>

<p>Source:</p>

<p><a href="https://github.com/aspeer/WebDyne">https://github.com/aspeer/WebDyne</a></p>

<p>Feedback, bug reports and especially experiments with the PAGI/SSE/WebSocket support are welcome.<br />
</p>]]>
        
    </content>
</entry>



<entry>
    <title>PAGI 0.002002: Clarifying How Applications Are Loaded</title>
    <link rel="alternate" href="https://blogs.perl.org/users/john_napiorkowski/2026/08/pagi-0002002-clarifying-how-applications-are-loaded.html" />
    <id>tag:blogs.perl.org,2026:/users/john_napiorkowski//556.12099</id>
    <published>2026-08-21T22:25:58Z</published>
    <updated>2026-08-21T22:41:46Z</updated>
    <author>
        <name>john napiorkowski</name>
        
    </author>
    <category term="pagi" label="pagi" scheme="http://www.sixapart.com/ns/types#tag" />
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/john_napiorkowski/">
        <![CDATA[<h2>Introduction</h2>

<p>(crossposted from <a href="https://dev.to/jjn1056/pagi-0002002-clarifying-how-applications-are-loaded-4c0f">dev.to</a>)</p>

<p>PAGI 0.002002 is a specification release. It does not change the runtime shape of a PAGI application. A running application is still one asynchronous code reference:</p>

<pre><code>async sub app {
    my ($scope, $receive, $send) = @_;

    await $send-&gt;({
        type    =&gt; 'http.response.start',
        status  =&gt; 200,
        headers =&gt; [[ 'content-type', 'text/plain; charset=utf-8' ]],
    });

    await $send-&gt;({
        type =&gt; 'http.response.body',
        body =&gt; 'Hello from PAGI!',
        more =&gt; 0,
    });
}
</code></pre>

<p>What the release clarifies is how a general-purpose runner may reach that runtime boundary.</p>

<h2>The gap between Python callables and Perl objects</h2>

<p>PAGI's <code>scope</code>, <code>receive</code>, and <code>send</code> runtime shape was inspired by <a href="https://asgi.readthedocs.io/en/latest/specs/main.html">ASGI</a>. ASGI describes an application as an asynchronous callable receiving those three values. In Python, that callable may be a function or an instantiated object implementing <code>__call__</code>; the distinction disappears at the call site.</p>

<p>Perl does not normally treat a blessed object as a code reference. We could approximate Python with callable-object overloading, but making overload magic part of the gateway contract would obscure a useful boundary. PSGI and Plack already give Perl a more familiar convention: use objects while configuring an application, then produce a code reference for runtime dispatch.</p>

<p>PAGI 0.002002 now makes that convention explicit. A general-purpose application runner should accept either:</p>

<ul>
<li>a native PAGI code reference; or</li>
<li>an already-instantiated object providing <code>to_app</code>.</li>
</ul>

<p>For example, an application component may retain configuration while it is being constructed:</p>

<pre><code>package MyApp;

use Future::AsyncAwait;

sub new {
    my ($class, %args) = @_;
    return bless { greeting =&gt; $args{greeting} // 'Hello' }, $class;
}

sub to_app {
    my ($self) = @_;

    return async sub {
        my ($scope, $receive, $send) = @_;

        await $send-&gt;({
            type    =&gt; 'http.response.start',
            status  =&gt; 200,
            headers =&gt; [[ 'content-type', 'text/plain; charset=utf-8' ]],
        });

        await $send-&gt;({
            type =&gt; 'http.response.body',
            body =&gt; $self-&gt;{greeting},
            more =&gt; 0,
        });
    };
}

# app.pl
package main;

MyApp-&gt;new(greeting =&gt; 'Hello from an application provider');
</code></pre>

<p>A runner adopting this convention calls <code>to_app</code> exactly once for that loaded instance, verifies that it returned a code reference, and uses only that code reference for connection dispatch. It does not call <code>to_app</code> for every request.</p>

<p>This matters for more than performance. Route compilation, middleware construction, and application setup happen at a predictable boundary. A broken provider fails while the application is loading rather than after the server begins accepting traffic. The server, middleware, and application still communicate through one uniform runtime interface.</p>

<h2>Deliberately not magical</h2>

<p>The provider must be an instantiated object. Neither of these is an application provider under the new convention:</p>

<pre><code>'MyApp'          # A package-name string
MyApp-&gt;to_app    # Class-method normalization
</code></pre>

<p>A command-line runner may accept a module name as part of its own discovery interface, but it must construct the object--or otherwise obtain a native application code reference--before PAGI provider normalization begins. This keeps module loading, constructor arguments, and plugin policy out of the gateway protocol.</p>

<p>Blessed code references remain code references and are dispatched directly. Their code-reference nature takes precedence over a <code>to_app</code> method.</p>

<p>Application lifecycle is also unchanged. Startup and shutdown belong to the PAGI lifespan protocol. Methods on a provider object do not silently become lifecycle hooks.</p>

<h2>A clearer server boundary</h2>

<p>The accompanying server integration specification no longer requires every conforming server class to implement a particular <code>new(app =&gt; ...)</code> constructor and <code>run</code> method. A server might be a class, function, embedded service, or external process. Conformance is about how it invokes the normalized application and exchanges PAGI events, not the shape of its configuration API.</p>

<p>This separates three responsibilities that had previously been too easy to conflate:</p>

<ol>
<li>Deployment tooling discovers and constructs an application.</li>
<li>A provider object is normalized once through <code>to_app</code>.</li>
<li>The server dispatches only the resulting PAGI code reference.</li>
</ol>

<p>The next step is to bring the reference PAGI runner into line with this clarification. This release defines the portable boundary first; it does not claim that every existing runner already accepts provider objects.</p>

<h2>One more clarification: what <code>$send</code> completion means</h2>

<p>PAGI 0.002002 also gathers the <code>$send</code> Future contract into one place. Applications should continue to await every send:</p>

<pre><code>await $send-&gt;({
    type =&gt; 'http.response.body',
    body =&gt; $chunk,
    more =&gt; 1,
});
</code></pre>

<p>Successful resolution means the server has validated and consumed the event, finished with any application-owned resource tied to it, and accepted the output into its outbound processing path. It does <strong>not</strong> mean the client has received the bytes. A server may keep the Future pending to apply backpressure, so awaiting <code>$send</code> is how an application self-paces without blocking the Perl thread.</p>

<h2>Versioning</h2>

<p>The distribution version is <strong>0.002002</strong>. The core PAGI specification advances to draft <strong>0.4</strong>, while the server integration specification is draft <strong>0.2</strong>. WWW and Lifespan remain at draft <strong>0.3</strong>. Core and protocol specifications now explicitly evolve independently; an omitted protocol <code>spec_version</code> uses that protocol's documented compatibility default rather than borrowing the core version.</p>

<p>The result is a small but important cleanup: PAGI keeps one minimal runtime protocol while giving Perl applications a conventional, explicit way to carry configuration and structure up to the point where runtime dispatch begins.</p>

<p>The complete change is available in <a href="https://github.com/jjn1056/pagi/pull/60">PAGI pull request #60</a>.</p>
]]>
        <![CDATA[<h2>Introduction</h2>

<p>PAGI 0.002002 is a specification release. It does not change the runtime shape of a PAGI application. A running application is still one asynchronous code reference:</p>

<p>```perl
async sub app {
    my ($scope, $receive, $send) = @_;</p>

<pre><code>await $send-&gt;({
    type    =&gt; 'http.response.start',
    status  =&gt; 200,
    headers =&gt; [[ 'content-type', 'text/plain; charset=utf-8' ]],
});

await $send-&gt;({
    type =&gt; 'http.response.body',
    body =&gt; 'Hello from PAGI!',
    more =&gt; 0,
});
</code></pre>

<p>}
```</p>

<p>What the release clarifies is how a general-purpose runner may reach that runtime boundary.</p>

<h2>The gap between Python callables and Perl objects</h2>

<p>PAGI's <code>scope</code>, <code>receive</code>, and <code>send</code> runtime shape was inspired by <a href="https://asgi.readthedocs.io/en/latest/specs/main.html">ASGI</a>. ASGI describes an application as an asynchronous callable receiving those three values. In Python, that callable may be a function or an instantiated object implementing <code>__call__</code>; the distinction disappears at the call site.</p>

<p>Perl does not normally treat a blessed object as a code reference. We could approximate Python with callable-object overloading, but making overload magic part of the gateway contract would obscure a useful boundary. PSGI and Plack already give Perl a more familiar convention: use objects while configuring an application, then produce a code reference for runtime dispatch.</p>

<p>PAGI 0.002002 now makes that convention explicit. A general-purpose application runner should accept either:</p>

<ul>
<li>a native PAGI code reference; or</li>
<li>an already-instantiated object providing <code>to_app</code>.</li>
</ul>

<p>For example, an application component may retain configuration while it is being constructed:</p>

<p>```perl
package MyApp;</p>

<p>use Future::AsyncAwait;</p>

<p>sub new {
    my ($class, %args) = @_;
    return bless { greeting => $args{greeting} // 'Hello' }, $class;
}</p>

<p>sub to<em>app {
    my ($self) = @</em>;</p>

<pre><code>return async sub {
    my ($scope, $receive, $send) = @_;

    await $send-&gt;({
        type    =&gt; 'http.response.start',
        status  =&gt; 200,
        headers =&gt; [[ 'content-type', 'text/plain; charset=utf-8' ]],
    });

    await $send-&gt;({
        type =&gt; 'http.response.body',
        body =&gt; $self-&gt;{greeting},
        more =&gt; 0,
    });
};
</code></pre>

<p>}</p>

<h1>app.pl</h1>

<p>package main;</p>

<p>MyApp->new(greeting => 'Hello from an application provider');
```</p>

<p>A runner adopting this convention calls <code>to_app</code> exactly once for that loaded instance, verifies that it returned a code reference, and uses only that code reference for connection dispatch. It does not call <code>to_app</code> for every request.</p>

<p>This matters for more than performance. Route compilation, middleware construction, and application setup happen at a predictable boundary. A broken provider fails while the application is loading rather than after the server begins accepting traffic. The server, middleware, and application still communicate through one uniform runtime interface.</p>

<h2>Deliberately not magical</h2>

<p>The provider must be an instantiated object. Neither of these is an application provider under the new convention:</p>

<p><code>perl
'MyApp'          # A package-name string
MyApp-&gt;to_app    # Class-method normalization
</code></p>

<p>A command-line runner may accept a module name as part of its own discovery interface, but it must construct the object--or otherwise obtain a native application code reference--before PAGI provider normalization begins. This keeps module loading, constructor arguments, and plugin policy out of the gateway protocol.</p>

<p>Blessed code references remain code references and are dispatched directly. Their code-reference nature takes precedence over a <code>to_app</code> method.</p>

<p>Application lifecycle is also unchanged. Startup and shutdown belong to the PAGI lifespan protocol. Methods on a provider object do not silently become lifecycle hooks.</p>

<h2>A clearer server boundary</h2>

<p>The accompanying server integration specification no longer requires every conforming server class to implement a particular <code>new(app =&gt; ...)</code> constructor and <code>run</code> method. A server might be a class, function, embedded service, or external process. Conformance is about how it invokes the normalized application and exchanges PAGI events, not the shape of its configuration API.</p>

<p>This separates three responsibilities that had previously been too easy to conflate:</p>

<ol>
<li>Deployment tooling discovers and constructs an application.</li>
<li>A provider object is normalized once through <code>to_app</code>.</li>
<li>The server dispatches only the resulting PAGI code reference.</li>
</ol>

<p>The next step is to bring the reference PAGI runner into line with this clarification. This release defines the portable boundary first; it does not claim that every existing runner already accepts provider objects.</p>

<h2>One more clarification: what <code>$send</code> completion means</h2>

<p>PAGI 0.002002 also gathers the <code>$send</code> Future contract into one place. Applications should continue to await every send:</p>

<p><code>perl
await $send-&gt;({
    type =&gt; 'http.response.body',
    body =&gt; $chunk,
    more =&gt; 1,
});
</code></p>

<p>Successful resolution means the server has validated and consumed the event, finished with any application-owned resource tied to it, and accepted the output into its outbound processing path. It does <strong>not</strong> mean the client has received the bytes. A server may keep the Future pending to apply backpressure, so awaiting <code>$send</code> is how an application self-paces without blocking the Perl thread.</p>

<h2>Versioning</h2>

<p>The distribution version is <strong>0.002002</strong>. The core PAGI specification advances to draft <strong>0.4</strong>, while the server integration specification is draft <strong>0.2</strong>. WWW and Lifespan remain at draft <strong>0.3</strong>. Core and protocol specifications now explicitly evolve independently; an omitted protocol <code>spec_version</code> uses that protocol's documented compatibility default rather than borrowing the core version.</p>

<p>The result is a small but important cleanup: PAGI keeps one minimal runtime protocol while giving Perl applications a conventional, explicit way to carry configuration and structure up to the point where runtime dispatch begins.</p>

<p>The complete change is available in <a href="https://github.com/jjn1056/pagi/pull/60">PAGI pull request #60</a>.</p>
]]>
    </content>
</entry>



<entry>
    <title>German Perl/Raku Workshop 2027 in Hanover - 14.04.2027-16.04.2027</title>
    <link rel="alternate" href="https://blogs.perl.org/users/max_maischein/2026/08/german-perlraku-workshop-2027-in-hanover---14042027-16042027.html" />
    <id>tag:blogs.perl.org,2026:/users/max_maischein//227.12098</id>
    <published>2026-08-18T22:19:53Z</published>
    <updated>2026-08-18T22:23:28Z</updated>
    <author>
        <name>Max Maischein</name>
        <uri>https://corion.net</uri>
    </author>
    <category term="conferences" scheme="http://www.sixapart.com/ns/types#category" />
    <category term="gpw2027" label="gpw2027" scheme="http://www.sixapart.com/ns/types#tag" />
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/max_maischein/">
        <![CDATA[<p>(English version below)</p>

<p>Hallo zusammen,</p>

<p>wir laden Euch herzlich ein<br />
zum <a href="https://act.yapc.eu/gpw2027/">Deutschen Perl/Raku Workshop 2027</a>.</p>

<p>Der Workshop findet nächstes Jahr vom Mittwoch 14. April bis<br />
Freitag 16. April im <a href="https://www.stadtteil-zentrum-nordstadt.de/">Stadteilzentrum Nordstadt in Hannover</a> statt.</p>

<p>Die Webseite und der Call for Papers sind bereits online unter<br />
<a href="https://act.yapc.eu/gpw2027/cfp.html">https://act.yapc.eu/gpw2027/cfp.html</a>. Wir freuen uns auf viele interessante<br />
Vorträge!</p>

<p>Über Unterstützung durch Sponsoren freuen wir uns immer. Wenn Ihr bzw. Eure<br />
Firma den Workshop unterstützen möchtet, wendet Euch gerne an uns. Wir finden<br />
gemeinsam sicher eine Möglichkeit!</p>

<p>Wenn Ihr Fragen an die Organisatoren habt, erreicht Ihr uns am besten<br />
direkt unter orga2027@german-perl-workshop.de .</p>

<p>Wir freuen uns auf Eure Teilnahme,<br />
Max Maischein für die Organisatoren und Frankfurt.pm</p>

<p>Wir arbeiten noch an<br />
Hotelempfehlungen und veröffentlichen diese auf der Webseite.<br />
</p>]]>
        <![CDATA[<p>Hello everyone,</p>

<p>We cordially invite you<br />
to the <a href="https://act.yapc.eu/gpw2027/">German Perl/Raku Workshop 2027</a>.</p>

<p>The workshop will take place next year from Wednesday, April 14, through<br />
Friday, April 16, at the <a href="https://www.stadtteil-zentrum-nordstadt.de/">Stadteilzentrum Nordstadt in Hanover</a>.</p>

<p>The website and the call for papers are already online at<br />
<a href="https://act.yapc.eu/gpw2027/cfp.html">https://act.yapc.eu/gpw2027/cfp.html</a>. We look forward to many interesting<br />
presentations!</p>

<p>We're always happy to receive support from sponsors. If you or your<br />
company would like to support the workshop, please feel free to contact us. Together, we're<br />
sure to find a way to make it happen!</p>

<p>If you have any questions for the organizers, the best way to reach us is<br />
directly at orga2027@german-perl-workshop.de.</p>

<p>We look forward to your participation,<br />
Max Maischein on behalf of the organizers and Frankfurt.pm</p>

<p>We're still working on<br />
hotel recommendations and will post them on the website.</p>

<p><br />
Translated with DeepL.com (free version)</p>]]>
    </content>
</entry>



<entry>
    <title>Pacman in Perl/Tk</title>
    <link rel="alternate" href="https://blogs.perl.org/users/alex1/2026/08/pacman-in-perltk.html" />
    <id>tag:blogs.perl.org,2026:/users/alex1//2058.12097</id>
    <published>2026-08-13T02:27:34Z</published>
    <updated>2026-08-13T02:50:04Z</updated>
    <author>
        <name>Alex</name>
        <uri>http://www.perltk.de</uri>
    </author>
    <category term="game" label="Game" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="pacman" label="Pacman" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="perl" label="Perl" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="tk" label="Tk" scheme="http://www.sixapart.com/ns/types#tag" />
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/alex1/">
        <![CDATA[<h1>Revisiting a Pac-Man Implementation in Perl/Tk</h1>

<p>More or less by accident, I recently came across an old project of mine from around 2013/2014. In it, I had tried to approximate Pac-Man using Perl and Tk.</p>

<p>It was never intended to be an exact reimplementation of the original game. There are plenty of those in almost every programming language imaginable. What interested me more was a different question: how could the structure of a game like Pac-Man be represented as a relatively simple data structure, with the Perl/Tk user interface merely visualizing that structure on a Canvas?</p>

<p>Back then, of course, there was no generative AI sitting next to me explaining what I had built.</p>

<p>More than ten years later, there is.</p>

<p>So I fed the old implementation to an AI and asked it to describe the underlying concept. Maybe, the approach is interesting for you. </p>

<p>So here it is.</p>

<p>Have fun reading.</p>

<h2>The current implementation</h2>

<p>Here is a short video showing the current state of the implementation:</p>

<p><iframe width="560" height="315" src="https://www.youtube.com/embed/zzKcHtNgwtE" title="Pac-Man implementation in Perl/Tk" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe></p>

<h2>The basic idea: the game world is a grid</h2>

<p>At the heart of the implementation is a two-dimensional array.</p>

<p>The Pac-Man maze is not primarily a collection of graphical objects. Instead, every position in the maze is represented by one cell in a table-like data structure.</p>

<p>Conceptually, it looks something like this:</p>

<pre>
1 1 1 1 1 1 1
1 o o o o o 1
1 o 1 1 1 o 1
1 o 0 0 0 o 1
1 1 1 1 1 1 1
</pre>

<p>Each value describes the logical state of one tile.</p>

<p>For example:</p>

<pre>
1 = wall
0 = free space
o = pill
O = power pill
W = warp point
</pre>

<p>The visual maze that appears on screen is generated from this data structure.</p>

<p>That distinction is important: the Canvas is not the game world. It is a visual representation of the game world.</p>

<h2>Pac-Man does not really move in pixels</h2>

<p>Pac-Man's position is represented by two integer values:</p>

<pre>
row
column
</pre>

<p>The same principle applies to the ghosts.</p>

<p>So from the game's point of view, Pac-Man may be located at:</p>

<pre>
row = 17
column = 17
</pre>

<p>There is no requirement for the actual game logic to know that his center might currently be at pixel position 350/350 on the screen.</p>

<p>The pixel position can simply be calculated from the tile position.</p>

<p>In this implementation, a tile is 20 by 20 pixels. Rendering therefore essentially follows the rule:</p>

<pre>
x = column * 20
y = row * 20
</pre>

<p>with a small offset added to place the image in the center of the tile.</p>

<p>This creates a useful separation between two different things:</p>

<pre>
Logical game state
        |
        v
row / column
        |
        v
Rendering
        |
        v
pixel x / pixel y
</pre>

<p>I planned to make the movement look smoother later.</p>

<h2>Movement is a transition between tiles</h2>

<p>This also means that movement is discrete.</p>

<p>If Pac-Man is standing on tile:</p>

<pre>
[17,17]
</pre>

<p>and moves to the right, the game calculates:</p>

<pre>
[17,17] -> [17,18]
</pre>

<p>Before that transition is accepted, the target tile is inspected.</p>

<p>If the target contains a wall, Pac-Man cannot move there.</p>

<p>If it is accessible, his logical row and column are updated.</p>

<p>The basic process is therefore roughly:</p>

<pre>
Player input
    |
    v
Determine intended direction
    |
    v
Calculate neighbouring tile
    |
    v
Check whether the tile is accessible
    |
    v
Update logical position
    |
    v
Handle events
    |
    v
Update visualization
</pre>

<p>There is no physics engine involved, no collision rectangle moving continuously through the maze and no need to determine whether two polygons intersect.</p>

<p>For this kind of game, the grid already contains most of the information required by the game logic.</p>

<h2>Smooth movement can still be just an animation</h2>

<p>The early version simply made Pac-Man jump visually from one tile to the next.</p>

<p>That obviously works, but it does not look particularly elegant.</p>

<p>I also found versions with smoother movement animations, but I still didn't get them to work that properly.</p>

<p>Conceptually, it was designed like this:</p>

<pre>
Logical state:

<p>[17,17] -> [17,18]</p>

<p><br />
Visual transition:</p>

<p>350 -> 354 -> 358 -> 362 -> 366 -> 370<br />
</pre></p>

<p>This does not necessarily require changing the logical model.</p>

<p>The game can still reason about tiles, while the user interface interpolates between their pixel positions.</p>

<p>In other words, smooth movement can be a presentation concern rather than a game-logic concern.</p>

<h2>The tiles contain game state as well</h2>

<p>...which might not be the best thing. But hey. We all started once.</p>

<p>A pill is not necessarily an independent object with its own coordinates.</p>

<p>Instead, the board itself can say:</p>

<pre>
board[row][column] = "o"
</pre>

<p>When Pac-Man enters that tile, the game detects the pill, increases the score and changes the tile to free space:</p>

<pre>
o -> 0
</pre>

<p>The maze therefore acts as both a description of the level geometry and a container for parts of the current game state.</p>

<p>This makes interactions rather straightforward.</p>

<p>Entering a tile can trigger events depending on what that tile contains:</p>

<pre>
wall       -> movement rejected
pill       -> increase score, clear tile
power pill -> special behaviour
warp point -> change target position
free space -> nothing special
</pre>

<h2>Collisions become very simple</h2>

<p>The same model simplifies collisions with ghosts.</p>

<p>Pac-Man and a ghost collide if they occupy the same logical tile:</p>

<pre>
pacman.row == ghost.row
and
pacman.column == ghost.column
</pre>

<p>Again, no pixel-perfect collision detection used.</p>

<p><br />
<h2>Warp tunnels are just special tile transitions</h2></p>

<p>The warp tunnel fits naturally into the same concept.</p>

<p>A warp point is simply another special tile type:</p>

<pre>
W
</pre>

<p>When Pac-Man enters such a tile, the normal target position is replaced by a configured position on the other side of the board.</p>

<p>One detail turned out to be useful here: the exit position does not have to be the other warp tile itself.</p>

<p>Instead, Pac-Man can be placed on the first normal tile immediately inside the maze.</p>

<p>For example:</p>

<pre>
[W] [ ] [ ] ... [ ] [W]
     ^               ^
     |               |
   exit            entrance
</pre>

<p>Entering the warp on the right while moving to the right places Pac-Man just inside the left side of the maze.</p>

<p>His movement direction remains unchanged.</p>

<p>So logically:</p>

<pre>
move right
    |
    v
enter right warp
    |
    v
appear on left side
    |
    v
continue moving right
</pre>

<p>The same principle works for vertical warp tunnels.</p>

<p>A warp changes the position. It does not have to change the movement direction.</p>

<h2>The maze can also be viewed as a graph</h2>

<p>There is another way of looking at this data structure which I find interesting today.</p>

<p>Although the board is stored as a two-dimensional grid, the accessible part of the maze can effectively be interpreted as a graph.</p>

<p>Every accessible tile is a node.</p>

<p>Possible movements to neighbouring tiles are edges.</p>

<p>A corridor therefore looks roughly like this:</p>

<pre>
[o] -- [o] -- [o] -- [o]
</pre>

<p>while an intersection might look like this:</p>

<pre>
        [ ]
         |
[ ] -- [ ] -- [ ]
         |
        [ ]
</pre>

<p>The old implementation already models ghosts using row and column coordinates just like Pac-Man, although the ghost movement itself was still largely unfinished in the version I rediscovered.</p>

<h2>Model first, visualization second</h2>

<p>Looking at the code today, AI would describe its central idea as:</p>

<pre>
                  GAME MODEL

<p>      Board                     Entities<br />
  board[row][col]          Pac-Man: row, col<br />
                           Ghost:   row, col<br />
        |                         |<br />
        +------------+------------+<br />
                     |<br />
                 Game logic<br />
                     |<br />
             tile-to-tile movement<br />
                     |<br />
          collisions / pills / warp<br />
                     |<br />
                     v<br />
                    VIEW<br />
                 Tk Canvas<br />
                     |<br />
              row/col -> x/y<br />
                     |<br />
          optional interpolation<br />
                     |<br />
                     v<br />
                  Screen<br />
</pre></p>

<p>It is obviously not a formal Model-View architecture, and I certainly did not sit down in 2013 thinking, "I am going to create an elegant architectural separation here."</p>

<p>But the basic separation is there.</p>

<p>The authoritative state of the game is not the Canvas.</p>

<p>It is the data behind it.</p>]]>
        
    </content>
</entry>



<entry>
    <title>pperl 0.6.12+ - &quot;Shall we play a game?&quot;</title>
    <link rel="alternate" href="https://blogs.perl.org/users/petamem/2026/08/pperl-0612---lets-play-a-game.html" />
    <id>tag:blogs.perl.org,2026:/users/petamem//1658.12095</id>
    <published>2026-08-11T03:41:21Z</published>
    <updated>2026-08-11T12:11:59Z</updated>
    <author>
        <name>PetaMem</name>
        <uri>http://www.petamem.com</uri>
    </author>
    <category term="benchmark" label="benchmark" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="jit" label="jit" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="pperl" label="pperl" scheme="http://www.sixapart.com/ns/types#tag" />
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/petamem/">
        <![CDATA[<p>There is no release attached to this post - the second half will
explain why. It is a post about measurement, twice over. Earlier
posts claimed that pperl is under high-velocity development, and a
claim like that should be quantified - so we do, with
the same statistics perl's own release notes use, epigraph
included. And we have started playing the Computer Language
Benchmarks Game: one program is finished, the result is a 36x
speedup on the game's terms, and the conclusions have reset our
speed goals.</p>
]]>
        <![CDATA[<h2>The epigraph, answered</h2>

<p>Perl release announcements traditionally open with an epigraph, a
short quotation the release manager chooses. For 5.44.0, Leon
Timmermans chose Frank Herbert's Dune:</p>

<blockquote>
Once men turned their thinking over to machines in the hope that
this would set them free. But that only permitted other men with
machines to enslave them.
</blockquote>

<p>It is a nice touch with all these thinking machines around these days.
Given pperl currently tracks 5.44, we think our epigraph should remain
within the same universe:</p>

<blockquote>
"We have just folded space from Ix... Many machines on Ix... new
machines. Better than those on Richesse."
</blockquote>

<h2>Velocity, in perldelta units</h2>

<p>Every perl release closes its perldelta with the same paragraph of
statistics. For 5.44.0 it reads:</p>

<blockquote>
Perl 5.44.0 represents approximately 12 months of development since
Perl 5.42.0 and contains approximately 270,000 lines of changes
across 1,300 files from 71 authors.<br><br>
Excluding auto-generated files, documentation and release tools,
there were approximately 110,000 lines of changes to 860 .pm, .t,
.c and .h files.
</blockquote>

<p>The same yardstick, applied to pperl per release sprint. Counting
is the tree diff between the two release commits, over half-open
ranges, so consecutive sprints neither overlap nor leave a gap.
The code columns apply the same exclusions as perl's second
sentence - no auto-generated files, no documentation, no assets,
no vendored modules, no tooling; .rs, .pl, .pm and .t files only:</p>

<table border="1" cellspacing="0" style="border-collapse: collapse; margin: 1em 0;">
<thead>
<tr><th style="border: 1px solid #888; padding: 3px 8px;">sprint</th><th style="border: 1px solid #888; padding: 3px 8px;">days</th><th style="border: 1px solid #888; padding: 3px 8px;">commits</th><th style="border: 1px solid #888; padding: 3px 8px;">lines</th><th style="border: 1px solid #888; padding: 3px 8px;">files</th><th style="border: 1px solid #888; padding: 3px 8px;">code lines</th><th style="border: 1px solid #888; padding: 3px 8px;">code files</th></tr>
</thead>
<tbody>
<tr><td style="border: 1px solid #888; padding: 3px 8px;">0.5.0 .. 0.6.0</td><td style="border: 1px solid #888; padding: 3px 8px; text-align: right;">106</td><td style="border: 1px solid #888; padding: 3px 8px; text-align: right;">403</td><td style="border: 1px solid #888; padding: 3px 8px; text-align: right;">8,756,000</td><td style="border: 1px solid #888; padding: 3px 8px; text-align: right;">43,000</td><td style="border: 1px solid #888; padding: 3px 8px; text-align: right;">254,000</td><td style="border: 1px solid #888; padding: 3px 8px; text-align: right;">770</td></tr>
<tr><td style="border: 1px solid #888; padding: 3px 8px;">0.6.0 .. 0.6.8</td><td style="border: 1px solid #888; padding: 3px 8px; text-align: right;">16</td><td style="border: 1px solid #888; padding: 3px 8px; text-align: right;">153</td><td style="border: 1px solid #888; padding: 3px 8px; text-align: right;">1,389,000</td><td style="border: 1px solid #888; padding: 3px 8px; text-align: right;">20,000</td><td style="border: 1px solid #888; padding: 3px 8px; text-align: right;">270,000</td><td style="border: 1px solid #888; padding: 3px 8px; text-align: right;">595</td></tr>
<tr><td style="border: 1px solid #888; padding: 3px 8px;">0.6.8 .. 0.6.xx</td><td style="border: 1px solid #888; padding: 3px 8px; text-align: right;">13</td><td style="border: 1px solid #888; padding: 3px 8px; text-align: right;">294</td><td style="border: 1px solid #888; padding: 3px 8px; text-align: right;">1,889,000</td><td style="border: 1px solid #888; padding: 3px 8px; text-align: right;">27,000</td><td style="border: 1px solid #888; padding: 3px 8px; text-align: right;">105,000</td><td style="border: 1px solid #888; padding: 3px 8px; text-align: right;">475</td></tr>
</tbody>
</table>

<p>Held against the quote above: the 16 days between 0.6.0 and 0.6.8
contain about as many lines of code change - 270,000 - as
perl 5.44.0 reports for twelve months of changes of every kind,
and the 13 days since 0.6.8 roughly match perl's full-year code
figure. That is what "high-velocity development" means here:
currently, about one perl-year of code change every two weeks.
Greenfield churn is not mature-project maintenance, and we
are not 71 authors - of course.</p>

<p>Two footnotes: the gross figures are mostly documentation, since
the manual ships in English and 41 translated locales; and all of
it lands under a harness - some 1,800 test files comparing pperl's
output against perl 5.44 byte for byte, over a runtime of roughly
800,000 lines of Rust.</p>

<h2>The Benchmarks Game</h2>

<p>The <a href="https://benchmarksgame-team.pages.debian.net/benchmarksgame/index.html">Computer Language Benchmarks Game</a>
is the longest-running cross-language benchmark in public view:
fixed problems, published source, any language, strict rules about
keeping the same algorithm. Perl's standing there is not a secret.
On nbody, the site lists C at about 2.1 s, node.js at about 8.6 s,
and the fastest Perl entry at about seven minutes - 215x behind C.</p>

<p>The film our title borrows from concluded that the only winning
move is not to play, and that has largely been Perl's relationship
with the Game: the entries are old, the rankings are what they
are, and the results get cited mainly by people arguing against
Perl. With a JIT in the runtime, not playing stopped being the
rational move. So we play - under house rules that keep the game
from turning into solitaire:</p>

<ul>
<li>Any entry we measure is Perl that implements the same algorithm
as the published one, runs on perl5 too, and produces
byte-identical output on both runtimes, at every input size.</li>
<li>The JIT is not shaped around the Game's programs, nor the
programs around the JIT: every gap a program exposes is fixed as
a general construct, or not at all.</li>
</ul>

<p><strong>The threads obstacle.</strong> The Game allows hand-threaded entries,
and the strong Perl entries use ithreads - mandelbrot, the program
this post reports, included. pperl is deliberately non-threaded;
its parallelism is Rayon inside the runtime, not a thread API in
the language, so the published entry cannot run on it at all. The
rules permit different code as long as the algorithm is the same,
and they do not require a parallel program either - some published
entries are serial. We stay inside those lines: our rewrite keeps
the kernel byte-identical and replaces the thread pool with a
plain loop over scanlines, output pixel-identical. The eventual
pperl entry will be more idiomatic, plainer Perl than today's
highly optimized one - that is the point of a runtime that does
the optimizing. On perl5 both versions run, which keeps the
bracket visible - four threads for the published entry, one for
the rewrite may be enough, more are an option.</p>

<p><strong>The result, in ballpark.</strong> Identical algorithm, single-threaded,
the JIT runs mandelbrot around 36x faster than perl5 - with
<code>--no-jit</code> at perl5 parity as the control, so the win is the
compiler, not baseline games. One pperl core beats the published
entry's four threads in wall clock, and by an order of magnitude
in CPU time - and the parallelizer contributes nothing here yet,
so that is one core with more still on the table. The reading that
matters most: the nearest runtime still ahead of us on this kernel
is node.js, within a factor of 1.5 - the competition has moved
from perl5 to V8.</p>

<p>And that is the promised explanation for the missing release.
Playing the Game reset the goal: the reference for a JIT-equipped
pperl is no longer perl5, it is the fastest scripted language of
the day - which, more often than not, means node.js and its V8.
Competing there takes more than closing gaps one by one, so we
have started the pperl JIT architecture from scratch - again.
Releases resume when the rebuild lands.</p>

<p>One more design decision belongs in that explanation. Benchmarks
may or may not translate to real-world programs, so the new JIT is
designed around real-world programs - the desired Game performance
has to arrive as a side effect of accelerating those.</p>

<p>And that is the promise: the next pperl release will smash every
interpreted language in the Game, and compete in every single
benchmark with the best scripted language there. It will do so not
with a specialized JIT tailored to this benchmark code, but with a
general-purpose JIT that also happens to smash this game.</p>

<p>As always: no dates. On the other hand you can now quantify the
velocity.</p>

<ul>
<li>Richard C. Jelinek, PetaMem s.r.o.</li>
</ul>
]]>
    </content>
</entry>


</feed>