<?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-06-01T22:13:51Z</updated>


<entry>
    <title>This week in PSC (227) | 2026-06-01</title>
    <link rel="alternate" href="https://blogs.perl.org/users/psc/2026/06/this-week-in-psc-227-2026-06-01.html" />
    <id>tag:blogs.perl.org,2026:/users/psc//4112.12064</id>
    <published>2026-06-01T22:10:41Z</published>
    <updated>2026-06-01T22:13: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>This week we were back to full strength. We have now dealt with all of the belated issues and all of the blockers. Paul will be shipping 5.43.11 very shortly. With the amount of changes we have had to merge, we will not be able to rush the .11 cycle, but we intend to start the work on the 5.44 RC early, to ensure that we can release with as little additional delay as possible.</p>

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

    </content>
</entry>



<entry>
    <title>Introducing ZuzuScript</title>
    <link rel="alternate" href="https://blogs.perl.org/users/toby_inkster/2026/05/introducing-zuzuscript.html" />
    <id>tag:blogs.perl.org,2026:/users/toby_inkster//1019.12063</id>
    <published>2026-06-01T01:22:23Z</published>
    <updated>2026-06-01T01:49:22Z</updated>
    <author>
        <name>Toby Inkster</name>
        <uri>https://toby.ink/</uri>
    </author>
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/toby_inkster/">
        <![CDATA[<div>
<p>So I've created a programming language which blends a fairly JavaScript-like syntax with fairly Perl-like semantics, and a few other features that I haven't really seen in many programming languages.</p>
<p>This Perl:<p>
<pre>use Path::Tiny;

my $str = uc(substr(Path::Tiny->new("myfile.txt")->slurp_utf8, 0, 80));
</pre>
<p>Becomes this in ZuzuScript:</p>
<pre>from std/path import Path;

let str := new Path("myfile.txt")
  ▷ ^^.slurp_utf8
  ▷ ^^[0:80]
  ▷ uc ^^;
</pre>
<p>The <code>▷</code> operator means "evaluate the left side, then evaluate the right side with <code>^^</code> set to the result of the left side". It's conceptually similar to <code>|</code> in shells and seems to make a lot of expressions so much easier to understand.</p>
</div>]]>
        <![CDATA[<p>Other features I like:</p>

<ul>
<li><p>Path/query operators for XPath/JSONPath-like deep access to nested objects.</p></li>
<li><p>Built-in <code>async</code>/<code>await</code>.</p></li>
<li><p>OO including roles/traits.</p></li>
<li><p>Runs in the browser!</p></li>
<li><p>PairLists (like hashes, but with ordered keys that allow duplicate keys)</p></li>
<li><p><code>for</code>/<code>else</code></p></li>
</ul>

<p>Familiar things from Perl: documentation uses pod, variables are lexical (actually almost everything is lexical), there's a topic variable (but it's called <code>^^</code> instead of <code>$_</code>), different operators for different data types (<code>&gt;</code> for numbers but <code>gt</code> for strings), weak typing, keywords like <code>say</code> and <code>warn</code>, first class regexps, and a CPAN-like site for sharing modules.</p> 

<p>The primary implementation is in Perl, but there are alternative implementations in Rust and JavaScript. Yes, this is coded with AI assistance.</p>

<p>More info: <a href="https://zuzulang.org/">https://zuzulang.org/</a>.</p>]]>
    </content>
</entry>



<entry>
    <title>ANNOUNCE: Perl.Wiki V 1.47, JSTree copy V 1.21</title>
    <link rel="alternate" href="https://blogs.perl.org/users/ron_savage/2026/05/announce-perlwiki-v-147-jstree-copy-v-121.html" />
    <id>tag:blogs.perl.org,2026:/users/ron_savage//297.12062</id>
    <published>2026-05-31T10:11:02Z</published>
    <updated>2026-05-31T10:14:17Z</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>Both are available from my <a href="https://savage.net.au/">Wiki Haven</a>.</p>

<p>Next step will be the validation module for CPAN::MetaCurator, using the new:<br />
use feature 'class'<br />
code.</p>

<p>After that, back to the re-write of all *.pm in CPAN::MetaCurator.<br />
</p>]]>
        
    </content>
</entry>



<entry>
    <title>ANNOUNCE: Perl.Wiki V 1.46 &amp; other news</title>
    <link rel="alternate" href="https://blogs.perl.org/users/ron_savage/2026/05/announce-perlwiki-v-146-other-news.html" />
    <id>tag:blogs.perl.org,2026:/users/ron_savage//297.12060</id>
    <published>2026-05-28T09:01:38Z</published>
    <updated>2026-05-28T09:18:14Z</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>The new Perl.Wiki.html V 1.46 & the JSTree version V 1.20 are available from my<br />
<a href="https://savage.net.au/">Wiki Haven.</a></p>

<p>Other news:<br />
(a) CPAN::MetaCurator:<br />
Status: On github & on CPAN.<br />
There are some issues:<br />
1. It is becoming increasingly awkward to process the records read from the database<br />
in order to transform them into JSTree stuff, so I am going to re-write the code to firstly<br />
read the db & store the data in a Tree::DAG_Node. That way the data more nearly<br />
matches the JSTree structure so the common cases & all the special cases (FAQ,<br />
See Also, pre-formatted paras, etc) are more easily processed.<br />
2. I use Moo in this module as I do so often but in CPAN::MetaCurator::Export I must<br />
have done something weird because sub BUILD is not called & I can't over-ride various<br />
defaults by passing options to new(). Solution: Re-write whole module using Perl's new<br />
feature 'class', thereby teaching myself how to do that & hopefully fixing the over-ride<br />
problem. This new code is temporarily called CPAN::MetaXPAN. It's on github but is<br />
rudimentary at best. Avert your eyes.<br />
Back to CPAN::MetaCurator...<br />
3. It contains a module, CPAN::MetaCurator::Search, which reads a file of module names<br />
& reports whether or not they are in Perl.Wiki.html. It is part of the future automation of the<br />
process of adding modules to Perl.Wiki.html.<br />
4. The next extension will be CPAN::MetaCurator::Validate, which will solely validate the<br />
format of tiddlers.json as exported from Perl.Wiki.html, just looking for typos.</p>

<p>(b) CPAN::MetaPackager:<br />
Status: On github & on CPAN.<br />
Do nothing.</p>

<p>(c) CPAN::MetaBuiltin:<br />
Status: Nothing in this module has been written yet, pending the outcome of the above<br />
feature 'class' experiment. I used perlbrew to install 2 versions of Perl, so then I can scan<br />
those & put the list of modules shipped with each Perl into a single SQLite db. Then that<br />
db can be read by CPAN::MetaCurator & that will enable me to annotate the modules<br />
listed in the JS tree with a flag indicating the module ships with some versions of Perl. It is<br />
currently /not/ my intention to list all versions of Perl the modules ship with.</p>

<p>(d) CPAN::MetaDeprecated:<br />
Status: Nothing in this module has been written yet, pending the outcome of the above<br />
feature 'class' experiment.<br />
I plan to do with deprecated modules exactly as I've just outlined with builtin modules.</p>

<p>Hold your collective breaths. Upon turning blue cease breathing/holding your breath/whatever.</p>

<p>(e) Retirement plans. Part of the reason for spelling all this out is that next month,<br />
June 2026, I will be 76 (much to my amazement & horror :-) & am hoping someone will<br />
one day take an interest in adopting these modules after I stop coding.</p>

<p>Further, I'm currently waiting for a time to be announced for an 'emergency' surgery due<br />
to bleeding in the bowel. It's almost certainly colonic polyps, a few of which I had removed<br />
9 years ago. So the beast is known, but still not pleasant. If I survive the op I will report<br />
back :-).<br />
</p>]]>
        
    </content>
</entry>



<entry>
    <title>This week in PSC (226) | 2026-05-25</title>
    <link rel="alternate" href="https://blogs.perl.org/users/psc/2026/05/this-week-in-psc-226-2026-05-25.html" />
    <id>tag:blogs.perl.org,2026:/users/psc//4112.12059</id>
    <published>2026-05-26T02:32:39Z</published>
    <updated>2026-05-26T02:34:45Z</updated>
    <author>
        <name>Perl Steering Council</name>
        
    </author>
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/psc/">
        <![CDATA[<p>Paul is away this week so only Leon and Aristotle were present. A number of the belated-for-this-cycle issues have been addressed this week and we are following up on the remaining ones. We do not yet have a firm commitment for the release manager for 5.43.11.</p>

<p>[<a href="https://www.nntp.perl.org/group/perl.perl5.porters/;msgid=ahTAT-HpgHz-xpV6@klangraum.plasmasturm.org">P5P posting of this summary</a>]</p>
]]>
        

    </content>
</entry>



<entry>
    <title>This week in PSC (225) | 2026-05-18</title>
    <link rel="alternate" href="https://blogs.perl.org/users/psc/2026/05/this-week-in-psc-225-2026-05-18.html" />
    <id>tag:blogs.perl.org,2026:/users/psc//4112.12057</id>
    <published>2026-05-19T12:44:07Z</published>
    <updated>2026-05-19T12:45:55Z</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 attended. We are dealing with a spate of (important, but thankfully relatively small) issues reported late in the cycle, so there will be a 5.43.11 dev release. We do not currently have a release manager for it yet and will ask for volunteers on the list.</p>

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

    </content>
</entry>



<entry>
    <title>Installing Bit::Vector on Debian 13 (Trixie)</title>
    <link rel="alternate" href="https://blogs.perl.org/users/dean/2026/05/installing-bitvector-on-debian-13-trixie.html" />
    <id>tag:blogs.perl.org,2026:/users/dean//558.12056</id>
    <published>2026-05-16T13:53:21Z</published>
    <updated>2026-05-16T14:11:40Z</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>Whilst <em>Bit::Vector</em> is available as a Debian package in <em>libbit-vector-perl</em>, when installing it using <em>cpanm</em> the compile failed for me.</p>

<p>The installation crashed during the <code>make</code> stage, throwing a specific compiler error regarding <code>false</code> and <code>true</code> tokens:</p>

<div style="background-color: #fff5f5; border-left: 4px solid #e53e3e; padding: 15px; margin: 20px 0; border-radius: 4px;">
    <div style="font-weight: bold; color: #c53030; margin-bottom: 8px;">Failure Output:</div>
    <pre style="background-color: #1a202c; color: #edf2f7; padding: 15px; border-radius: 6px; overflow-x: auto; font-family: 'Courier New', Courier, monospace; font-size: 13px; margin: 15px 0; white-space: pre-wrap; word-break: break-all;">Building Bit-Vector-7.4
...
cc -c   -D_REENTRANT -D_GNU_SOURCE -fwrapv -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_FORTIFY_SOURCE=2 -O2   -DVERSION=\"7.4\" -DXS_VERSION=\"7.4\" -fPIC "-I/home/dean/perl5/perlbrew/perls/perl-5.40.3/lib/5.40.3/x86_64-linux-thread-multi/CORE"   BitVector.c
In file included from BitVector.c:12:
ToolBox.h:98:20: error: cannot use keyword 'false' as enumeration constant
   98 |              enum { false, true };
      |                    ^~~~~
ToolBox.h:98:20: note: 'false' is a keyword with '-std=c23' onwards
make: *** [Makefile:343: BitVector.o] Error 1
-> FAIL Installing Bit::Vector failed.</pre>
</div>

<h3>Why this happens:</h3>
<p>The issue stems from modern Linux distributions upgrading their default compiler (like GCC 15+) to use the <strong>C23 (ISO C 2023)</strong> standard. In C23, <code>false</code> and <code>true</code> became official, reserved language keywords[cite: 2]. Because <em>Bit::Vector</em> is an older module, its internal <code>ToolBox.h</code> file explicitly tries to define them inside an <code>enum</code> block for legacy compatibility, which modern C23 compliance strictly forbids[cite: 2].</p>

<p>A solution that worked was:</p>
<blockquote style="background-color: #f0fff4; border-left: 4px solid #38a169; padding: 15px; margin: 20px 0; border-radius: 4px; font-family: 'Courier New', Courier, monospace;">cpanm --configure-args="OPTIMIZE='-O2 -std=gnu17'" Bit::Vector</blockquote>

<h3>What this does:</h3>
<ul style="list-style-type: none; padding-left: 0; margin-top: 15px;">
    <li style="margin-bottom: 12px; padding-left: 15px; border-left: 3px solid #3182ce;">
        <strong><code>--configure-args</code></strong><br />
        Passes arguments directly to the underlying <code>Makefile.PL</code> build script[cite: 2].
    </li>
    <li style="margin-bottom: 12px; padding-left: 15px; border-left: 3px solid #3182ce;">
        <strong><code>OPTIMIZE='-O2 -std=gnu17'</code></strong><br />
        Overrides the default compiler optimization settings[cite: 2]. It keeps standard <code>-O2</code> optimization performance but strictly forces the compiler to treat the codebase as <strong>GNU C17</strong> instead of C23[cite: 2]. This bypasses the keyword restrictions and allows the legacy <code>enum</code> declaration to succeed flawlessly[cite: 2].
    </li>
</ul>

<p style="margin-top: 25px; font-style: italic; color: #4a5568;">Hopefully this will help someone else running into the same roadblock!</p>]]>
        
    </content>
</entry>



<entry>
    <title>Is your account on blogs.perl.org registered with an @cpan.org email address?</title>
    <link rel="alternate" href="https://blogs.perl.org/users/meta/2026/05/at-cpan-dot-org-on-bpo.html" />
    <id>tag:blogs.perl.org,2026:/users/meta//1290.12053</id>
    <published>2026-05-14T12:43:34Z</published>
    <updated>2026-05-16T19:22:32Z</updated>
    <author>
        <name>Aristotle</name>
        <uri>http://plasmasturm.org/</uri>
    </author>
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/meta/">
        <![CDATA[<p>Then here is a reminder that <a href="https://log.perl.org/2026/04/cpanorg-email-forwarding-has-been-shut.html" title="The Perl NOC: cpan.org email forwarding has been shut down">in light of current events</a> you will want to update your account to use a different email address. (There are about 130 of you that get to jump through this hoop now.)</p>

<p>If you need help, email <a href="&#109;&#97;&#x69;&#108;&#x74;&#x6F;:&#x63;&#111;&#x6E;&#116;&#x61;&#99;&#x74;&#64;&#98;&#x6C;&#111;&#103;&#x73;.&#112;&#101;&#x72;&#108;&#46;&#111;&#114;&#x67;">&#x63;&#111;&#x6E;&#116;&#x61;&#99;&#x74;&#64;&#98;&#x6C;&#111;&#103;&#x73;.&#112;&#101;&#x72;&#108;&#46;&#111;&#114;&#x67;</a> as per usual.</p>
]]>
        

    </content>
</entry>



<entry>
    <title>My Journey with Devel::ptkdb - Origins</title>
    <link rel="alternate" href="https://blogs.perl.org/users/matthew_persico/2026/05/my-journey-with-develptkdb---origins.html" />
    <id>tag:blogs.perl.org,2026:/users/matthew_persico//2139.12051</id>
    <published>2026-05-14T03:23:03Z</published>
    <updated>2026-05-14T03:28:48Z</updated>
    <author>
        <name>Matthew Persico</name>
        
    </author>
    <category term="develptkdb" label="Devel::ptkdb" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="perldebugger" label="perl debugger" scheme="http://www.sixapart.com/ns/types#tag" />
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/matthew_persico/">
        <![CDATA[<p>This <a href="https://matthewpersico.github.io/2026/05/12/ptkdb-origins.html">post</a> is the first in a series that will follow my re-development of the Devel::ptkdb debugger. This post explains the beginnings of my involvement with the Perl Tk debugger.</p>]]>
        
    </content>
</entry>



<entry>
    <title> Introducing Time::Str</title>
    <link rel="alternate" href="https://blogs.perl.org/users/chansen/2026/05/introducing-timestr.html" />
    <id>tag:blogs.perl.org,2026:/users/chansen//165.12050</id>
    <published>2026-05-12T22:28:22Z</published>
    <updated>2026-05-12T23:17:21Z</updated>
    <author>
        <name>Christian Hansen</name>
        
    </author>
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/chansen/">
        

        <![CDATA[<p><a href="https://metacpan.org/pod/Time::Str">Time::Str</a> is a Perl module for parsing and formatting date/time strings across 20+ standard formats. It has an optional C/XS backend, nanosecond precision, and rejects input it cannot parse unambiguously rather than guessing.</p>

<pre><code>use Time::Str qw(str2time str2date time2str);

my $time = str2time('2024-12-24T15:30:45Z');
# 1735052445

my $str = time2str($time, format =&gt; 'RFC2822', offset =&gt; 60);
# 'Tue, 24 Dec 2024 16:30:45 +0100'
</code></pre>

<h2>Standards Compliance</h2>

<p>Each format is implemented according to its specification: RFC 3339, RFC 2822, RFC 2616, ISO 8601 (calendar dates), ISO 9075, ITU-T X.680 (ASN.1), RFC 5545.</p>

<pre><code>RFC3339      2024-12-24T15:30:45+01:00
RFC2822      Tue, 24 Dec 2024 15:30:45 +0100
RFC2616      Tue, 24 Dec 2024 15:30:45 GMT
ISO8601      20241224T153045.500+0100
ISO9075      2024-12-24 15:30:45 +01:00
ASN1GT       20241224153045Z
CLF          24/Dec/2024:15:30:45 +0100
RFC5545      20241224T153045Z
ECMAScript   Tue Dec 24 2024 15:30:45 GMT+0100
</code></pre>

<p>RFC 2822 allows an optional day name and a comment; the regex accepts both. ISO 8601 calendar dates are supported in both basic and extended formats with optional fractional parts on the least significant component. RFC 2616 requires three date formats (IMF-fixdate, RFC 850, and asctime); all three are accepted.</p>

<p>Optional fields are optional. Constrained fields are validated. Day names, when present, are verified against the actual date.</p>

<h2>No Guessing</h2>

<p>If Time::Str cannot parse the input unambiguously, it croaks rather than returning a wrong answer.</p>

<h3>The <code>01/02/2024</code> Problem</h3>

<p>Is <code>01/02/2024</code> January 2nd or February 1st? It depends on who you ask:</p>

<ul>
<li><strong>Date::Parse</strong> assumes American (MM/DD). Documents this as a <em>known bug</em> with no workaround.</li>
<li><strong>Date::Parse::Modern</strong> assumes European (DD/MM). Swaps day and month if the month exceeds 12.</li>
<li><strong>Time::ParseDate</strong> assumes American by default. A <code>UK</code> option switches to European. Applies heuristics when values exceed 12.</li>
</ul>

<p>Time::Str requires numeric dates in year-month-day order. Ambiguous formats are rejected. When the month is written as a name or Roman numeral, the order is flexible because the month is unambiguous:</p>

<pre><code>str2date('2024-12-24',     format =&gt; 'DateTime');  # Y-M-D
str2date('24 Dec 2024',    format =&gt; 'DateTime');  # named month
str2date('Dec 24th, 2024', format =&gt; 'DateTime');  # named month
str2date('24.XII.2024',    format =&gt; 'DateTime');  # Roman numeral

str2date('12-24-2024',     format =&gt; 'DateTime');  # M-D-Y rejected
str2date('24-12-2024',     format =&gt; 'DateTime');  # D-M-Y rejected
</code></pre>

<h3>Two-Digit Years</h3>

<p><code>01/02/03</code>: is that 2003, 1903, or 2001? Existing modules apply different heuristics, and some produce results that depend on <em>today's date</em>. Time::Str's <code>DateTime</code> parser requires a four-digit year. Formats that inherently use two-digit years (like ASN.1 UTCTime) provide a configurable <code>pivot_year</code> parameter with a documented default.</p>

<h3>Timezone Abbreviations</h3>

<p><code>IST</code> could be India Standard Time (+05:30), Israel Standard Time (+02:00), or Irish Standard Time (+01:00).</p>

<p>Time::Str captures abbreviations in <code>tz_abbrev</code> without resolving them. <code>str2time</code> requires a UTC designator or numeric offset to produce a timestamp:</p>

<pre><code>my %d = str2date('24 Dec 2024 15:30:45 IST', format =&gt; 'RFC2822');
# tz_abbrev =&gt; 'IST' -- you decide what it means

str2time('24 Dec 2024 15:30:45 IST', format =&gt; 'RFC2822');
# croaks: "Unable to convert: timestamp string without a UTC 
#          designator or numeric offset"
</code></pre>

<h2>The <code>DateTime</code> Format</h2>

<p>The <code>DateTime</code> format is a permissive parser for real-world dates that does not use heuristics. It accepts 12-hour clocks, ordinal suffixes, day names, Roman numeral months, and RFC 9557 timezone annotations:</p>

<pre><code>str2date('Monday, 24th December 2024 at 3:30 PM UTC+01:00',
       format =&gt; 'DateTime');
# (year      =&gt; 2024, 
#  month     =&gt; 12, 
#  day       =&gt; 24, 
#  hour      =&gt; 15,   
#  minute    =&gt; 30,
#  tz_utc    =&gt; 'UTC', 
#  tz_offset =&gt; 60)

str2date('2024-12-24T15:30:45+01:00[Europe/Stockholm]',
       format =&gt; 'DateTime');
# ..., tz_offset =&gt; 60, tz_annotation =&gt; '[Europe/Stockholm]'
</code></pre>

<p>Every accepted date can be parsed deterministically. Ordinal suffixes must match the day number (<code>24th</code> is valid, <code>24st</code> is rejected). Day names must match the actual date.</p>

<h2>Optional C/XS</h2>

<p>Time::Str has two backends. The XS backend (C99) is loaded when a compiler is available; otherwise it falls back to Pure Perl. The <code>TIME_STR_PP</code> environment variable forces the Pure Perl path.</p>

<pre><code>say Time::Str::IMPLEMENTATION;  # "XS" or "PP"
</code></pre>

<p>Both backends share the same precompiled regexps from <code>Time::Str::Regexp</code> and produce identical results. The C backend implements token extraction and the <code>time2str</code> formatting engine natively.</p>

<h3>Benchmarks</h3>

<h4><code>str2time</code>: Parsing Performance</h4>

<p>Parsing <code>'2012-12-24T12:30:45.123456789+01:00'</code>:</p>

<pre><code>                    Rate DT8601 DT3339 D::Parse T::Str T::Moment
DT::F::ISO8601   20935/s     --   -42%     -81%   -98%     -100%
DT::F::RFC3339   36127/s    73%     --     -68%   -97%      -99%
D::Parse        112320/s   437%   211%       --   -90%      -98%
T::Str         1093307/s  5122%  2926%     873%     --      -80%
Time::Moment   5543754/s 26381% 15245%    4836%   407%        --
</code></pre>

<p>With XS, <code>str2time</code> is ~10x faster than <code>Date::Parse</code> and ~30x faster than <code>DateTime::Format::RFC3339</code>. My other module <a href="https://metacpan.org/pod/Time::Moment">Time::Moment</a> is faster, but it's a purpose-built C library for a single format. Time::Str handles 20+ formats at over 1M parses/s.</p>

<p>Across Time::Str's own formats, named-format parsers (RFC3339, RFC4287, W3C) run at ~1M/s, while the permissive <code>DateTime</code> parser (which handles the full grammar) runs at ~500K/s:</p>

<pre><code>             Rate  DateTime   W3C RFC3339 RFC4287
DateTime  503643/s       --  -50%    -52%    -52%
W3C      1005176/s     100%   --     -5%     -5%
RFC3339  1054837/s     109%    5%      --     -0%
RFC4287  1060112/s     110%    5%      1%      --
</code></pre>

<h4><code>time2str</code>: Formatting Performance</h4>

<p><code>time2str</code> is implemented in C with its own format-spec interpreter, independent of <code>strftime</code> and locale. RFC 3339 formatting runs at ~2x the speed of <code>scalar gmtime</code>:</p>

<pre><code>                 Rate T::Moment    gmtime   RFC2822   RFC3339
T::Moment   2161089/s        --      -29%      -61%      -68%
gmtime      3060278/s       42%        --      -44%      -54%
RFC2822     5477189/s      153%       79%        --      -18%
RFC3339     6719250/s      211%      120%       23%        --
</code></pre>

<h2>Light on Dependencies</h2>

<p>Requires Perl 5.10.1 or later. Runtime dependencies are <code>Carp</code> and <code>Exporter</code>, both core modules. The XS backend needs a C99 compiler at build time. Without a compiler, the Pure Perl fallback has zero non-core dependencies.</p>

<h2>Time::HiRes</h2>

<p><code>str2time</code> returns a floating-point Unix timestamp, the same representation <code>Time::HiRes::time()</code> uses. Fractional seconds are preserved up to nanosecond resolution:</p>

<pre><code>use Time::HiRes qw(time);
use Time::Str   qw(str2time time2str);

# Round-trip a high-resolution timestamp
my $now  = time;             # e.g., 1735052445.123456
my $str  = time2str($now);   # '2024-12-24T15:30:45.123456Z'
my $back = str2time($str);   # 1735052445.123456

# Full nanosecond control with the nanosecond parameter
my ($sec, $us) = Time::HiRes::gettimeofday();
my $str = time2str($sec, nanosecond =&gt; $us * 1000, precision =&gt; 6);
</code></pre>

<p>Parsing truncates; formatting rounds. The <code>nanosecond</code> parameter bypasses rounding for exact output.</p>

<h2>DateTime and Time::Moment</h2>

<p><code>str2date</code> returns parsed components that maps directly to <a href="https://metacpan.org/pod/Time::Moment">Time::Moment</a> and <a href="https://metacpan.org/pod/DateTime">DateTime</a> constructors:</p>

<pre><code>use Time::Str qw(str2date);
use DateTime;
use Time::Moment;

my %d = str2date('2024-12-24T15:30:45.500+01:00');

# Feed directly into Time::Moment
my $tm = Time::Moment-&gt;new(
  year       =&gt; $d{year},
  month      =&gt; $d{month},
  day        =&gt; $d{day},
  hour       =&gt; $d{hour},
  minute     =&gt; $d{minute},
  second     =&gt; $d{second},
  nanosecond =&gt; $d{nanosecond},
  offset     =&gt; $d{tz_offset},
);

# Feed directly into DateTime
my $dt = DateTime-&gt;new(
  year       =&gt; $d{year},
  month      =&gt; $d{month},
  day        =&gt; $d{day},
  hour       =&gt; $d{hour},
  minute     =&gt; $d{minute},
  second     =&gt; $d{second},
  nanosecond =&gt; $d{nanosecond},
  time_zone  =&gt;
    DateTime::TimeZone-&gt;offset_as_string($d{tz_offset} * 60)
);
</code></pre>

<p>This decouples parsing from representation. Time::Moment's constructor maps 1:1 with <code>str2date</code>'s output: <code>offset</code> takes minutes and <code>nanosecond</code> takes the same integer range.</p>

<h2>Tools for Custom Parsers</h2>

<p>Time::Str also exposes its building blocks for writing custom parsers.</p>

<h3><a href="https://metacpan.org/pod/Time::Str::Regexp">Time::Str::Regexp</a></h3>

<p>Each format's precompiled regex is individually exportable, with named captures:</p>

<pre><code>use Time::Str::Regexp qw($RFC3339_Rx $RFC2822_Rx $DateTime_Rx);

if ($line =~ $RFC3339_Rx) {
  my $year   = $+{year};
  my $month  = $+{month};
  my $offset = $+{tz_offset};
  # ...
}
</code></pre>

<p>All regexes are anchored and use consistent named captures: <code>year</code>, <code>month</code>, <code>day</code>, <code>hour</code>, <code>minute</code>, <code>second</code>, <code>fraction</code>, <code>tz_offset</code>, <code>tz_utc</code>, <code>tz_abbrev</code>, <code>tz_annotation</code>, <code>day_name</code>, <code>meridiem</code>.</p>

<h3><a href="https://metacpan.org/pod/Time::Str::Token">Time::Str::Token</a></h3>

<p>Token parsers convert the raw captured strings into semantic values:</p>

<pre><code>use Time::Str::Token qw(parse_month parse_day parse_tz_offset);

parse_month('Dec');        # 12
parse_month('XII');        # 12
parse_month('December');   # 12

parse_day('24th');         # 24
parse_day('1st');          # 1

parse_tz_offset('+05:30'); # 330 (minutes)
parse_tz_offset('0530');   # 330
</code></pre>

<h3><a href="https://metacpan.org/pod/Time::Str::Calendar">Time::Str::Calendar</a></h3>

<p>Calendar utilities for validation and conversion:</p>

<pre><code>use Time::Str::Calendar qw( leap_year 
                            valid_ymd
                            ymd_to_dow );

leap_year(2024);            # true
valid_ymd(2024, 2, 29);     # true
valid_ymd(2023, 2, 29);     # false
ymd_to_dow(2024, 12, 24);   # 2 (Tuesday; 1=Mon, 7=Sun)
</code></pre>

<p>These three modules can be combined to build parsers for other date formats, using the same validation and calendar logic as the built-in formats.</p>

<h2>What's Next: Native C Parsers</h2>

<p>Currently, the XS backend uses Perl's regex engine for the initial string match, then switches to C for token extraction, validation, and epoch calculation. The next step is moving the parsers themselves into native C, eliminating the regex overhead entirely and closing the gap with Time::Moment's parsing speed.</p>

<hr />

<p>Time::Str is available on <a href="https://metacpan.org/pod/Time::Str">CPAN</a> and <a href="https://github.com/chansen/p5-time-str">GitHub</a>.</p>

<p><code>
cpanm Time::Str
</code></p>
]]>
    </content>
</entry>



<entry>
    <title>This week in PSC (224) | 2026-05-11</title>
    <link rel="alternate" href="https://blogs.perl.org/users/psc/2026/05/this-week-in-psc-224-2026-05-11.html" />
    <id>tag:blogs.perl.org,2026:/users/psc//4112.12049</id>
    <published>2026-05-12T09:27:15Z</published>
    <updated>2026-05-12T09:29:02Z</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 attended further final release preparation. Issue triage continued; a number of small issues came in late, and we merged some of them. We also <a href="https://github.com/Perl/perl5/pull/24410" title="#24410 Revert \&quot;SelfLoader: don't mix buffered and unbuffered I/O\&quot;">reverted</a> a late <a href="https://github.com/Perl/perl5/pull/24404" title="#24404 SelfLoader: don't mix buffered and unbuffered I/O">small fix</a> that we had merged recently which turned out to <a href="https://github.com/Perl/perl5/issues/24407" title="#24407 7a6d188450d generates new test failures on FreeBSD and Linux">cause problems</a>. Overall we once again left the meeting without open release blockers.</p>

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

    </content>
</entry>



<entry>
    <title>Beautiful Perl feature : low-precedence boolean operators &apos;and&apos;, &apos;or&apos;</title>
    <link rel="alternate" href="https://blogs.perl.org/users/dami/2026/05/beautiful-perl-feature-low-precedence-boolean-operators-and-or.html" />
    <id>tag:blogs.perl.org,2026:/users/dami//4113.12048</id>
    <published>2026-05-11T01:35:42Z</published>
    <updated>2026-05-11T01:42:02Z</updated>
    <author>
        <name>dami</name>
        <uri>https://metacpan.org/author/DAMI</uri>
    </author>
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/dami/">
        <![CDATA[<p>After a pause in April, here is a new article in the <a href="https://dev.to/damil/series/34753">Beautiful Perl features</a> series on dev.to: <a href="https://dev.to/damil/beautiful-perl-feature-low-precedence-boolean-operators-and-or-3900">low-precedence boolean operators 'and', 'or'</a></p>]]>
        
    </content>
</entry>



<entry>
    <title>The Perl Toolchain Summit 2026</title>
    <link rel="alternate" href="https://blogs.perl.org/users/paul_johnson/2026/05/the-perl-toolchain-summit-2026.html" />
    <id>tag:blogs.perl.org,2026:/users/paul_johnson//528.12047</id>
    <published>2026-05-10T01:36:11Z</published>
    <updated>2026-05-10T18:52:43Z</updated>
    <author>
        <name>Paul Johnson</name>
        <uri>http://www.pjcj.net</uri>
    </author>
    <category term="pts" label="PTS" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="pts2026" label="PTS 2026" scheme="http://www.sixapart.com/ns/types#tag" />
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/paul_johnson/">
        <![CDATA[<p>I was once again privileged to be able to attend this year's Perl Toolchain Summit.  This is the 13th year (in a row if you discount the Covid years) that I have been able to attend and it is the technical highlight of my year.</p>

<p>This year the event was held in Vienna and, for the first time, my wife accompanied me.  We took a direct train from Zürich to Vienna and had a wonderful trip through the glorious Swiss and Austrian countrysides.</p>

<p>We arrived fairly late on Wednesday evening so didn't meet up with anyone then, but we saw a few of the other attendees at breakfast the next morning, and then I set off for the venue where I met up with everyone else, heard BooK's opening speech, took part in the introductions and then split off into a room with the MetaCPAN group with whom I spent about half of my time.  Meanwhile, my wife set off to explore Vienna.</p>

<p>I came with a set of work needing to be completed and things I wanted to discuss with people there.  The discussions were the most important part and are the raison d'être of the event, and over the four days I had plenty of useful discussions, both planned and non-planned.  The first one started whilst walking to the venue on the first day with Paul Evans.  We discussed the possibility of separating the behaviour of <code>$^P</code> and <code>PL_perldb</code> - perhaps by using another bit.  Devel::Cover uses <code>$^P</code> but doesn't want the behaviour of <code>PL_perldb</code>.</p>

<p>I also discussed further integrating the cpancover infrastructure with that of MetaCPAN.  Getting cpancover to run entirely within containers was one of my goals from last year's summit.  It wasn't finished during the summit though the problems were solved and all that remained was finishing the implementation, which happened during the year.  So now the discussion was more about how to use that.  I had a good chat with Leo on that topic and we also sorted out the cpancover backups to MetaCPAN.</p>

<p>I didn't just speak to the English folk though.  I spent a fair amount of time talking with Salve on topics primarily related to CPAN security and adjacent subjects.  And I chatted with Merijn about life, the universe and everything.  He also tested Devel::Cover 2.00 for me and gave me good feedback.  We held our second annual CLI throwdown.  Many of us live in the terminal so we have a (now somewhat formalised) tradition of sharing our favourite tools.</p>

<p>I had wondered whether I might be in a position to release Devel::Cover 2.00 during the summit but, whilst I was able to do a fair amount of work on it, I realised that there were more things to look at.  Hopefully it won't be too much longer before I can put out a test release.  This will be the first major (breaking) release since Devel::Cover became stable and it should bring speed improvements, a new criterion and a new report style.</p>

<p>There were plenty of talks and group discussions which I enjoyed, and the four days seemed to race by.  It's an intense time which genuinely takes its toll.  You can tell that by Sunday many folk are feeling the effects as things start to wind down.  Nevertheless it's also extremely satisfying and productive.  As usual I left with far more to do than when I arrived - plenty to keep me going until next year.</p>

<p>Beyond work and technical discussions it was lovely to meet up with old friends, to travel around the beautiful city of Vienna and sample some of its lovely cafés and restaurants.</p>

<p>The venue was excellent, die Hauswirtschaft.  Our part was small enough that we weren't too far away from each other yet large enough that we weren't on top of each other and with enough space to hold group discussions without disturbing those not involved.</p>

<p>Huge thanks to the organisers.  From my point of view, everything ran without a hitch though I'm sure an awful lot of effort went into making it seem that way.  And thanks also to our generous sponsors without whom the event could never have happened:</p>

<ul>
<li><a href="https://www.perlfoundation.org/">The Perl and Raku Foundation</a></li>
<li><a href="https://www.grantstreet.com/">Grant Street Group</a></li>
<li><a href="https://geizhals.de/">Geizhals Preisvergleich</a></li>
<li><a href="https://vienna.pm.org/">Vienna.pm</a></li>
<li><a href="https://www.suse.com/">SUSE</a></li>
<li>Trans-Formed Media LLC</li>
<li><a href="https://www.ctrlo.com/">Ctrl O</a></li>
<li><a href="https://www.simplelists.com/">Simplelists</a></li>
<li><a href="https://koha-support.eu/">HKS3 / Koha Support</a></li>
<li>Harald Joerg</li>
<li>Michele Beltrame (<a href="https://www.blendgroup.it/">Sigmafin</a>)</li>
<li>Laurent Boivin</li>
</ul>

<p><a href="https://blogs.perl.org/users/paul_johnson/assets_c/2026/05/pts-2026-5332.html" onclick="window.open('https://blogs.perl.org/users/paul_johnson/assets_c/2026/05/pts-2026-5332.html','popup','width=1920,height=1080,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"><img src="https://blogs.perl.org/users/paul_johnson/assets_c/2026/05/pts-2026-thumb-800x450-5332.jpg" width="800" height="450" alt="Group photo, Perl Toolchain Summit 2026, Vienna" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto 20px;" /></a></p>
]]>
        

    </content>
</entry>



<entry>
    <title>PTS 2026</title>
    <link rel="alternate" href="https://blogs.perl.org/users/leo_lapworth/2026/05/pts-2026.html" />
    <id>tag:blogs.perl.org,2026:/users/leo_lapworth//12.12045</id>
    <published>2026-05-08T14:28:58Z</published>
    <updated>2026-05-08T14:51:18Z</updated>
    <author>
        <name>Ranguard</name>
        <uri>http://leo.cuckoo.org/</uri>
    </author>
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/leo_lapworth/">
        <![CDATA[<p>  A quick summary of what I got up to at PTS 2026 in Vienna.</p>

<p><strong>Test::Smoke's</strong> long-term future. I had several useful discussions with H. Merijn Brand (Tux) and Todd Rinaldo  (toddr) about keeping Test::Smoke maintainable for the long term. This tied directly into the MetaCPAN hosting migration below: DigitalOcean offers managed Postgres, Hetzner doesn't, and Test::Smoke's existing database usage wasn't especially efficient. The outcome was toddr starting a rewrite that runs as a single container backed by SQLite and local files -- much more portable and easier to operate.</p>

<p><strong>Migrating MetaCPAN from DigitalOcean to Hetzner</strong>. I spent a big chunk of the summit pairing with Shawn  Sorichetti (hide) on the migration, including reorganising our Kubernetes setup so it deals more cleanly with multiple environments. Shawn was making a large number of changes; I focused on reviewing them quickly so we could iterate fast.</p>

<p><strong>Devel::Cover hosting</strong>. I spent time with Paul Johnson (pjcj) talking through Devel::Cover's hosting and configuration, and helped sketch a container design that should be more efficient and scalable for what he needs next.</p>

<p><strong>MetaCPAN security</strong>. As part of the MetaCPAN team I joined several discussions -- including with the CPAN security team -- about parts of the system that recent security reports have flagged for improvement.</p>

<p><strong>Side work</strong>. Outside the main Perl tracks, I contributed to the <a href="https://nono.sh/">Nono Sandbox </a> tool and dropped into several conversations and talks about AI in the context of Perl modules and publishing.</p>

<p>I really want to thank the <a href="https://perltoolchainsummit.org/pts2026/sponsors.html">Sponsors</a> projects move so much faster when we can get people together and focused. Something that just doesn't happen any other way</p>

<p>
  <a href="https://www.perlfoundation.org/">The Perl and Raku Foundation</a>,
  <a href="https://www.grantstreet.com/">Grant Street Group</a>,
  <a href="https://geizhals.de/">Geizhals Preisvergleich</a>,
  <a href="https://vienna.pm.org/">Vienna.pm</a>,
  <a href="https://www.suse.com/">SUSE</a>,
  Trans-Formed Media LLC,
  <a href="https://www.ctrlo.com/">Ctrl O</a>,
  <a href="https://www.simplelists.com/">Simplelists</a>,
  Harald Joerg,
  Michele Beltrame (<a href="https://www.blendgroup.it/">Sigmafin</a>),
  Laurent Boivin.
</p>

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



<entry>
    <title>Signing CPAN Releases with SigStore</title>
    <link rel="alternate" href="https://blogs.perl.org/users/timothy_legge/2026/05/signing-cpan-releases-with-sigstore.html" />
    <id>tag:blogs.perl.org,2026:/users/timothy_legge//3716.12044</id>
    <published>2026-05-07T05:50:00Z</published>
    <updated>2026-05-07T06:33:33Z</updated>
    <author>
        <name>Timothy Legge</name>
        
    </author>
    <category term="pts" label="PTS" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="signatures" label="Signatures" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="sigstore" label="SigStore" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="supplychain" label="Supply-Chain" scheme="http://www.sixapart.com/ns/types#tag" />
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/timothy_legge/">
        <![CDATA[<p><strong><big>Signing CPAN Releases with SigStore</big></strong></p>

<p>At the most recent Perl Tool Chain Summit (PTS) in Vienna we decided to deprecate Module::Signature.  Module::Signature has been around for a long time but it has become increasingly clear that it does not provide the security assurances that it was designed to deliver.</p>

<p><a href="https://metacpan.org/dist/Dist-Zilla-Plugin-SigStore-SignRelease">Dist::Zilla::Plugin::SigStore::SignRelease</a> is a new plugin that signs your CPAN release with SigStore before uploading. SigStore uses short-lived, OIDC-issued certificates.  You authenticate with Google, GitHub, or Microsoft, and <strong><em>cosign</em></strong> produces a signature bundle. No long-lived keys, no keyserver dance.</p>

<p><strong><big>How it works</big></strong></p>

<p>The plugin extends the Dist::Zilla plugin UploadToCPAN. During the <strong><em>dzil</em></strong> release, it:</p>

<ol>
	<li>Calls <em>cosign sign-blob</em> on your release archive, producing a <em>.sigstore.json</em> bundle file.</li>
	<li>Pulls the certificate out of the bundle and verifies the signature locally before anything leaves your machine.</li>
	<li>Uploads both the tarball and the bundle to PAUSE.</li>
</ol>

<p>SigStore also add the release and signature information to the <strong><em><a href="https://search.sigstore.dev/">Rekor Transparency Log</a></em></strong>.  Rekor is SigStore's public append-only transparency log, and the inclusion proof is bundled into the <strong><em>.sigstore.json</em></strong>.  Anyone verifying your release can confirm the signing event was publicly logged at the time it happened.</p>

<p><strong><big>Using it</big></strong></p>

<p>Remove the existing uploader from your Dist::Zilla configuration file (dist.ini) and add the plugin to your dist.ini:</p>

<p>[@Filter]<br />
bundle = @Basic<br />
remove = UploadToCPAN    ; Do this if you use @Basic</p>

<p>[SigStore::SignRelease]</p>

<p><strong><big>Non-CPAN Requirements</big></strong></p>

<p>You'll need <strong><u><a href="https://github.com/sigstore/cosign/releases">cosign</a></u></strong> in your PATH.</p>

<p><strong><big>Next Steps</big></strong></p>

<p>PAUSE doesn't verify SigStore signatures yet, and no CPAN client checks them on install. </p>

<p>This is opt-in transparency: the bundle ships alongside the tarball, and anyone who cares can verify it themselves.</p>

<p>Hopefully more support for SigStore will be developed soon.  Until then signing your releases will allow them to be verified:</p>

<p>cosign verify-blob Your-Dist-0.01.tar.gz \<br />
    --bundle Your-Dist-0.01.tar.gz.sigstore.json \<br />
    --certificate-identity you@example.com \<br />
    --certificate-oidc-issuer https://accounts.google.com</p>

<p>It's a first step, towards verifiable releases in the Perl ecosystem. Try it on your next release and let me know how it goes.<br />
</p>]]>
        
    </content>
</entry>



<entry>
    <title>Perl Toolchain Summit 2026 - Vienna</title>
    <link rel="alternate" href="https://blogs.perl.org/users/timothy_legge/2026/05/perl-toolchain-summit-2026---vienna.html" />
    <id>tag:blogs.perl.org,2026:/users/timothy_legge//3716.12043</id>
    <published>2026-05-06T04:56:36Z</published>
    <updated>2026-05-06T05:32:00Z</updated>
    <author>
        <name>Timothy Legge</name>
        
    </author>
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/timothy_legge/">
        <![CDATA[<p>This year, I was once again honored to be invited to the Perl Toolchain Summit (PTS), held in Vienna. Following productive years in Lisbon and Leipzig, the CPAN Security Group (CPANSec) spent time discussing how to improve the security of the Perl and CPAN ecosystem.</p>

<p>As always, the magic of PTS lies in the hallway discussions and focused groups where we can work on complex problems that are nearly impossible to coordinate over email or GitHub alone.</p>

<p><strong>CPANSec: Maturing our CNA Role</strong></p>

<p>Since we established CPANSec as a CVE Numbering Authority (CNA) in 2025, our focus has shifted toward efficiency and sustainability. We have a small group working on finding and documenting vulnerabilities.  We spent time in Vienna discussing:</p>

<ol>
	<li>Reducing Time to CVE: We discussed how we can reduce the overhead required to issue CVEs without adversely affecting quality.</li>
<li>Workflow Optimization: We worked on improving the disclosure process to ensure that security reports move from initial contact to a patched release as smoothly as possible.</li>
<li>The Maintainer Human Element: A major takeaway this year was recognizing the impact security reports have on maintainers. We want to ensure that being "tapped" for a security fix is a supportive experience rather than a stressful burden on our volunteer community.</li>
</ol>

<p><strong>Crypt::OpenSSL::RSA: Restoring Functionality</strong></p>

<p>Following the work Todd Rinaldo (toddr) and I did last year in Leipzig where we disabled PKCS1 padding to mitigate the Marvin attack, we revisited Crypt::OpenSSL::RSA.</p>

<p>Working together in Vienna, we were able to release a version that restores PKCS1 v1.5 padding specifically for signatures. This padding is no longer allowed for encryption but it remains vital for signature verification an it was great to finally close this loop and fix this issue.</p>

<p><strong>Deep Dives and the "Bus Factor"</strong></p>

<p>One of the highlights of PTS for me was when H. Merijn Brand (Tux) sat down with a small group of us to present metaconfig and Configure.</p>

<p>While the configuration process for Perl isn't something that changes frequently, it is complex. Merijn gave us a great overview of its importance and inner workings. Our hope is to improve the "bus factor" for this critical piece of the Perl build process, ensuring that more people in the community are equipped to maintain the core configuration logic across various platforms.</p>

<p>I also had the chance to sit in on several other great discussions:</p>

<ol>
	<li>UTF-8: Karl Williamson shared deep technical insights into UTF-8 handling.</li>
	<li>Perl & AI: We discussed the emerging role of AI within the Perl community, both as a tool for development and the challenges it poses.</li>
	<li>Platform Support: Ensuring Perl continues to run flawlessly across the diverse landscape of modern (and ancient) operating systems.</li>
         <li>Paul Evan's update on features and changes coming to Perl.</li>
</ol>

<p>As in past years, Paul provided both an entertaining and very informative description of things that could be coming to Perl in future releases.  </p>

<p>Perl has not been standing still, it continues to grow and evolve, all without breaking  backward compatibility.</p>

<p><strong>Retiring the Old: The Deprecation of Module::Signature</strong></p>

<p>Security is as much about moving forward as it is about patching the past. During PTS, we decided it was time to deprecate Module::Signature.</p>

<p>After reaching out to Audrey Tang, the original author, who approved the deprecation, we concluded that the module does not provide the expected security assurances required for a modern supply chain. It is time to retire it and shift our focus toward more robust, modern solutions for module integrity.</p>

<p><strong>Organizers and Sponsors</strong></p>

<p>A massive thank you to the organizers for another flawlessly executed PTS. The hospitality of Vienna and the focus provided by the venue allowed us to get an incredible amount of work done.</p>

<p>Finally, PTS simply wouldn't happen without the generous support of our sponsors. Their commitment to the "invisible" work of PTS is what makes it possible.  It helps keep the Perl and CPAN ecosystem healthy, secure, and moving forward for everyone.</p>

<p><a href="https://www.perlfoundation.org/">The Perl and Raku Foundation</a>,<br />
<a href="https://www.grantstreet.com/">Grant Street Group</a>,<br />
<a href="https://geizhals.de/">Geizhals Preisvergleich</a>,<br />
<a href="https://vienna.pm.org/">Vienna.pm</a>,<br />
<a href="https://www.suse.com/">SUSE</a>,<br />
Trans-Formed Media LLC,<br />
<a href="https://www.ctrlo.com/">Ctrl O</a>,<br />
<a href="https://www.simplelists.com/">Simplelists</a>,<br />
Harald Joerg,<br />
Michele Beltrame <a href="https://www.blendgroup.it/">Sigmafin</a>,<br />
Laurent Boivin</p>]]>
        
    </content>
</entry>



<entry>
    <title>This week in PSC (223) | 2026-05-04</title>
    <link rel="alternate" href="https://blogs.perl.org/users/psc/2026/05/this-week-in-psc-223-2026-05-04.html" />
    <id>tag:blogs.perl.org,2026:/users/psc//4112.12042</id>
    <published>2026-05-06T02:42:56Z</published>
    <updated>2026-05-06T02:45:14Z</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 attended for some more issue triage. We accepted some assorted small fixes and CPAN dual-life module updates. The release blocker list remains empty.</p>

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

    </content>
</entry>



<entry>
    <title>GTC 2.1 go pro</title>
    <link rel="alternate" href="https://blogs.perl.org/users/lichtkind/2026/05/gtc-21-go-pro.html" />
    <id>tag:blogs.perl.org,2026:/users/lichtkind//275.12018</id>
    <published>2026-05-05T15:11:48Z</published>
    <updated>2026-05-05T23:55:46Z</updated>
    <author>
        <name>lichtkind</name>
        <uri>http://www.lichtkind.de/</uri>
    </author>
    <category term="Graphics::Toolkit::Color" scheme="http://www.sixapart.com/ns/types#category" />
    <category term="Perl 5" scheme="http://www.sixapart.com/ns/types#category" />
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/lichtkind/">
        <![CDATA[<p><a href="https://blogs.perl.org/users/lichtkind/2026/03/graphicstoolkitcolor-20-feater-overview.html">Hai again</a>, after a very productive three weeks I can announce the next major release of <a href="https://metacpan.org/pod/Graphics::Toolkit::Color">Graphics::Toolkit::Color</a> (despite the rather small version number jump). In this post I explain what changed(+12 spaces, +1 method, +7 method args), why it is relevant and how I used LLMs to achieve that.</p>
]]>
        <![CDATA[<p><strong>A lot more space to explore !</strong></p>

<p>The 12 new color spaces are: <em>LinearRGB</em>, <em>CIERGB</em>, <em>Adobe 98 RGB</em>, <em>Apple RGB</em>, <em>Pro Photo RGB</em>, <em>Wide Gamut RGB</em> (Adobe),<em> Rec.709</em>, <em>Rec.2020</em>, <em>Display P3</em>, <em>Display P3 Linear</em>, <em>DCI P3</em> and <em>DCI P3 Linear</em>. Most of them are or were relevant in (professional) photography, videography or for designers (This is what I meant by go pro). But almost none of these spaces have any CPAN support (until now). With the notable exception of <em>PDL::Transform::Color</em> which does know 7 of them. This makes GTC a "modern" color handling library, especially with GTC 2.3, when another such batch is supposed to land. And if you want to know more about all these spaces, just read our <a href="https://metacpan.org/pod/Graphics::Toolkit::Color::Space::Hub#COLOR-SPACES">fine docs</a>. </p>

<p><strong>Are you good ?</strong></p>

<p>The new spaces are more or less a variant of good old standard RGB. They have just slightly different white point or gamma value or transfer function. And many of them have a much wider gamut than SRGB. Gamut is just a fancy word meaning range - meaning there are colors you can normally describe in e.g. <em>AdobeRGB</em>, that would not be included (out of range) in standard RGB. When converting, GTC clamps just the values that stick out on an per axis basis. Because this is not ideal, it is planned for 2.4 to have professional gamut mapping (to the visually nearest color). All I can offer for now is the method: <strong>is_in_gamut</strong>. It allows you to know, if a color will be clamped just by the fact of being read or written by GTC. I also added the argument <strong>raw</strong>, which prevents such clamping (at your own risk of getting strange values or misbehaving operations). <em>is_in_gamut</em> is also available as standalone sub if you want to check values before creating an object.</p>

<p><strong>Just trust me !</strong></p>

<p>Another issue with these new spaces is that their values are in most cases normalized (0..1) but sometimes come as 16 or even 32 bit integer. Of course you should expect that GTC can read them all. For that scenario I extended the constructor with the option to have named arguments too:</p>

<pre><code>my $color = Graphics::Toolkit::Color-&gt;new( color =&gt; ['ProPhotoRGB' =&gt; 523, 1000, 27 ], range =&gt; 2**16, raw =&gt; 1);
</code></pre>

<p>Output such values works the same way:</p>

<pre><code> $color-&gt;values( in =&gt; 'WideGamutRGB', range =&gt; 2**32, as =&gt; 'named_string', raw =&gt; 1);
</code></pre>

<p><strong>Space Names</strong></p>

<p>Another topic brought up by the new spaces is color space names. Take for instance linear Display P3. You might want to write <em>Display-P3 Linear</em> or<em> linear_display_p3</em>. Now you have such flexibility with all space names. It was always case insensitive but now you can insert or remove the 4 chars: ' ', '_', '-', '.'; Same is true for the equally valid alias name of a color space. That would be <em>'BT.2020'</em> for the <em>'Rec.2020'</em> or <em>XYZ</em> for the <em>CIEXYZ</em> space.</p>

<p><strong>Adapt to the current Sun !</strong></p>

<p>Then we got the new method <strong>apply</strong> to do gamma correction. The math behind it is trivial but it's there for convenience. And as always GTC tries to give you a little extra by being able to apply to each axis a different gamma value, if you want to. The method name is a bit vague on purpose so it can do more in the future.</p>

<p><strong>Space inversion</strong></p>

<p>Last but not least I expanded the possibilities of the <strong>invert</strong> method. You can now specify which axis to invert, which makes this tool much more powerful and perlish (TIMTOWTDI), since inverting <em>'hue'</em> (in HSL, OKSL, LCH ...) is the same as computing the <strong>complement</strong> with the method of the same name.</p>

<p><strong>Talking Machines</strong></p>

<p>Of course nothing is written by an LLM. I tried it once for test rewrites, but I didn't like it and it wasn't even that much of a productivity boost. It's awful if you lose haptic contact with your mental model of the software. But I did use an LLM in two areas which made me a much better and faster developer.</p>

<p>First is planning. Research and evaluating possible features, grouping them, crafting a roadmap, are things that LLMs speed up. If you have concrete questions with answers that you can evaluate then it's not only a speedup but also a form of QA. If I have a well thought out roadmap much earlier in my head I can make much smarter decisions during development.</p>

<p>The second area where LLMs are very helpful are concrete calculations combined with research. Most helpful was being able to throw a conversion matrix at Claude and ask it how many decimals of precision this thing had and which white point it used. And my ProPhotoRGB conversion matrix needed a CAT Bradford adaptation. Claude reminded me of that fact on its own and computed after I asked for it a combined CAT and conversion matrix and told me the white point of it. It already knew it should use only values from the standard, colour-science or Lindbloom. All things I could potentially do too, but that would take me an extra half an hour at least. Of course I wrote the tests for that matrix by hand, but this was the real eye-opener to me.</p>
]]>
    </content>
</entry>



<entry>
    <title>Ideas for the CPAN Meta v3 Specification</title>
    <link rel="alternate" href="https://blogs.perl.org/users/robert_rothenberg/2026/05/ideas-for-the-cpan-meta-v3-specification.html" />
    <id>tag:blogs.perl.org,2026:/users/robert_rothenberg//2308.12040</id>
    <published>2026-05-04T22:12:17Z</published>
    <updated>2026-05-06T12:48:34Z</updated>
    <author>
        <name>Robert Rothenberg</name>
        
    </author>
    <category term="Metadata" scheme="http://www.sixapart.com/ns/types#category" />
    <category term="auditing" label="auditing" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="cpansec" label="cpansec" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="metadata" label="metadata" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="perltoolchainsummit" label="perl toolchain summit" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="security" label="security" scheme="http://www.sixapart.com/ns/types#tag" />
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/robert_rothenberg/">
        <![CDATA[<p>At the <a href="https://perltoolchainsummit.org/pts2026">2026 Perl Toolchain Summit</a>
<a href="https://metacpan.org/author/SJN">Salve Nilsen</a> and I proposed some ideas  that we have been discussing on and off for the past several months for <a href="https://security.metacpan.org">CPANSec</a>, for a CPAN Meta v3 Specification.</p>

<h2>Why does the specification need to be extended?</h2>

<p>Version 2 of the <a href="https://metacpan.org/release/RJBS/CPAN-Meta-2.150013/view/lib/CPAN/Meta/Spec.pm">CPAN Meta Spec</a> (CPAN distributio
n metadata specification) is does not allow the addition of new data, except using fields prefixed by "x_".</p>

<p>However, there is a need to include additional metadata about:</p>

<ul>
<li>external dependencies (services, libraries, files, or environment variable)</li>
<li>embedded external libraries, e.g. zlib or bootstrap.</li>
<li>licensing</li>
<li>vulnerability reporting</li>
<li>parent-child relationships (e.g. forked project)</li>
<li>fixed vulnerabilities in this fork or in embedded libraries</li>
<li>code and documentation generated through automation or using LLMs</li>
<li>how and where to report security vulnerabilities</li>
<li>project funding and sponsorship</li>
<li>how the project is supported by the maintainers</li>
<li>enumeration of community health documents, e.g. <code>SECURITY.md</code>, <code>GOVERNANCE.md</code> and <code>AI_POLICY.md</code></li>
</ul>

<p>This is too much information to embed in existing <code>META.json</code> files, and
some of this metadata exists in alternative formats, for example:</p>

<ul>
<li>SBOM (Software Bill of Materials) files</li>
<li>Attestations</li>
<li>VEX files</li>
<li>Common Lifecycle Enumeration (CLE)</li>
<li><a href="https://maastrichtu-ids.github.io/doap-workshop/">Project Description (DOAP) file</a></li>
<li><a href="https://github.com/ossf/security-insights">OSSF Security Insights File</a></li>
<li><a href="https://reuse.software/spec-3.3/">REUSE</a></li>
</ul>

<p>Note that most of this data is not necessary for installing CPAN modules. It exists mainly for documentation and auditing:</p>

<ul>
<li>generating SBOMs for an application using its dependencies</li>
<li>auditing software for security vulnerabilities</li>
<li>auditing software for license compliance</li>
<li>displaying the external documentation for a module such as the security policy</li>
</ul>

<h2>Specification</h2>

<p>The specification is simple:</p>

<ol>
<li><p>All new metadata will be saved in the <code>CPAN-META</code> directory at the root of the distribution and software repository.</p></li>
<li><p>All files and subdirectories saved in that directory will have well-known names.</p>

<p>Currently there is <code>automation-policy.json</code> for the AI and Automation Policy metadaya,
that I have worked with <a href="https://metacpan.org/author/ATOOMIC">Nicolas Rochelemagne</a>.
This will be discussed in a separate blog post.</p></li>
<li><p>The metadata <em>should never</em> be added as "x_" keys to the <code>META.yml</code> or <code>META.json</code> files.</p></li>
<li><p>This metadata may be provided as a separate file from a distribution.</p></li>
</ol>

<p>The proposed specification can be found at <a href="https://github.com/CPAN-Security/cpan-metadata-v3">https://github.com/CPAN-Security/cpan-metadata-v3</a></p>

<p>To suggest addition or changes, please create an issue or pull request.</p>

<h2>Tooling</h2>

<p>There are not yet tools for handling the METAv3 specification.</p>

<p>The tools will need to minimise the workload for project maintainers.</p>

<p>Modules should be configurable, testable and installable without any
tools that support this specification. However, metadata may be useful
for tools that understand them, for example, to ensure external
dependencies are met.</p>

<h2>Thanks</h2>

<p>Thanks to the organisers of the PTS for their hard work and hospitality, and to the sponsors who are keeping the Perl ecosystem growing:</p>

<ul>
<li><a href="https://www.perlfoundation.org/">The Perl and Raku Foundation</a>, </li>
<li><a href="https://www.grantstreet.com/">Grant Street Group</a>,</li>
<li><a href="https://geizhals.de/">Geizhals Preisvergleich</a>,</li>
<li><a href="https://vienna.pm.org/">Vienna.pm</a>,</li>
<li><a href="https://www.suse.com/">SUSE</a>,</li>
<li>Trans-Formed Media LLC,
<a href="https://www.ctrlo.com/">- Ctrl O</a>,</li>
<li><a href="https://www.simplelists.com/">Simplelists</a>,</li>
<li>Harald Joerg,</li>
<li>Michele Beltrame (<a href="https://www.blendgroup.it/">Sigmafin</a>),</li>
<li>Laurent Boivin.</li>
</ul>
]]>
        

    </content>
</entry>



<entry>
    <title>ANNOUNCE: Perl.Wiki V 1.45 etc</title>
    <link rel="alternate" href="https://blogs.perl.org/users/ron_savage/2026/05/announce-perlwiki-v-145-etc.html" />
    <id>tag:blogs.perl.org,2026:/users/ron_savage//297.12039</id>
    <published>2026-05-03T04:30:17Z</published>
    <updated>2026-05-03T04:36:01Z</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>All downloads mentioned here are available from my<br />
<a href="https://savage.net.au/">Wiki Haven:</a></p>

<p>a. Perl.Wiki V 1.45<br />
b. cpan.metacurator.tree.html V 1.17<br />
c. Mojo.Wiki V 1.18<br />
d. PHP.Wiki V 1.02</p>

<p>CPAN::MetaCurator V 1.17 has already been released to MetaCPAN, although I can't see it yet.<br />
</p>]]>
        
    </content>
</entry>



<entry>
    <title>Welcome to the Perl Toolchain Summit 2026!</title>
    <link rel="alternate" href="https://blogs.perl.org/users/book1/2026/04/welcome-to-the-perl-toolchain-summit-2026.html" />
    <id>tag:blogs.perl.org,2026:/users/book1//208.12036</id>
    <published>2026-04-29T19:00:00Z</published>
    <updated>2026-04-29T19:34:13Z</updated>
    <author>
        <name>BooK</name>
        <uri>http://www.bruhat.net/</uri>
    </author>
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/book1/">
        <![CDATA[<p>This post is adapted from my notes and recollection of the welcome
speech I gave on the morning of Thursday April 23, 2026, just before the
initial stand-up.</p>

<p><a href="https://geizhals.at/"><img alt="Geizhals Preisvergleich logo" src="https://blogs.perl.org/users/book1/pts2026/geizhals_logo_official.svg" style="max-width: 200px;float: right; margin: 0 20px 0px 0;" /></a></p>

<p>This post is brought to you by
<a href="https://geizhals.at/">Geizhals Preisvergleich</a>,
a Gold sponsor for the Perl Toolchain Summit 2026.</p>

<p>You can learn more about Geizhals at the end of this article.</p>
]]>
        <![CDATA[<h3>Welcome to the sixteenth Perl Toolchain Summit 2026!</h3>

<p>This is not the first time we're gathering in Vienna. Back in 2010, the
third Perl QA Hackathon was held in Vienna, and organized 100% locally.</p>

<p>Nowadays, the Perl Toolchain Summit is organized in a distributed fashion,
with a global team managing the invitations and the recurring sponsors,
and the local team finding the venue, the hotel, and organizing the
activities around the event (like the pre-conference meeting yesterday).</p>

<p>So I'd like to begin with a big thank you to the Vienna team:</p>

<ul>
<li><a href="https://metacpan.org/author/ABRAXXA">Alexander Hartmaier</a></li>
<li><a href="https://metacpan.org/author/DOMM">Thomas Klausner</a></li>
<li><a href="https://metacpan.org/author/MAROS">Maroš Kollár</a></li>
<li><a href="https://metacpan.org/author/PEPL">Michael Kröll</a></li>
<li><a href="https://metacpan.org/author/WOLLMERS">Helmut Wollmersdorfer</a></li>
</ul>

<p>You might know that the French are very proud of their
<a href="https://en.wikipedia.org/wiki/Croissant">croissants</a>.
I personally got into heated arguments with Italian colleagues about the
Italian variations stuffed with jam (I've wisened up since). Here's a
bit of trivia for this you: the generic name for these sweet dough
products at the meeting point between bread and pastry in French is
<em>viennoiseries</em>. This morning, thanks to
<a href="https://en.wikipedia.org/wiki/Viennoiserie#History">Wikipedia</a>, I
learnt that, though the shape is Austrian-inspired, the recipe was developed
in France in the nineteenth century. That's it for the history lesson.</p>

<p>We try to make this event as cheap as possible to attend for you,
the people who are going to work for free for the benefit of the Perl
communities. It wouldn't be possible without our sponsors. Or at least,
it would cost you a lot more to attend.</p>

<p>We're trying to recognize support in all its form: financial (sending us
money directly) and in-kind (paying for some of our attendees expenses
directly). Some sponsors even contribute both ways.</p>

<p>At the Diamond level, we are very lucky to have:</p>

<ul>
<li><a href="https://perlfoundation.org/">The Perl and Raku Foundation</a></li>
<li>a very generous anonymous donor</li>
</ul>

<p>Gold sponsors:</p>

<ul>
<li><a href="https://www.grantstreet.com/">Grant Street Group</a></li>
<li><a href="https://vienna.pm.org/">Vienna.pm</a></li>
<li><a href="https://geizhals.at/">Geizhals</a></li>
</ul>

<p>Silver sponsors:</p>

<ul>
<li><a href="http://www.suse.com/">SUSE</a></li>
<li>Trans-Formed Media</li>
</ul>

<p>Bronze sponsors:</p>

<ul>
<li><a href="https://www.simplelists.com/">SimpleLists</a></li>
<li><a href="https://www.ctrlo.com/">Ctrl-O</a></li>
<li><a href="https://www.koha-support.eu/">HKS3 / Koha Support</a></li>
</ul>

<p>We also received monetary support from the community:</p>

<ul>
<li>Harald Jörg</li>
<li>Michele Beltrame, <a href="https://www.blendgroup.it/">Sigmafin</a></li>
<li>our anonymous donor who's been making a monthly donation for
several years now</li>
</ul>

<p>While the size of the group has stabilized around thirty
people, and some of us are <em>very</em> regular attendees (heya
<a href="https://metacpan.org/author/HMBRAND">Merijn</a>!), we're
also making a genuine effort to invite people to their
first Perl Toolchain Summit. This year, our first timers
are <a href="https://metacpan.org/author/RRWO">Robert Rothenberg</a>,
<a href="https://metacpan.org/author/KHW">Karl Williamson</a> and
<a href="https://metacpan.org/author/TEODESIAN">Andy Baugh</a>.</p>

<p>As you've seen, new blood is not necessarily <em>young</em> blood. I'm a bit
worried that the average age of this group is basically growing by one
year every year (someday I'll do the maths). I wonder who is the youngest
in the group.  Is there anyone below 40? [It took little time to find
the youngest member of the group, and he was just 40 years old.]</p>

<p>Speaking of regular attendees, you might have noticed a few people
are missing. At least one of them has a good excuse: <a href="https://metacpan.org/author/INGY">Ingy döt
Net</a> couldn't attend this year,
because he got married yesterday to Ashley!</p>

<p><a href="https://blogs.perl.org/users/book1/assets_c/2026/04/2026-04-23-02-25-46-853-Ingy-wedding-5325.html" onclick="window.open('https://blogs.perl.org/users/book1/assets_c/2026/04/2026-04-23-02-25-46-853-Ingy-wedding-5325.html','popup','width=1200,height=1600,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"><img src="https://blogs.perl.org/users/book1/assets_c/2026/04/2026-04-23-02-25-46-853-Ingy-wedding-thumb-400x533-5325.jpg" width="600" height="800" alt="Ingy and Ashley" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto 20px;" /></a></p>

<p>Once again, all Perl Steering Council members are 
attending, the <a href="https://metacpan.org/about">MetaCPAN</a>
team has already annexed an appropriately sized room
for themselves, and our whiteboard already has a few
<a href="https://perltoolchainsummit.org/pts2026/wiki?node=Discussions">discussions</a>
listed.</p>

<p>A few announcements before we start:</p>

<ul>
<li>the group dinner will happen on Friday evening</li>
<li>we'll do the annual group photo on Saturday</li>
<li>I will be recording interviews for <a href="https://underbar.cpan.io/">The Underbar podcast</a> all of  Sunday</li>
</ul>

<p>Now it's time for the stand-up. Given how many of us there are, we want
to keep it short, really a few sentences per person. Tell us you name
and nickname, for those who don't know you yet, and what you plan to
work on this year.</p>

<p>I expect that tomorrow, we'll already be talking about results!</p>

<h3>About Geizhals Preisvergleich</h3>

<blockquote>
  <p>For over 25 years, Perl has been at the core of our technical
ecosystem. It has also been instrumental in building one of the leading
product price comparison services in the German-speaking region. At
Geizhals Preisvergleich, we are proud to sponsor the 2026 edition of the
Perl Toolchain Summit and to support the continued development of the core
Perl ecosystem we have long relied on -- and continue to rely on today.</p>
</blockquote>
]]>
    </content>
</entry>



<entry>
    <title>Reading CPAN Testers Reports Using AI Agents</title>
    <link rel="alternate" href="https://blogs.perl.org/users/preaction/2026/04/reading-cpan-testers-reports-using-ai-agents.html" />
    <id>tag:blogs.perl.org,2026:/users/preaction//398.12038</id>
    <published>2026-04-29T12:35:39Z</published>
    <updated>2026-05-04T15:21:42Z</updated>
    <author>
        <name>preaction</name>
        <uri>http://preaction.me</uri>
    </author>
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/preaction/">
        <![CDATA[<p><a href="https://cpantesters.org">CPAN Testers</a> produce a lot of data. Every <a href="https://cpan.org">CPAN</a> distribution gets tested by our volunteers almost immediately after upload. These testers run every version of Perl across every platform you can imagine, and some you never knew existed. Instead of each project maintaining its own testing environments, the community maintains these systems so the project developers can focus on developing their project. There are more than 150 million test reports so far, and that number currently grows by about one million every month.</p>

<p>Sorting through all of those test reports is a big job. The community helps: Slaven Rezić, Andreas König, and others regularly submit tickets to a project's bug tracker for problems revealed by the testing systems they maintain. And individual maintainers can visit one of the UIs to view the data like <a href="https://fast2-matrix.cpantesters.org">the CPAN Testers Matrix</a> (by Slaven) or <a href="https://magpie.cpantesters.org">CPAN Testers Magpie</a> (by Scott Baker). But this, too, is a lot of manual effort.</p>

<p>Large Language Models (LLM) or "AI" agents have recently arisen as a way to chew through large data sets to produce summaries, even if the data is not well-formatted or "machine-readable." By making requests in plain language, a human can tell an agent to fetch data, analyze it, reformat it, compare it, and produce reports. This year, at <a href="https://perltoolchainsummit.org/pts2026/">the 2026 Perl Toolchain Summit in Vienna Austria</a>, I have built <a href="https://mcp.cpantesters.org">an interface so agents can easily discover and analyze the CPAN Testers data using the Model Context Protocol (MCP.)</a></p>
]]>
        <![CDATA[<p>By pointing your agent at <a href="https://mcp.cpantesters.org">https://mcp.cpantesters.org</a>, you can ask for CPAN Testers reports for any distribution, and your agent can give you a composite of which tests are failing on which Perl versions and platforms and even suggest fixes! CPAN authors can even ask for a summary across all of their projects to find easy things to fix when they've got a few minutes free. If your agent has scheduled tasks, you can get daily digests of all the test failures from the last day, a feature that was once part of the CPAN Testers website, now entirely customizable to your preferences. If you'd like to help expand the possibilities for agent integration, join <a href="https://github.com/cpan-testers/mcp">the CPAN Testers MCP project on Github</a>.</p>

<p>I'm not that experienced yet with using these agents, but here are some quick examples I made while testing the MCP integration with the Claude desktop application:</p>

<blockquote>
  <p><a href="https://claude.ai/share/941df0f8-421f-47fc-8ab1-cd01a68e95d6">"list test reports for all of PREACTION's dists and check for fails"</a></p>

<p><a href="https://claude.ai/share/96c89a4f-a9a6-49cc-a141-41135428ea00">"List all failing test reports from the last month for distributions by SRI analyze text for issues that are easy to fix."</a></p>
</blockquote>

<p>That second query impressed me. With this, CPAN authors could remove a lot of the tedium from diagnosing failures from user test reports!</p>

<p>This work was made possible by <a href="https://perltoolchainsummit.org/pts2026/sponsors.html">the sponsors of the 2026 Perl Toolchain Summit in Vienna, Austria</a>: <a href="https://www.perlfoundation.org/">The Perl and Raku Foundation</a>, 
<a href="https://www.grantstreet.com/">Grant Street Group</a>,
<a href="https://geizhals.de/">Geizhals Preisvergleich</a>,
<a href="https://vienna.pm.org/">Vienna.pm</a>,
<a href="https://www.suse.com/">SUSE</a>,
Trans-Formed Media LLC,
<a href="https://www.ctrlo.com/">Ctrl O</a>,
<a href="https://www.simplelists.com/">Simplelists</a>,
Harald Joerg,
Michele Beltrame (<a href="https://www.blendgroup.it/">Sigmafin</a>,
Laurent Boivin.</p>
]]>
    </content>
</entry>



<entry>
    <title>The Perl Documentation - Rewritten</title>
    <link rel="alternate" href="https://blogs.perl.org/users/petamem/2026/04/the-perl-documentation---rewritten.html" />
    <id>tag:blogs.perl.org,2026:/users/petamem//1658.12035</id>
    <published>2026-04-26T18:05:02Z</published>
    <updated>2026-04-27T12:43:42Z</updated>
    <author>
        <name>PetaMem</name>
        <uri>http://www.petamem.com</uri>
    </author>
    <category term="documentation" label="Documentation" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="perl5" label="Perl5" 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>Well, not <em>all</em> of it ... yet. But some of it has been rewritten many times in many languages and "all" of it will be rewritten in many more languages. Of course "all" will never be reached, so it will be an ongoing endeavor, but at least you know the goalposts.</p>

<p>You can read it at <a href="https://perl.petamem.com/docs/eng/">https://perl.petamem.com/docs/eng/</a>, and the language picker in the upper right hand corner will tell you, more honestly than any sentence in this post can, where the public-facing part of the work currently stands.</p>
]]>
        <![CDATA[<h2>What you will find there</h2>

<p>A reworked documentation set for Perl - shipping with pperl, but addressed to the wider Perl audience. It covers the things you would expect (introduction, getting-started, CLI, how-tos, P5 and PP runtime references) and is rendered through a tailored Sphinx pipeline so the navigation, search, and theming behave the way modern documentation sites are expected to behave.</p>

<p>The picker currently shows four languages in colour: English, German, French, Spanish. All four are work in progress. English is the source - the canonical version every other language is translated from - but it is itself being rewritten and reorganised, chapter by chapter, and is not in any sense "done." The other three languages are translations that follow the moving English target. The remaining flags in the picker are greyed: planned coverage, in scripts spanning Latin, Cyrillic, Greek, Hebrew, and Arabic. Languages will move from grey to colour as their translations land. We are not committing to a date on any of them, and we are not committing to the final list either. The picker is a roadmap of work we are confident we can finish, not a wish list.</p>

<p>Readers who followed <em>Rust-PDL Part Two</em> a few days ago will find the corresponding documentation at <a href="https://perl.petamem.com/docs/eng/p5/PDL.html">https://perl.petamem.com/docs/eng/p5/PDL.html</a>. The pattern, in case it is not obvious from the two posts read together: code first, documentation as soon afterwards as we can manage. Documentation that lags the code is documentation nobody trusts.</p>

<h2>Two axes of work</h2>

<p>There are two distinct kinds of work happening here, and they do not always speak to the same reader.</p>

<p><strong>The first is content.</strong> A lot of what ships in the canonical Perl documentation has been with us for a long time, was written under different conventions of technical writing, and shows it. Some of it is excellent. Some of it presumes a reader who has already learned Perl from somewhere else and just needs the reference. The work we are doing on this axis is not a polite refresh of existing prose. It is, in places, a structural rewrite - new organisation, new examples, new emphasis on the things modern Perl users actually run into. The goal is documentation that a Perl programmer in 2026 can read without having to imaginatively reconstruct the assumptions of a Perl programmer in 1998.</p>

<p><strong>The second is translation.</strong> Perl documentation has historically been an English artefact. There are pockets of community translation work, scattered and varying in completeness, but no comprehensive multi-language documentation set in the modern sense. We are building one. The motivation is simple: a developer whose first language is not English currently has, for many languages, exactly zero Perl documentation in their language. The bar to clear, for that developer, is not perfection. It is "better than nothing." That is one piece of a larger picture I do not propose to lay out here. The picker shows the ambition; the four currently active languages (English plus three) show the present.</p>

<h2>A case study: the debugging chapter</h2>

<p>The most concrete way to show what "rewritten" means is to point at one chapter. The debugging guide at <a href="https://perl.petamem.com/docs/eng/guide/debugging/index.html">https://perl.petamem.com/docs/eng/guide/debugging/index.html</a> is structured around what a developer actually does when something is going wrong:</p>

<ul>
<li>Preflight (what to check before reaching for a debugger)</li>
<li>Print and die (the simplest tools, used well)</li>
<li>Inspecting state</li>
<li>The interactive debugger</li>
<li>Breakpoints</li>
<li>Tracing</li>
<li>Exceptions</li>
<li>IDE / DAP integration</li>
</ul>

<p>This is a different shape from the upstream <code>perldebug</code> document, in two ways. The first is structural: each of the eight topics is its own URL, addressable from a search result, rather than a section in one long narrative. The second is depth-layered: each topic exists at three reading depths - an overview that orients, a reference that defines precisely, and a "gory details" layer for the reader who has met a corner case and needs to understand it. This is the shape every guide chapter is moving toward. It serves the reader who arrives with a specific question and the reader who wants to understand the topic seriously, from the same source.</p>

<p>How was this chapter produced? The honest description is: we used an AI as a research assistant, with access to the same kinds of sources a human technical writer would consult - perldoc itself, CPAN module documentation, conference talks, blog posts, mailing-list archives, the source code of the Perl debugger, and one book used with the explicit permission of its author, Richard Foley - and ran several distillation passes over its output. De-obsoleting (removing references to versions and tools nobody runs anymore). De-duplicating (the same advice repeated by different authors collapses into one statement). Synthesising (multiple partial treatments of one topic become a single complete treatment). The methodology is not exotic, but it is genuinely different from "writer sits down and writes a chapter," and it produces results of a kind that "writer sits down" cannot easily reach - because no human writer has read everything that has been written about Perl debugging, and the cost of doing so would be prohibitive.</p>

<p>A note on the obvious question: this is the same activity any technical author performs. Reading existing material on a topic to inform new writing is how technical writing has always worked. Copyright protects expression, not facts; the output is a new piece of writing in a new structure, not a derivative of any one source. The AI changes the scale and speed of what one author can read and synthesise. It does not change the underlying activity. We have been deliberate about this. Human review is not a checkpoint at the end of the process; it accompanies the process, which is iterative and ongoing.</p>

<p>One sequencing detail worth naming. The rewriting described above is the first stage. The translations come after - applied to the rewritten English, not to the upstream original. And translation follows a different discipline from rewriting: faithful, not inventive. We grant ourselves latitude when restructuring an English chapter against thirty-year-old upstream prose. We grant ourselves no such latitude when carrying that chapter into French or German or Spanish - obviously. Compounding deviation across two transformations would defeat the point of doing either one carefully.</p>

<h2>A case study: the build pipeline</h2>

<p>The translation work is one branch of a larger pipeline. Before any translation happens, the English source is assembled out of multiple inputs: hand-authored Markdown, per-function reference files (perlfunc has one Markdown file per built-in, with YAML frontmatter for category tags, signature, and prose body), runtime metadata derived from the pperl source tree, CLI help auto-extracted from the binary's option parsing, and a lint pass that catches the kinds of things linters catch in code: dead links, malformed cross-references, frontmatter schema drift. All of this lands in a staging tree that Sphinx then renders. The source tree is never written to during a build; the staging tree is the contract between the assembly stage and the rendering stage.</p>

<p>Translation hangs off this staging tree. Each target language has its own PO catalogue, identified by ISO 639-3 code (<code>fra</code>, <code>deu</code>, <code>spa</code>, and so on - the three-letter codes are deliberate, because the two-letter ISO 639-1 set runs out of distinctions for several of the languages on our roadmap). The translation passes are run by per-language translator agents that are seeded with terminology consistency rules and a fixed register for technical prose. PO catalogues are merged forward when the English source changes, so a sentence revised on the English side surfaces as a fuzzy entry in every language catalogue and is re-translated rather than silently going stale.</p>

<p>The pipeline handles right-to-left scripts (Arabic, Hebrew) and non-Latin scripts (Cyrillic, Greek) without per-language special casing - the layout is handled at the theme level, which means a new language is, infrastructurally, a new PO catalogue and not a build-system rewrite. That separation is what makes the long roadmap tractable.</p>

<p>Each language renders to two outputs: the HTML site that the picker navigates, and a single large PDF of the whole documentation set. The PDF is not a polite afterthought. There are readers and use cases - offline reading, print, archival, regulated environments - where a self-contained PDF is the format that actually gets used.</p>

<p>Each HTML page also offers a "Source (accessible &amp; AI friendly)" link that returns the page's underlying source rather than its rendered HTML. This is partly conventional - source links are good engineering practice for any documentation site - but it is also a deliberate accommodation for a class of reader we expect to grow: AIs, agents, and other automated consumers of documentation. They do not need the navigation chrome, the syntax-highlighting CSS, or the responsive layout. They need the text, structured. The source links give them that.</p>

<p>Some numbers to give a sense of the translation volume. The <code>perlfunc</code> reference in French alone takes roughly 1.9 million tokens of translation work, split across eight chunks running in parallel, around 22 to 23 minutes per chunk. That is one section, in one language. Multiply by the number of sections, multiply again by the number of languages on the roadmap, and the order of magnitude becomes the point: this is work that historically did not happen in the Perl world because nobody had the resources to do it. We do, now, and we are doing it.</p>

<h2>A note on the difficulty</h2>

<p>It would be easy to read the volume figures and conclude that with a sufficiently large language model and enough patience, multi-language technical documentation is now a solved problem. It is not. The honest description of the work is that it is brutally hard.</p>

<p>Terminology has to stay consistent across thousands of strings within one language and across all languages. The idiomatic register has to match the original's tone, which itself varies by section. Technical precision has to survive translation, which is harder than it sounds when the source language is English and the target language has different defaults for, say, the number of words a sentence can carry before it becomes grammatically ambiguous. Code examples raise their own questions - do you translate variable names? Function names? Error message strings the reader will see in their own terminal? (We do not, mostly, but the question recurs at every chapter.) Right-to-left layouts produce edge cases that no test catches until a human reads the rendered page. AI is the reason this work is feasible at all. But - you know - AI+Perl makes hard things... possible. </p>

<h2>Heads up</h2>

<p>This is a "release early" post. The English documentation is in a state where you can use some of it. The three active translations are in a state where they are useful for the chapters that have landed and unfinished for the chapters that have not. The greyed flags in the picker are work we intend to do; we are not promising any specific date.</p>

<p>If you read Perl in English and prefer documentation that has been organised around modern reading habits, the site is worth a look today. If you read Perl better in another language, the picker shows whether your language is live yet, and the answer for most is "not quite, but coming." If you find errors, things that should be re-organised, or sections that read awkwardly, no surprise there - it is an early shape.</p>

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



<entry>
    <title>This week in PSC (222) | 2026-04-25</title>
    <link rel="alternate" href="https://blogs.perl.org/users/psc/2026/04/this-week-in-psc-222-2026-04-25.html" />
    <id>tag:blogs.perl.org,2026:/users/psc//4112.12034</id>
    <published>2026-04-25T19:08:18Z</published>
    <updated>2026-04-25T19:11:53Z</updated>
    <author>
        <name>Perl Steering Council</name>
        
    </author>
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/psc/">
        <![CDATA[<p>This meeting took place at the Perl Toolchain Summit 2026. All three of us met in person in Vienna and reviewed our release blocker status. Things are in the clear: no new blockers have come in since last meeting, and the previously identified ones are already resolved, with a single exception, namely the workflow for EOL notices in release announcements. Conveniently, Philippe, who just released 5.43.10, is also in attendance in Vienna, and has provided concrete feedback, so this is being addressed.</p>

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

    </content>
</entry>



<entry>
    <title>Who tests the tester? Me !!!</title>
    <link rel="alternate" href="https://blogs.perl.org/users/lichtkind/2026/04/who-tests-the-tester-me.html" />
    <id>tag:blogs.perl.org,2026:/users/lichtkind//275.12032</id>
    <published>2026-04-24T19:21:07Z</published>
    <updated>2026-04-29T18:49:40Z</updated>
    <author>
        <name>lichtkind</name>
        <uri>http://www.lichtkind.de/</uri>
    </author>
    <category term="Color" scheme="http://www.sixapart.com/ns/types#category" />
    <category term="Perl 5" scheme="http://www.sixapart.com/ns/types#category" />
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/lichtkind/">
        <![CDATA[<p>As <a href="https://blogs.perl.org/users/lichtkind/2026/03/graphicstoolkitcolor-20-feater-overview.html">already reported</a>, I'm writing this <a href="https://metacpan.org/pod/Graphics::Toolkit::Color">color library</a>. Recently I created my own test function for it. And since it was easier that I thought, I want to show you how, so you can write your own!</p>
]]>
        <![CDATA[<p><strong><big>The Task</big></strong></p>

<p>If you look into the <a href="https://blogs.perl.org/users/lichtkind/2025/08/architecture-of-gtc-18.html">GTC internals</a>, you see instantly that the most common none OO data structure is a <em>'tuple'</em> with the values of one color in one color space (usually three values). Since we do not have a native tuple type in Perl - it is an ordinary Array, one could  also say a value vector. In the GTC test suite they get checked very often. Each time I have to ask:</p>

<ol>
    <li>Did I get an Array ?</li>
    <li>Does it have the right amount of values ?</li>
    <li>Check every value of the tuple for equality.</li>
</ol>

<p>These are usually 5 lines that could and should have been one line. In practice this means 200 rows of test code will shrink to 40 - neat. Less to type, less to read - wonderful.  And the assertions will be more to the point telling WHAT I want to test and WHY not just two values match, which is also good for readability and clarity on the other end. If running the tests, the <em>ok</em> - messages will tell me what is actually tested and not that two values matched. But the most important improvement comes to light when something goes wrong. Let's say the function we test collapses and returns <em>undef</em>. The first test will tell us that we got no ARRAY, good, but the next who checks the lengths of the array will crash in a hard syntax error. You have to fix the causing bug in order to see the next test results because your test suite crashed. Wouldn't it be so much nicer the test suite could run to the end and the error messages tell you about all the bugs at once, so you could theoretically hunt them in one go.</p>

<p>You see: less code, clearer code, better error message, only relevant error messages and no hard crashes. These are more than enough motivating reasons to write custom error functions, so let's do it, let's write a variant of </p>

<pre><code>is( $got, $expected, $test_name );
</code></pre>

<p>(which is a test function you all used at some point) and name it:</p>

<pre><code>is_tuple( $got, $expected, $axis, $test_name );
</code></pre>

<p>You notice I needed one more argument for the axis names. So I can get the nice error message: "the red value was 13 but I expected 15".</p>

<p><strong><big>The Solution</big></strong></p>

<p>The Module you need to create that is <a href="https://metacpan.org/pod/Test::Builder">Test::Builder</a>. So the smart ones among you have guessed you need to:  </p>

<pre><code>use Test::Builder;
my $tb = Test::Builder-&gt;new;
</code></pre>

<p>You can also subclass Test::Builder but this works as well, since ->new will give you the only instance of $tb anyway. And to start our test function we need just:</p>

<pre><code>sub is_tuple {
my ($got, $expected, $axis, $name) = @_;
my $pass = 0;
my $diag = '';
</code></pre>

<p>Well if you are reading this blog, you know about Perl argument handling. $pass is the pseudo boolean than holds the information if the test was successful, we pass it on to the Test::Builder method at the end. Same is true for $diag, which is the error message we maybe have to drop. This is not the way you have to do it. Calling the diag method several times is also an option, but i prefer to have short error messages that fits in one line and only tells me what exactly went wrong. Let's skip the testing logic, since a bunch of nested if statements with some basic value checks isn't that impressive and educational. I just like a clean separation between out test logic and the part where I talk to Test::Builder. That is why I declared the variables at the start, fill them when i need to, so I only call the following once at the end of the subroutine:</p>

<pre><code>$tb-&gt;diag( $diag ) unless $pass;
$tb-&gt;ok( $pass, $name );
</code></pre>

<p>Yes that is all. Do not forget to:</p>

<pre><code>require Exporter;
our @ISA = qw(Exporter);
our @EXPORT = qw(is_tuple);
</code></pre>

<p>But this was really it. It didn't even hurt and we didn't have to see Detroit.</p>

<p><strong><big>The Tests</big></strong></p>

<p>More challenging I found it to write the test file that checks the logic code I didn't show in the above example.</p>

<pre><code>use v5......;
use warnings;
use lib '.',...;
use Test::More tests =&gt; ...;
use Test::Builder::Tester;
use Test::Color;
</code></pre>

<p>This is mostly your usual start template of any test file. The <em>lib</em> pragma needs to receive the directory where the module lives, that contains <em>is_tuple</em>, which would be in my case Test::Color. And we need <a href="https://metacpan.org/pod/Test::Builder::Tester">Test::Builder::Tester</a> to test what we built with Test::Builder (the module names fit).</p>

<pre><code>test_out("ok 1 - is_tuple runs");
is_tuple([1,1,1], [1,1,1], [qw/red green blue/], 'is_tuple runs');
test_test("simplest is_tuple case runs");
</code></pre>

<p>Our first little smoke test seems trivial. We call <em>is_tuple</em> with the the result values of some operation (<em>$got</em>) and the values to check them against (<em>$expected</em>), then the axis names and at last the test name (the name of the test is_tuple performs). But BEFORE that you HAVE TO tell Test::Builder::Tester what output to expect from the test function <em>is_tuple</em> on STDOUT. The last line tells Test::Builder::Tester the name of the test we did by testing the test function. That HAS to come AFTER calling <em>is_tuple</em>.</p>

<p>Now we are ready for the juicy bit. How to test a failing test? I mean by that: <em>is_tuple</em> will fail because we gave it bad data by purpose. And if <em>is_tuple</em> tries to give the right error message to STDERR (standard error output), Test::Builder::Tester should intervene and call it a successful test to STDOUT. The code to do this is:</p>

<pre><code>test_out("not ok 1 - C");
test_err("# failed test: C - got values that are not a tuple (ARRAY ref)");
test_fail(+1);
is_tuple(1, [1,1,1], [qw/red green blue/], 'C');
test_test("is_tuple checks if got values in an ARRAY");
</code></pre>

<p>First we tell via <em>test_out</em> again what STDOUT suppose to receive. Then we tell via <em>test_err</em> what error message should land in STDERR. And when a test fails Test::Builder will create an additional error message telling where it happened. In order to not have to chase line numbers we got the convenience function:  <em>test_fail(+1);</em> You can translate it to: "Hej Test::Builder::Tester, the next line (+1) will cause an error, this is fine, please do not create this additional error with the line number". What actually happens is, this call gets forwarded to a <em>test_err</em> call with the appropriate string that contains the right line number. Then we finally call the test function we want to test and at the very end again - the name of this (meta) test.</p>

<p>One last useful hack. You noticed I called the test that <em>is_tuple</em> does in the last example  just uppercase C. This is not a nice and telling name for any test and brutally counterproductive - However - since we testing the test and the name of the inner test is part of the STDOUT and STDERR check string in the outer test, it is nice to trace easily what substring comes from where and this is also rather educational for this demo. What this (inner) test suppose to do is documented anyway by the name of the outer test. </p>
]]>
    </content>
</entry>



<entry>
    <title>PPC Summer 2026 - Call for Participation!</title>
    <link rel="alternate" href="https://blogs.perl.org/users/oodler_577/2026/04/ppc-summer-2026---call-for-participation.html" />
    <id>tag:blogs.perl.org,2026:/users/oodler_577//3888.12033</id>
    <published>2026-04-24T15:22:19Z</published>
    <updated>2026-05-02T00:56:54Z</updated>
    <author>
        <name>Brett Estrade</name>
        
    </author>
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/oodler_577/">
        <![CDATA[<p><a href="https://www.papercall.io/perlcommunityconferencesummer26">https://www.papercall.io/perlcommunityconferencesummer26</a></p>

<p><strike>Please share this post, the powers that be rage against us; you will not see any post regarding our activities in Perl Weekly!</strike></p>

<p><strong>Update:</strong> We got in Perl Weekly <a href="https://perlweekly.com/archive/770.html">this week</a>, but not for the right reasons. I hope in the future this will not be repeated or necessary. Thanks to the Perl Weekly editor who included this announcement. Special acknowledgement to David Cross for heading up the negotiations.</p>

<p>If you wish to comment about this post, please do so at <a href="https://www.reddit.com/r/perlcommunity/comments/1ss0z0h/cfp_now_open_for_perl_community_conference_summer/">r/perlcommunity</a>.</p>

<p><a href="https://www.papercall.io/perlcommunityconferencesummer26"><img src="https://blogs.perl.org/users/oodler_577/assets_c/2026/04/highres_533738236-thumb-869x488-5322.jpg" width="869" height="488" alt="highres_533738236.jpg" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto 20px;" /></a></p>

<h1>Perl Community / Science Perl Committee Impact in 2025</h1>

<p><strong>Talks Delivered at Winter 2025 Perl Community Conference in Austin, TX</strong></p>

<p>Video editing in progress, will be released after the 2026 Summer PPC.</p>

<p>Each PPC has its own <a href="https://www.youtube.com/@PerlCommunity/playlists">playlist </a> on our YT channel!</p>

<p><strong>Talks Delivered at Summer 2025 Perl Community Conference in Austin, TX</strong></p>

<ul>
<li><a href="https://www.youtube.com/watch?v=UMbT6Ui4Nao">Perl Community Conference Summer 2025 - Vincent Napiorkowski   What I Learned About Perl &amp; Catalyst</a></li>
<li><a href="https://www.youtube.com/watch?v=NADhMHyn8lE">Perl Community Conference Summer 2025  - Perl Types Will Braswell, Jr</a></li>
<li><a href="https://www.youtube.com/watch?v=gkgTLZMnoa8">Perl Community Conference Summer 2025 - Valiant Update  John Napiorkowski</a></li>
<li><a href="https://www.youtube.com/watch?v=w5kSHmDeMig">Perl Community Conference Summer 2025 - Will Braswell, Ohinoyi Moiza - Intern Report</a></li>
<li><a href="https://www.youtube.com/watch?v=hBNo93xowAc">Perl Community Conference Summer 2025  - Brett Estrade wxPerl on Windows</a></li>
<li><a href="https://www.youtube.com/watch?v=V_kE8SUcEAI">Perl Community Conference Summer 2025 - Virtues Update</a></li>
<li><a href="https://www.youtube.com/watch?v=M1jOWZAM-cA">Perl Community Conference Summer 2025 - Science Perl Committee Report</a></li>
<li><a href="https://www.youtube.com/watch?v=_BBjBommYaM">Perl Community Conference Summer 2025 - Privacy Preserving Applications</a></li>
<li><a href="https://www.youtube.com/watch?v=2JlZ9FUxBxw">Perl Community Conference Summer 2025 - Kai Baker, et al Shiny CMS</a></li>
<li><a href="https://www.youtube.com/watch?v=5TpkjJlKIg4">Perl Community Conference Summer 2025 - John Napiorkowski Porting ASGI from Python to Perl</a></li>
<li><a href="https://www.youtube.com/watch?v=hI-ywpW7mqw">Perl Community Conference Summer 2025 - Brett Estrade Building Beowulf Clusters with Perl</a></li>
<li><a href="https://www.youtube.com/watch?v=kQgSuzaa1Fs">Perl Community Conference Summer 2025 - Perl Can Dev Ops Better Than You Think</a></li>
<li><a href="https://www.youtube.com/watch?v=oL4uCN6me_0">Perl Community Conference Summer 2025 - State of the Onions</a></li>
<li><a href="https://www.youtube.com/watch?v=YgVajMRJN2Y">Perl Community Conference Summer 2025 - Justin Kelly Perl Supabase</a></li>
<li><a href="https://www.youtube.com/watch?v=9BQcpYV5f90">Perl Community Conference Summer 2025 - Brett Estrade Review of John P Linderman's  Quick Sort Paper</a></li>
</ul>

<p><strong>Talks Delivered at Winter 2024 Perl Community Conference in Austin, TX</strong></p>

<ul>
<li><a href="https://www.youtube.com/watch?v=sfVDfpqmfBs">Perl Community Conference Winter 2024 - Intro - Dr. Christos Argyropoulos, MD</a></li>
<li><a href="https://www.youtube.com/watch?v=uzZOy-hFQrc">Perl Community Conference Winter 2024 - State of the Noonien - Will Braswell, Jr</a></li>
<li><a href="https://www.youtube.com/watch?v=3Ka2Zp2CM2k">Perl Community Conference Winter 2024 - Metamaterials - Dr. Luis Mochán</a></li>
<li><a href="https://www.youtube.com/watch?v=tZrtlTmfIso">Perl Community Conference Winter 2024 - Valiant - John Napiorkowski</a></li>
<li><a href="https://www.youtube.com/watch?v=v2-SaHAQ9iA">Perl Community Conference Winter 2024  - CPAN Ontologies - Dr  Adam Russell</a></li>
<li><a href="https://www.youtube.com/watch?v=OeWgpp_zuR0">Perl Community Conference Winter 2024 - Limits of Thread Safety in the Perl C API - Brett Estrade</a></li>
<li><a href="https://www.youtube.com/watch?v=UUdjE3ZZ0P8">Perl Community Conference Winter 2024 - Perl FFI, Native C Options  - Dr. Christos Argyropoulos, MD</a></li>
<li><a href="https://www.youtube.com/watch?v=kgwZ5KoptO4">Perl Community Conference Winter 2024 - Chemometrics - Dr.  Andrew O'Neil</a></li>
<li><a href="https://www.youtube.com/watch?v=vuShpuCGdbg">Perl Community Conference Winter 2024 - Perl Types - WIll Braswell, Jr</a></li>
<li><a href="https://www.youtube.com/watch?v=bzIFx4lu_0g">Perl Community Conference Winter 2024 - Leadership Panel - WIll Braswell, Jr, et al.</a></li>
<li><a href="https://www.youtube.com/watch?v=pcQL92D5xzU">Perl Community Conference Winter 2024 - Lightning Talks - Various</a></li>
<li><a href="https://www.youtube.com/watch?v=t6PLBGmwGGM">Perl Community Conference Winter 2024 - Closing - Dr. Christos Argyropoulos, MD</a></li>
</ul>

<p><strong>Science Track Paper-based Talks Delivered at Summer 2024 Perl &amp; Raku Conference in Las Vegas</strong></p>

<ul>
<li><em>Science Track Keynote &amp;</em> <a href="https://perlcommunity.org/awards/#diamondperl"><em>Diamond PERL Editor's Choice of Technical Excellence</em></a><em>,</em> <em>Winner</em>: <strong>Enhancing Non-Perl Bioinformatic Applications with Perl - Christos Argyropoulos, MD, PhD.</strong></li>
<li><a href="https://www.youtube.com/watch?v=dn9msFIED-8&amp;list=PLA9_Hq3zhoFw6patag2gZcDjpugDLBStL&amp;index=16">Structure Based Structuring of Unstructured Data - Adam Russell, PhD.</a></li>
<li><a href="https://www.youtube.com/watch?v=BcFl4efLuuk&amp;list=PLA9_Hq3zhoFw6patag2gZcDjpugDLBStL&amp;index=3">Chemometrics with Perl &amp; Pharmaceutical Applications - Andrew O'Neil, PhD</a></li>
<li><a href="https://www.youtube.com/watch?v=Agw6E1omIvY&amp;list=PLA9_Hq3zhoFw6patag2gZcDjpugDLBStL&amp;index=4">PerlGPT, A Code Llama LLM Fine-Tuned For Perl - William N. Braswell, Jr.</a></li>
<li><a href="https://www.youtube.com/watch?v=EgpWWt1R11U&amp;list=PLA9_Hq3zhoFw6patag2gZcDjpugDLBStL&amp;index=10">Reasoning About the Rigor of Perl Programs - George Baugh - TPRC 2024</a></li>
<li><a href="https://www.youtube.com/watch?v=dUGVAQ6wafE&amp;list=PLA9_Hq3zhoFw6patag2gZcDjpugDLBStL&amp;index=15">Supporting Universal Dependencies in the Tree Editor TrEd - Jan Štěpánek, PhD.</a></li>
<li><a href="https://www.youtube.com/watch?v=jlnVfznTSOA&amp;list=PLA9_Hq3zhoFw6patag2gZcDjpugDLBStL&amp;index=32">ASGS - A Real-Time Operational Storm Surge Forecasting Framework - Brett Estrade, MS</a></li>
<li>Perl Cross-Compiler for Microcontrollers - Manickam Thanneermalai</li>
</ul>

<h1>Our Code of Virtues</h1>

<p>Codes of Conduct focus on vices and naively attempt to list all the banned behaviors. We focus on virtues and thus set the bar on behavior <strong>HIGH</strong>. Therefore, we do not have a Code of Conduct. We have a <strong>Code of Virtues</strong>—virtues that have been present and part of Perl from the very beginning.</p>

<p>The concept of virtues is very old, dating back to <em>Nicomachean Ethics</em>. Aristotle identified virtues as character traits that enable individuals to live a good life and achieve eudaimonia (flourishing or happiness). Examples include courage, temperance, and justice. Virtue was seen as the "golden mean" between two extremes (e.g., courage is the balance between recklessness and cowardice).</p>

<p>Aristotle’s virtues, such as courage, justice, and temperance, emphasize achieving a balanced and flourishing life through reason. These ideals directly influenced formal Christian virtues, particularly through St. Thomas Aquinas’ prolific writings, which integrated them with faith, hope, and charity as moral principles for spiritual growth. For example, the medieval codes of chivalry reflected this synthesis, urging knights to embody classical virtues like courage, meekness, humility, and compassion, as seen in their oaths to protect the weak and uphold justice.</p>

<p>Here we describe what Larry Wall meant in correct, virtuous, and perhaps chivalrous terms.</p>

<h1>The 3 Virtues of a Perl Programmer, Properly Defined</h1>

<p>Our Code of Virtues is made of the <strong>Three Virtues of a Perl Programmer</strong>, first elucidated by Perl's founder, Larry Wall. With a mind toward virtue, we define what he described in positive terms of virtue rather than the traditional words, which are actually vices.</p>

<h2>Practical Wisdom or Prudence, Not Laziness</h2>

<p>Unlike the vice of laziness, this virtue refers to practical wisdom or prudence. It involves the ability to make sound decisions and take appropriate actions based on understanding, experience, and ethical considerations. This aligns closely with Larry’s definition of laziness, summarized as:</p>

<blockquote>
  <p>"...the quality that makes you go to great effort to reduce overall energy expenditure. It makes you write labor-saving programs that other people will find useful and document what you wrote so you don’t have to answer so many questions about it."</p>
</blockquote>

<h2>Spiritedness, Not Impatience</h2>

<p>Accounting for the passionate aspect of human nature, this encompasses emotions like anger, righteous indignation, and the drive to achieve justice or excellence. Who among us has not experienced this in some form, particularly during heated online discussions? This aligns well with Larry’s definition of impatience, summarized as:</p>

<blockquote>
  <p>"...the anger you feel when the computer is being lazy. This makes you write programs that don’t just react to your needs but actually anticipate them—or at least pretend to."</p>
</blockquote>

<h2>Good Order, Not Hubris</h2>

<p>Far from harmful pride, this refers to maintaining good order and governance, both in societal contexts and personal conduct. Applied to programming, it signifies creating well-structured, organized, and maintainable code. This aligns well with Larry’s definition of hubris, summarized as:</p>

<blockquote>
  <p>"...the quality that makes you write (and maintain) programs that other people won’t want to say bad things about."</p>
</blockquote>
]]>
        

    </content>
</entry>



<entry>
    <title>Importance of Repositories in Public</title>
    <link rel="alternate" href="https://blogs.perl.org/users/mikko_koivunalho/2026/04/importance-of-repositories-in-public.html" />
    <id>tag:blogs.perl.org,2026:/users/mikko_koivunalho//2432.12031</id>
    <published>2026-04-24T00:39:44Z</published>
    <updated>2026-04-24T01:55:15Z</updated>
    <author>
        <name>Mikko Koivunalho</name>
        <uri>https://metacpan.org/author/MIKKOI</uri>
    </author>
    <category term="How-To" scheme="http://www.sixapart.com/ns/types#category" />
    <category term="Introduce-Package" scheme="http://www.sixapart.com/ns/types#category" />
    <category term="Musings" scheme="http://www.sixapart.com/ns/types#category" />
    <category term="distzilla" label="Dist::Zilla" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="distributionrelease" label="distribution release" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="dzil" label="dzil" scheme="http://www.sixapart.com/ns/types#tag" />
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/mikko_koivunalho/">
        <![CDATA[<p>It used to be so that a <strong>repository</strong> was only a place of work
and the <strong>distribution</strong> was the actual result of that work.
Only the contents of the distribution mattered. People would
read the files <code>README</code> and <code>INSTALL</code> from the distribution
after having downloaded it.</p>

<p>Not so anymore. Today the repository is out in the open in
<a href="https://www.github.com">GitHub</a>,
<a href="https://www.gitlab.com">GitLab</a>,
<a href="https://codeberg.org/">Codeberg</a>
or other shared hosting site.
On the other hand, the documentation in the distribution is often
discarded as distribution packages are rarely downloaded manually
but rather via a package manager which installs them automatically.</p>

<p>Publicly viewable repository has in fact become much more
than just a place of work. It is also an advertisement
for the project and of the community behind it, if there is
more than one author or contributor.</p>

<p>When a potential user first finds the project repository,
the hosting site commonly presents him with the project <code>README</code>
file. That makes <code>README</code> file in fact the <strong>welcome page</strong>
to the project. Its purpose is changed from being purely
informational to being an advertisement which
competes for user&#8217;s attention with bright colors,
animated pictures, videos and exciting diagrams, shapes
and &#8220;bumper stickers&#8221;.</p>

<p>But under all the exciting cover it must also remain
true to its nature: present the project
as precisely as possible and stay up to date with
its development.</p>

<p><code>README</code> might also not be the only file which needs
to be kept up to date because it is accessed in the (public) repository.
Other potential files can include
<code>INSTALL</code>, <code>Changes</code> and <code>CODEOWNERS</code>.</p>

<p>Many files therefore contain text which
must be updated at least at the time of release:
version numbers, API documentation, examples,
file lists.</p>

<p>It is difficult to keep these files in sync
with the code; just like documentation, which fact
every programmer knows. The <a href="https://metacpan.org/dist/Dist-Zilla">Dist::Zilla</a>
plugin <a href="https://metacpan.org/dist/Dist-Zilla-Plugin-WeaveFile">Dist-Zilla-Plugin-WeaveFile</a>
will prevent the files from falling out of sync
because their content is tested continuously.</p>

<p>There are other ways to do this, for instance
<a href="https://metacpan.org/dist/Dist-Zilla-Plugin-CopyFilesFromBuild">Dist::Zilla::Plugin::CopyFilesFromBuild</a>.</p>

<p>It is my philosophy that nothing in the repository
is changed <em>behind programmer&#8217;s back</em>.
It can also be dangerous to the programmer
if he is not a frequent Git committer.
Failed local tests are much safer.
And when the test fails, it is easy
to run <code>dzil weave</code> to update the files.</p>

<h1>Dist-Zilla-Plugin-WeaveFile</h1>

<p>The plugin <a href="https://metacpan.org/dist/Dist-Zilla-Plugin-WeaveFile">Dist-Zilla-Plugin-WeaveFile</a> works very much like my earlier plugin <a href="https://metacpan.org/dist/Dist-Zilla-Plugin-Software-Policies">Dist-Zilla-Plugin-Software-Policies</a>: it consists of three pieces: The Dist::Zilla command <code>weave</code>, the plugin <code>WeaveFile</code> which is used to define the configuration in <code>dist.ini</code> file, and the plugin <code>Test::WeaveFile</code> which creates tests for the distribution which check that the defined files exist and match their definition.</p>

<p>Example from <code>dist.ini</code> file:</p>

<pre><code>; Uses default config file .weavefilerc
[WeaveFile / README.md]

; Uses a custom config file and specifies file explicitly
[WeaveFile]
config = install-weave.yaml
file = INSTALL

[Test::WeaveFile]
</code></pre>

<p>And the definition file <code>.weavefilerc</code> would then contain, for example:</p>

<pre><code>---
snippets:
    badges: |
        [![CPAN](https://img.shields.io/cpan/v/My-Dist)](https://metacpan.org/dist/My-Dist)
    license: |
        # LICENSE
        [% USE date -%]

        This software is copyright (c) [% date.format(date.now, '%Y') %] by [% dist.author %].

        This is free software; you can redistribute it and/or modify it under
        the same terms as the Perl 5 programming language system itself.
files:
    "README.md": |
        [% snippets.badges %]

        # [% dist.name %] - [% dist.version %]

        [% dist.abstract %]

        [% pod("My::Module", "SYNOPSIS") %]
        [% pod("My::Module", "DESCRIPTION") %]

        [% pod("bin/myprog", "EXAMPLE") %]

        [% snippets.license %]
</code></pre>

<p>The templating system is <a href="https://metacpan.org/dist/Template-Toolkit">Template-Toolkit</a>.
I am planning to change this so that user can choose another templating system if wanted, and then Template-Toolkit will be optional to install. Also allowing to change the output format (currently Markdown) is in plans. All pod text is converted to Markdown.</p>

<p>With a configuration like the above, when user runs <code>dzil test</code>, if the static files <code>README.md</code> and <code>INSTALL</code> are not in sync with their definitions, user can run:</p>

<pre><code>dzil weave
</code></pre>

<p>or</p>

<pre><code>dzil weave README.md
dzil weave INSTALL
</code></pre>

<h1>Future</h1>

<p>There might be additional generated information which we will be forced - for practical reasons - to commit into the repository. <code>cpanfile</code> could be one such. GitHub repositories are being scanned by different AI tools which could draw benefit from having such information at hand, instead of being generated and only available in the distribution in <a href="https://metacpan.org/">MetaCPAN</a>. It does fight the principal of DRY, or, in this case &#8220;do not commit generated files&#8221; but it could be the lesser evil.</p>

<p>I have lately learned that <a href="https://devin.ai/">Devin, the AI software engineer</a> is being used to create summaries and presentations of GitHub repositories in <a href="https://deepwiki.com/">DeepWiki</a>. For an example of a Perl project, my <a href="https://deepwiki.com/mikkoi/env-assert">Env-Assert</a>.</p>
]]>
        

    </content>
</entry>



<entry>
    <title>AI as a Chance - Opinion</title>
    <link rel="alternate" href="https://blogs.perl.org/users/petamem/2026/04/ai-as-a-chance.html" />
    <id>tag:blogs.perl.org,2026:/users/petamem//1658.12030</id>
    <published>2026-04-23T16:17:26Z</published>
    <updated>2026-04-23T16:50:01Z</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="ai" label="AI" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="community" label="Community" 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/petamem/">
        <![CDATA[<p>Use AI. Use it more and better. If you are not yet equipped to use it
well - that is fine, learning takes time - but please do not inhibit
those in the community who are.</p>

<p>That is the whole argument. The rest of this piece is why I think it
is correct, and why I think the current register of the Perl community
around this topic is costing us something specific and avoidable.</p>
]]>
        <![CDATA[<h2>Who is saying this, and why that matters</h2>

<p>PetaMem, an AI company, was founded in 2001. I ran it - still do.
My MSc is from an AI department. I was doing neural networks in
the 1990s, when the field was still in one of its winters and the word
"AI" was something one did not put on a grant application. I have not
stopped working on this for thirty years.</p>

<p>Which makes me exactly the kind of person whose opinion on AI should be suspected of motivated reasoning. Someone who has built a career on a technology has an obvious incentive to tell you it is good. You are safe, because I have spent most of my adult life living with a particular internal watchdog that distrusts convenient conclusions - the INTJ tendency to run every thought through a "is this actually right?" pass before letting it speak (even then it does not stop). For the record: I don't share the fear of AI that many seem to feel - won't pretend I do.</p>

<p>In 2012 I gave a keynote at the London Perl Workshop about "Perl Strategy". You know: how to make Perl great again. It resulted in the
formation of Propaganda.pm, an effort aimed at improving Perl's public
standing and institutional visibility. I ceased effort with
Propaganda.pm around 2017, for lack of enthusiasm from the
community. No worries: I am not restarting it - this piece is not a
campaign. It is one text, offered once, because something specific has
changed since 2017 - the arrival of a technology that alters the
arithmetic of what a small group of people can do - and the
implications matter for Perl in particular.</p>

<h2>What has changed</h2>

<p>The rate of capability improvement in general-purpose AI over the last
eighteen months is not the rate most people have updated their priors
at. Scepticism acquired six months ago is already out of date. What is
worse, that calcification biases your judgment in the present: the
model that frustrated you then has been replaced - possibly more than
once - by something that behaves differently, fails differently,
succeeds differently, and wants different things from its user. You
are evaluating today's capability through a lens shaped by a version
that no longer exists. Updating in the face of rapid change is the
only honest response to it.</p>

<p>"AI slop" is real. Bad AI-generated code exists and is ubiquitous. But
if you look carefully at where the slop comes from, the pattern is
almost always the same: the failure is not in the model. The failure
is in the interface between the model and the task - what was asked,
how it was asked, how the context was prepared, how the output was
reviewed, what was done with it afterward. A skilled operator and an
unskilled operator, using the same model on the same task, produce
radically different output. We have all the evidence we need that this
is true; we just do not often name it, because naming it is
uncomfortable. "Slop" is, in the great majority of cases, a
description of a process rather than the product of this technology.</p>

<h2>The scale nobody is quite talking about</h2>

<p>Here is an observation I find more interesting than any
benchmark. When I ask an AI to estimate how long a task will take, the
estimates it produces are clearly calibrated on training data from
human developer estimates - "three weeks", "a month", that sort of
thing. I have found, repeatedly and consistently, that those estimates
are off by a factor that lives somewhere in the ballpark of a
thousand. Give or take. The AI is, in a literal sense, unable to see
its own impact. It is estimating how long it would take a human
without AI to do the work, because that is what its training data
contains.</p>

<p>I am not sure people have fully internalised what this means. The
models cannot currently estimate the productivity of humans using
them, because the training data for that does not yet exist at
scale. Which means that even the people who use AI every day are
operating with intuitions that were formed before the current
capability existed, and which the AI itself cannot correct. The
feeling of "this took me three days" and the reality of "this
represents three months of pre-AI work" can coexist in the same person
without the person noticing the gap.</p>

<p>What this adds up to, for someone who has learned to use the tool well
and who picks the right problems to aim it at: the output of a single
individual can easily exceed what was previously the output of a
medium-sized development team. My current conservative estimate of my
own output relative to a standard human-only development team is fifty
to one hundred. This is not a claim about me specifically. It is a
claim about what the ceiling is, for any individual developer who
applies themselves to learning the craft of AI-assisted work. The
ceiling was not this high eighteen months ago. It is this high now.</p>

<p>You do not have to take my word for this. The 575-commits-in-a-week
event that has been discussed recently in the Perl community is an
instance of the same phenomenon. The community has seen the
output. The response has mostly been to focus on fifteen or twenty
commits out of the 575 that were weak. I want to come back to that
response below.</p>

<h2>A different job, not a faster one</h2>

<p>It is tempting to describe what changes with AI as "becoming a faster
developer". That is the wrong frame. The metaphor I keep coming back
to is a naval one.</p>

<p>In the old ecosystem, programmers were sailors. You made sure the
bulkheads were closed, the galley was filled, the deck was
swabbed. You were good or bad at your job depending on how well you
pulled the ropes. With AI used well, you stop pulling ropes. The ropes
get pulled. You become, if you choose to, an admiral - someone who
orders a fleet to take a strategic position around a group of isles,
who makes directional choices, who reviews outcomes, who
integrates. You are not doing the old job faster. You are doing a
different job.</p>

<p>This has implications that are worth sitting with. Experience and
judgment matter more in the admiral's job than in the sailor's job,
not less. The admiral's chief skill is knowing what ought to be done,
what a good result looks like, and when the fleet's output is
off-course. That is exactly the skill a senior developer has
accumulated. The replacement narrative - "AI will take our jobs" -
gets the transition backwards. The people whose jobs are most at risk
are the ones whose work consists of pulling ropes that AI can now
pull. The people whose value goes up are the ones with enough
experience to direct the fleet. If you have been in this profession
for ten or twenty years, the capability that has just arrived is not
your threat. It is your lever.</p>

<h2>The fear, named accurately</h2>

<p>I want to talk about what is happening in the community around AI,
because I think we are having the wrong debate.</p>

<p>Quality concerns about AI-generated code are legitimate. There is bad
AI-generated code. There are real code-review cases where AI-produced
output made a codebase worse. People evaluating individual
contributions on their merits and finding them wanting are doing the
work that code review is supposed to do. None of what follows
contradicts any of that.</p>

<p>But the debate in the community is not, structurally, a debate about
quality. It has a specific shape. Out of 575 commits, fifteen or
twenty bad ones get selected and made representative of the
whole. That selection is not a quality evaluation. No dispassionate
evaluator, trying to characterise a body of work, would pick from the
worst 3% and treat it as the norm. The consistency of this pattern -
across many individuals, many contexts, always selecting in the same
direction - tells us that something other than quality evaluation is
happening.</p>

<p>Consider how we handle this in the case of human developers. We have
all encountered good developers and bad developers. Some
human-produced code is excellent; some is a disgrace. We do not, on
the basis of the disgraceful code, conclude that human developers as a
class should be treated with suspicion. We evaluate individuals on
their individual output. We extend to other humans, by default, the
courtesy of being judged on their specific work in its specific
context.</p>

<p>The same courtesy is not being extended to AI-assisted contributions
in the current community register. A class-wide judgment is being
drawn from a selected subset of output, in a way we would recognise as
unfair if applied to any other category of contributor. The
inconsistency is not subtle. And the consistency of the
inconsistency - across many people, many threads, many contexts,
always in the same direction - suggests that what is being expressed
is not an evaluation. It is something else.</p>

<p>The honest name for that something else, in most cases, is fear of
obsolescence. It is not shameful. It is one of the most predictable
human responses to rapid capability change. But fear dressed as a
quality argument is not an argument, and treating it as one means we
are debating a simulation of the real disagreement.</p>

<h2>The cost</h2>

<p>A community that spends its AI debate in this form is a community not
spending that time learning the tool. The members who are already
skilled at using it do not pay the cost - they adapted, they moved on,
they are building things. The cost is paid by the members who are not
yet skilled and who are not receiving social permission from their
peers to become skilled in public. In a community where the dominant
register around AI is suspicion, experimenting with AI publicly is
socially expensive. That expense is a tax on exactly the learning the
community most needs to be doing.</p>

<p>A management mentor of mine once told me: "I have never seen a company
go bust because it had to. All the companies I have seen go bust did
so because they committed suicide." At the time I thought he was
wrong - surely external circumstances, markets, competition. He was
not wrong. Every declining community I have watched up close has
declined by choice, one quiet decision at a time, each decision
looking reasonable in isolation, the collective arithmetic only
visible in retrospect. The companies my mentor described did not vote
to commit suicide. They just kept making small reasonable-seeming
choices that added up to it.</p>

<p>The Perl ecosystem has been on a cooling trajectory for a long
time. The exponential drop is far behind us; we are in the long tail
now, and the long tail may persist for a long time. Like a white
dwarf, still radiating, nowhere near its former brightness. This is
not a eulogy. White dwarfs are stars. They persist. What they do not
do is get brighter on their own, without something changing the
equations they are operating under.</p>

<p>AI is a change in the equations. It is the largest change in the
equations of software development in the lifetime of anyone reading
this. It is also a change that rewards exactly the kind of judgment
that an experienced community has accumulated. The ecosystem problem
of Perl - and I say this with affection, having worked on it for
decades - is fundamentally a problem of hands. There are not enough of
us to maintain what exists, let alone to expand it.</p>

<p>A community that arrives at this change and chooses to spend its
collective time on the 3% that is bad, rather than on the 97% that
demonstrates what is now possible, is a community making the small
reasonable-seeming choice that my mentor described. And if the pattern
continues, the arithmetic will do what it does.</p>

<p>The tools are here. The question is whether we embrace them.</p>

<p>My answer is hereby on record.</p>

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



<entry>
    <title>AI Contributions to CPAN: The Copyright Question</title>
    <link rel="alternate" href="https://blogs.perl.org/users/todd_rinaldo/2026/04/ai-contributions-to-cpan-the-copyright-question.html" />
    <id>tag:blogs.perl.org,2026:/users/todd_rinaldo//1762.12029</id>
    <published>2026-04-22T15:55:52Z</published>
    <updated>2026-04-22T16:14:23Z</updated>
    <author>
        <name>Todd Rinaldo</name>
        
    </author>
    <category term="AI" scheme="http://www.sixapart.com/ns/types#category" />
    <category term="cpan" scheme="http://www.sixapart.com/ns/types#category" />
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/todd_rinaldo/">
        <![CDATA[<p><em>This is a developer's perspective, not legal advice. I'm not a lawyer. What follows is my personal reading of publicly available licenses, policy documents, and one court decision. If you're making decisions about your module's legal exposure, talk to a lawyer.</em></p>

<p>The open source world is actively debating whether to accept AI-assisted contributions. QEMU, NetBSD, and <a href="https://wiki.gentoo.org/wiki/Project:Council/AI_policy">Gentoo</a> have said no. A lot of CPAN maintainers haven't written down a policy but have a reflex &mdash; if a PR looks AI-assisted, close it without a real review.</p>

<p>Two very different concerns sit underneath that reflex, and they usually get mashed together:</p>

<ul>
<li><strong>Quality.</strong> AI can produce plausible-looking code that doesn't actually work, hallucinates APIs, or subtly misunderstands the problem. This wastes reviewer time.</li>
<li><strong>Copyright.</strong> AI might reproduce memorized training material the contributor has no right to relicense. The output itself may not even be copyrightable. The contributor may not actually own what they're submitting.</li>
</ul>

<p>This article is about the second one. The quality concern is real, but you already know how to handle it &mdash; you read the PR, you run the tests, you ask questions. It's the same concern you have about any contributor. The copyright concern is the structurally novel one, and it's the question where CPAN's particular history matters.</p>

<p>I maintain CPAN modules, and for a while I've been running AI-assisted modernization tools (<a href="https://blogs.perl.org/users/todd_rinaldo/2026/04/575-pull-requests-in-three-weeks-what-happens-when-ai-meets-cpan-maintenance.html"><tt>koan</tt></a> is one example of the category) against the modules I care for. Across the <a href="https://github.com/cpan-authors">cpan-authors GitHub organization</a> &mdash; a collection of repositories where many CPAN modules are collaboratively maintained &mdash; hundreds of those PRs have been merged, every one with a human in the loop: a maintainer reading the diff, running the tests, and taking full responsibility. My argument isn't that nothing has ever been wrong. It's that the copyright concerns people raise about AI-assisted contributions are not a new kind of concern for CPAN. They are the same concern CPAN has always carried, under a different name.</p>

<p>The reflex to reject AI-assisted PRs on copyright grounds quietly smuggles in an assumption: that human-authored CPAN contributions came with a clean bill of provenance, and AI is the thing threatening to pollute the water. That assumption does not survive five minutes of honest thought about how CPAN actually works.</p>

<h2>What CPAN actually verifies</h2>

<p>CPAN's rule for accepting new code is, in full: the author uploads a tarball, declares a license, and we trust them. Many modules pick "same terms as Perl," but plenty use Artistic 2.0, MIT, Apache 2.0, or something else &mdash; and a surprising number ship with an ambiguous or missing license line.</p>

<p>There's no <a href="https://developercertificate.org/">DCO</a>. There's no <a href="https://en.wikipedia.org/wiki/Contributor_License_Agreement">CLA</a>. PAUSE confirms that you are who you say you are. It does not confirm that you own what you just uploaded. When the license is "same terms as Perl" &mdash; still the most common declaration on CPAN &mdash; it points to a moving target: Artistic 1.0 plus GPL 1-or-later. That pair has been tested in U.S. court basically once, in <em>Jacobsen v. Katzer</em> (Fed. Cir. 2008), and never that I know of outside the U.S. The part of Artistic that reliably works is the warranty disclaimer &mdash; and every common CPAN license (Artistic 2.0, MIT, Apache 2.0, GPL) has a disclaimer of its own. Those disclaimers appear, to me, to be doing most of the real legal work for CPAN today.</p>

<p>If you've maintained a module for a while, you already know the quiet risks sitting in the index:</p>

<ul>
<li>Code an employer actually owns, uploaded by an employee who never got permission. Some of this has been in widely-used distributions for decades.</li>
<li>Code copied from a book, a paper, or a reference implementation, with the original copyright never mentioned.</li>
<li>Modules ported from another language without telling &mdash; or asking &mdash; the original author.</li>
<li>Abandoned modules whose authors can't be reached and whose ownership chain is, in practice, lost.</li>
<li>License tags like "same terms as Perl 5.6.1" that point at a license text nobody has read in twenty years, and whose present-day legal force I personally wouldn't want to bet on.</li>
</ul>

<p>None of this has blown up into a real copyright fight. That's not because CPAN is clean. It's because the licenses CPAN actually uses &mdash; Artistic/GPL, Artistic 2.0, MIT, Apache &mdash; are all permissive enough that, as I read them, a plaintiff would have a hard time showing they lost anything. It's because every one of those licenses comes with a warranty disclaimer that blocks most ways of suing in the first place. And it's because the culture of the ecosystem does not go looking for that kind of fight.</p>

<p>This is not a knock on CPAN. It's the cost of a thirty-year-old volunteer archive that chose breadth over lawyering. The trade has held up fine.</p>

<p>But it's the baseline you need to admit before you reject an AI-assisted PR on sight, on copyright grounds alone.</p>

<h2>AI is not a new kind of copyright risk</h2>

<p>Once you look honestly at how CPAN handles contributions, the AI copyright debate stops being about whether to let something dirty into a clean system. It becomes about whether to add one more flavor of provenance uncertainty to a system that has always run on several.</p>

<p>The copyright concerns people raise about AI are real:</p>

<ul>
<li>The model might reproduce copyrighted code it was trained on, without attribution.</li>
<li>Parts of the output may not be copyrightable at all, which weakens the contributor's ability to license it to you.</li>
<li>The contributor may not have a clear legal claim to what they're granting.</li>
</ul>

<p>Now compare those to the copyright concerns that have always been there for CPAN:</p>

<ul>
<li>The contributor's employer might own the code under a work-for-hire arrangement and never agreed to release it.</li>
<li>Part of the module might be a near-copy of a GPL-incompatible reference implementation from a textbook or another project.</li>
<li>Ported-from-another-language code may have the original author's copyright still attached to it in ways the contributor never addressed.</li>
<li>Abandoned modules may have copyright holders who are unreachable and whose heirs could, in theory, assert rights tomorrow.</li>
</ul>

<p>It's the same shape. We're not sure the human who signed for this code had the legal authority to grant the license they claimed, and if they didn't, we have no efficient way to find out. The source of that uncertainty has changed &mdash; it used to be a textbook or an employer, now it might also be a training set &mdash; but the failure mode is the same, and the way CPAN handles it is the same: the human takes responsibility, the warranty disclaimer catches what falls through, and the permissive license protects downstream users.</p>

<p>That's why the pragmatic camp in open source &mdash; Linux kernel, Red Hat, Apache, GitLab, OpenInfra &mdash; has landed where it has: allow AI-assisted contributions with commit-trailer disclosure (GitHub-native projects commonly use <tt>Co-authored-by:</tt>; the kernel uses <tt>Assisted-by:</tt>) and full human responsibility. The strict-ban camp &mdash; QEMU, NetBSD, Gentoo &mdash; quietly assumes the pre-AI baseline was clean. For projects with DCOs and CLAs, that assumption is at least defensible. For CPAN, it isn't.</p>

<h2>What actually changes: volume</h2>

<p>There is one thing AI genuinely changes about the copyright picture, and it's the thing every CPAN maintainer should care about: <strong>volume</strong>.</p>

<p>CPAN's background rate of problematic code &mdash; borrowed from a book, copied out of work-for-hire, lifted from an incompatible-license reference implementation, and now potentially reproduced from a model's training set &mdash; has never been zero, and systematic provenance review has never been the norm on CPAN. What AI changes is that one contributor with a decent model and a decent tool can produce PRs at a rate no individual ever could before. Ship more, roll the dice more.</p>

<p>That's the change worth naming. Not a new kind of risk. Just more throws of the same dice &mdash; which justifies some proportional vigilance, not a categorical rejection.</p>

<h2>How to actually evaluate an AI-assisted PR</h2>

<p>When an AI-assisted PR lands on your module, the quality checks are the ones you already know how to run &mdash; does the change make sense, do the tests pass, does the style match the repo, does the contributor engage when you ask questions. None of that is AI-specific.</p>

<p>The copyright-specific checks are small and cheap:</p>

<ul>
<li><strong>Look for the obvious copy tells.</strong> Anything with copyright headers, SPDX tags, or author-name strings in it should bounce &mdash; those are signs of memorized training material. A quick GitHub search for any distinctive identifier or string literal will catch the worst cases. This scales with the volume.</li>
<li><strong>Expect disclosure.</strong> Ask contributors to mark AI-assisted commits with a <tt>Co-authored-by:</tt> trailer &mdash; the standard GitHub convention, and one Red Hat names alongside <tt>Assisted-by:</tt> and <tt>Generated-by:</tt> as an acceptable option in its <a href="https://www.redhat.com/en/blog/ai-assisted-development-and-open-source-navigating-legal-issues">Nov 2025 analysis</a>. The point is transparency: a reader of the log should be able to see which commits had AI help. Only humans should appear in <tt>Signed-off-by:</tt> lines; an AI has no standing to certify the DCO.</li>
<li><strong>Hold the human accountable, not the tool &mdash; and be clear about which human.</strong> Who is asserting the license grant depends on the workflow. When an external contributor opens a PR, they are the one claiming the right to license their submission; the merge accepts that claim. When you are running an AI tool yourself against modules you maintain, the assertion and the acceptance collapse into a single merge click, and you are the accountable human on both sides. Third-party AI-assisted PRs sit in the normal contributor-plus-maintainer model; self-operated tools compress it. Either way, a human with legal standing is accountable somewhere. If you can't explain what you're merging &mdash; because you didn't review it, or because the contributor can't explain it either &mdash; don't merge it.</li>
</ul>

<p>The difference between a PR you merge and one you reject, on copyright grounds, isn't whether a model was involved. It's whether a human with legal standing to grant the license took responsibility for doing so &mdash; at submission, at merge, or both. That test is the same test CPAN has been running, implicitly, for thirty years.</p>

<h2>One argument to retire</h2>

<p>"CPAN was in the training data, so AI output is CPAN-compatible." Let it go. We don't know what's in Claude's training data, or GPT's, or anyone else's. We do know these models have read Perl from all over GitHub (including incompatible licenses), from Stack Overflow (CC BY-SA), from books, blogs, and anywhere else crawlable. "Trained on CPAN" is a comforting story, but we can't confirm it, and resting an argument on it makes the argument weaker, not stronger.</p>

<p>The honest claim is smaller and more useful. The contributor has reviewed the output. The contributor is accountable. The warranty disclaimer catches the residual risk the same way it has for three decades. That is what the kernel, Red Hat, Apache, and OpenInfra policies all quietly say. It's the claim you can defend &mdash; and it holds whether the tool in the contributor's hand is Claude, GPT, a generous colleague, or a textbook.</p>

<h2>Closing</h2>

<p>The copyright concern with AI-assisted contributions is real, but it isn't new. CPAN has been running on the same legal structure for thirty years &mdash; permissive licenses, warranty disclaimers, contributor accountability &mdash; and that structure handles AI-assisted contributions the same way it handles every other kind of provenance uncertainty CPAN has quietly carried since the beginning. The risk profile is the same. Only the volume is new.</p>

<p>Do the copyright-specific checks when the volume warrants them. Look for the obvious copy tells. Ask for <tt>Co-authored-by:</tt> disclosure. Hold the contributor accountable for the license grant they're making. Trust the warranty disclaimer that's been doing the heavy lifting the whole time.</p>

<p>And update your own internal picture of CPAN while you're at it: it was never a clean room on provenance, and pretending otherwise was always a story we told ourselves.</p>

<hr>]]>
        
    </content>
</entry>



<entry>
    <title>PDL in Rust -- Part Two</title>
    <link rel="alternate" href="https://blogs.perl.org/users/petamem/2026/04/rust-pdl-part-two.html" />
    <id>tag:blogs.perl.org,2026:/users/petamem//1658.12028</id>
    <published>2026-04-19T20:01:04Z</published>
    <updated>2026-04-20T02:17:50Z</updated>
    <author>
        <name>PetaMem</name>
        <uri>http://www.petamem.com</uri>
    </author>
    <category term="PDL" scheme="http://www.sixapart.com/ns/types#category" />
    <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><br />
<img alt="Logo.png" src="https://blogs.perl.org/users/petamem/2026/04/05/petamem/Logo.png" width="102" height="78" class="mt-image-none" style="" /></p>

<p><br />
<p>Two weeks ago we posted <em>"PDL in Rust - A Native<br />
Reimplementation of the Perl Data Language"</em>. At the time the<br />
score was 45 tests, all green. That was enough to say "it compiles,<br />
it runs, here is the arithmetic surface." It was not enough to say<br />
"you can use this."</p></p>

<p>We are now on the second number. The current PDL implementation
in <a href="https://perl.petamem.com">pperl</a> covers roughly 3,000
assertions end-to-end: about 1,400 on the Perl-facing connector side
and about 1,600 on the engine side. As of this writing roughly 98%
of the connector assertions match upstream PDL 2.103 exactly, and
most of the remaining couple of dozen we already know why they
fail. By the time you read this the numbers will have drifted a
little in our favour - give or take - but the shape is the point,
not the decimal.</p>

<p>Everything described below is shipping, today, at
<a href="https://perl.petamem.com">https://perl.petamem.com</a>.</p>]]>
        <![CDATA[<h3>What Part One Was</h3>

<p>Part One was a flag-planting exercise. Someone on Reddit asked
whether pperl would support PDL; we said yes; we then had to
actually do it. Forty-five tests covering construction, arithmetic,
reductions, a handful of transcendentals, and operator overloading.
The point was to prove the mechanism - that a pure-Rust engine
behind a pperl native module could stand up to Perl-side code that
expects `use PDL; my $x = pdl([1,2,3]);` to just work. It did. But
forty-five tests is not a usable PDL.</p>

<p>This post is about what happened when we turned the heat up.</p>

<h3>What's in the Box</h3>

<p>The Perl-facing module hierarchy now mirrors upstream PDL's own,
file-for-file where it matters:</p>

<pre>
PDL              - top-level boot, exports pdl/zeroes/ones/...
PDL::Core        - constructors, accessors, DESTROY, magic
PDL::Ops         - arithmetic, comparison, bitwise, unary
PDL::Ufunc       - reductions, sorting, statistics
PDL::Math        - trig, hyperbolic, Bessel, erf, special functions
PDL::Primitive   - matmult, which, where, clip, append, convolve
PDL::Slices      - slice, xchg, mv, reshape, clump, dummy, range
PDL::Basic       - sequence, xvals, yvals, zvals, rvals, linvals
PDL::MatrixOps   - det, inv, LU, eigens, trace, norm
PDL::Bad         - bad-value flags, setbadat, copybad, locf
PDL::FFT         - Cooley-Tukey radix-2 FFT/IFFT
PDL::NiceSlice   - source filter: $x(:,0) syntax
PDL::Lite        - lightweight import variant
PDL::LiteF       - lightweight import with fewer functions
</pre>

<p>Fifteen data types. Broadcasting. Bad values. Dataflow. Affine
slicing. Storable round-trip. FFT. 2D image convolutions. Matrix
decompositions. Source-filter support for PDL::NiceSlice's
<code>$x(:,0)</code> syntax. Operator overloading for ~40
operators. It is PDL, not a demo subset. Whether it walks quite
like the grown-up yet is for the user to judge.</p>

<h3>The Connector, in One Example</h3>

<p>Part One described the split at a high level - a standalone Rust
crate for the engine, a pperl native module for the bridge. This
time let us walk one operation end to end, because the connector is
where the Perl audience will want the detail.</p>

<p>Consider <code>$A x $B</code>, the matrix-multiply overload:</p>

<pre class="prettyprint lang-perl">
use PDL;
my $A = pdl([[1,2],[3,4]]);
my $B = pdl([[5,6],[7,8]]);
my $C = $A x $B;
</pre>

<p>Here is what the <code>x</code> overload looks like on our side.
The handler is a plain Rust function registered into the PDL stash
at boot. No <code>use overload</code> block, no BEGIN-time aliasing,
no XSUB wrapper file:</p>

<pre class="prettyprint lang-rust">
// At boot, overload entries go straight into the PDL stash.
let overloads: &[(&[u8], unsafe extern "C" fn(*mut CV))] = &[
    (b"(+\0",    xs_plus_overload),
    (b"(*\0",    xs_mult_overload),
    (b"(x\0",    xs_matmult_overload),   // matrix multiply
    // ~40 more operators …
];
for &(name, func) in overloads {
    newXS(/* PDL::(X */, Some(func), file);
}
</pre>

<p>The handler itself extracts the PDLs from their SVs, validates
dimensions, calls into the engine, wraps the result:</p>

<pre class="prettyprint lang-rust">
unsafe extern "C" fn xs_matmult_overload(_cv: *mut CV) {
    let mark  = xs_pop_mark();
    let items = xs_items(mark);
    let (a_sv, b_sv, _swap) = binop_args(mark, items);
    xs_sp_set(mark);
    let mut a_temp = false;
    let mut b_temp = false;
    let a = sv_to_pdl_or_scalar(a_sv, &mut a_temp);
    let b = sv_to_pdl_or_scalar(b_sv, &mut b_temp);
    let a_dims = core_vtable::pdl_dims(a);
    let b_dims = core_vtable::pdl_dims(b);
    if a_dims[0] != b_dims[1] {
        Perl_croak(b"PDL: matmult: dimension mismatch\0".as_ptr() as *const c_char);
    }
    let c = core_vtable::pdl_alloc_output();
    // --- THE ENTIRE DISPATCH ---
    // One call into Rust-PDL. No XS wrapping, no interpreter
    // round-trip, no hidden allocations.
    let err = rust_pdl::primitive::pp_matmult
                 ::pdl_run_matmult(a, b, c);
    if a_temp { core_vtable::pdl_destroy(a); }
    if b_temp { core_vtable::pdl_destroy(b); }
    xs_push(sv_2mortal(pdl_to_sv(c)));
}
</pre>

<p>Three things about this example are worth drawing out for a
Perl audience.</p>

<p><strong>The overload dispatch is a function-pointer table.</strong>
<code>$A x $B</code> resolves as: stash lookup → function-pointer
call → Rust. Upstream PDL's equivalent path is: <code>use overload</code>
table lookup → dispatch to a Perl sub → XSUB boundary → PP-generated C
→ inner kernel. pperl removes the two middle layers entirely, because
the handler <em>is</em> the kernel-caller, compiled into the interpreter
binary.</p>

<p><strong><code>sv_to_pdl</code> is a magic walk, not a method call.</strong>
PDL SVs in pperl store the engine's <code>*mut c_pdl</code> as
<code>PERL_MAGIC_ext</code> on the inner SV - the same mechanism Perl
itself uses for tied variables, dualvars, and a dozen other features.
Extracting the pointer is a flag check plus a linked-list walk. No
method dispatch, no <code>can()</code>, no string comparisons:</p>

<pre class="prettyprint lang-rust">
let flags = SvFLAGS(inner);
if flags & (SVs_GMG | SVs_SMG | SVs_RMG) != 0 {
    let mut mg = (*sv_any).xmg_u.xmg_magic;
    while !mg.is_null() {
        if (*mg).mg_virtual == &PDL_MAGIC_VTBL {
            return (*mg).mg_ptr as *mut c_pdl;  // ← the c_pdl pointer, raw
        }
        mg = (*mg).mg_moremagic;
    }
}
</pre>

<p>So <code>$A->at(0)</code> in pperl is: one stash method lookup, one
magic walk, one pointer-arithmetic offset inside a Rust function. We
did not invent a new mechanism for binding Rust objects to Perl
variables - we reused Perl's own magic infrastructure, because that is
what it is for.</p>

<p><strong>Type promotion: upstream's rule, reimplemented from scratch.</strong>
Matrix multiply with mixed types - <code>long x float</code> - must
promote to double for numerical stability. Upstream's rule is "an NV
scalar forces minimum promotion to double; an NV PDL keeps its wider
type if already ≥ double." We ported the rule verbatim; the
implementation is thirty lines of Rust match arms. No C preprocessor
switch tables. No <code>.xs</code> file. No PDL::PP code-generation
templates. When we discovered that pperl was keeping <code>float</code>
instead of promoting to <code>double</code> for <code>float + 0.2</code>
- the fix was two match arms, one rebuild, twenty-five tests
recovered. In the upstream world the equivalent change would touch
PDL::PP's templates and trigger a rebuild of forty op files.</p>

<h3>pd2multi - We build a Compiler</h3>

<p>Upstream PDL has PDL::PP: a macro/template processor that weaves
<code>.pd</code> definitions with C fragments and hands the output to
<code>cc</code>. It has served PDL for thirty years. It works. But
the output is always C, and it is a text-substitution engine, not
a compiler.</p>

<p>We could not extend it. We needed something that could emit Rust
today, and potentially other targets later - a C backend to validate
against upstream's own output, a GPU backend for OpenCL, a Perl
backend to let pperl's JIT see through the operation into the
surrounding loop. So we built a proper parser → AST → emitter
pipeline. <code>pd2multi</code> ingests the same input language,
builds an explicit intermediate representation, and delegates output
to pluggable backend emitters.</p>

<p>This is internal infrastructure. Nothing user-facing ships from
it yet; it is the scaffolding under the engine. It is roughly two
orders of magnitude more work than the template-substitution
approach it replaces. The payoff is optionality: when we want to add
a GPU backend, it is one more emitter walking the AST, not a new
compiler.</p>

<h3>Correctness</h3>

<p>The connector test suite runs every assertion against both
upstream perl5+PDL and pperl+Rust-PDL, compares outputs, and
categorizes each result. A representative snapshot from the harness
at the time of writing:</p>

<pre>
Tests: ~1380 matched, ~25 mismatched, ~110 extra / ~1400 total
</pre>

<p><strong>Matched</strong> - the large majority, give or take:
pperl produces the same output as upstream.</p>

<p><strong>Mismatched</strong> - on the order of two dozen: pperl
differs from upstream. Most of these are real work in progress and
we know where the gaps are. The biggest cluster is in forward-flow
dataflow - when a parent PDL is mutated, downstream slice children
should see the update eagerly. Our implementation is currently
lazy, which catches most cases via dirty-flag refresh but misses
the paths that read child data without going through the normal
accessor. A handful of tests in 220-broadcasting fail on that. The
rest are distributed drift: error message text, one edge case in
<code>unbroadcast</code>, STL round-trip issues. None are
conceptual; all are bounded.</p>

<p><strong>Extra</strong> - around a hundred: tests that pperl
passes and upstream does not. Some of these are test-surface
coverage - we wrote more thorough tests for overload dispatch,
import semantics, and native-function dispatch than upstream did.
Others are legitimate behavioural differences where we produce a
correct result and upstream does not; the precision and diagnostics
subsections below cover the interesting cases.</p>

<p>There is a separate, smaller number worth singling out.</p>

<h4>Where pperl and Upstream Disagree, and pperl is Right</h4>

<p>A handful of tests produce different results between the two
implementations where pperl's result is more correct. Six in the
first sweep; we are no longer surprised when we find another. Three
patterns are worth naming.</p>

<p><strong>Precision.</strong> <code>pctover</code> computes the
k-th percentile along a dimension:</p>

<pre class="prettyprint lang-perl">
my $x = pdl(0,0,6,3,5,0,7,14,94,5,5,8,7,7,1,6,7,13,10,2,101,19,7,7,5);
my $r = $x->pctover(0.9);
printf "%.20f\n", $r->at();
</pre>

<p>Upstream says <code>17.00000000000000710543</code>. pperl says
<code>17.00000000000000000000</code>. Both stringify to <code>17</code>,
so to the eye they are identical. But <code>$r->at() == 17</code>
is <em>false</em> on upstream (the 7e-15 drift catches the numeric
comparison) and <em>true</em> on pperl. The test was written as a
plain <code>== 17</code> assertion, assuming the kernel would
return an exact integer.</p>

<p>Why does pperl get it right? Same numerical recipe, different
float accounting. Upstream's PP-generated C coerces intermediates
into <code>PDL_Double</code> early and rounds once per sample. The
Rust kernel, passing through LLVM's optimizer, keeps a wider
accumulator and rounds once at the final divide. It is not
cleverness on our side - it is what the modern toolchain does with
the same numerical recipe when nothing in the code forces early
narrowing. The same pattern appears in <code>xlogvals</code> and a
couple of other reductions.</p>

<p><strong>Perl context.</strong> <code>lgamma</code> returns two
values: the log-gamma value, and the sign. Upstream's PP-generated
XS returns both as a two-element list regardless of context. In
scalar context, Perl takes the last element - so
<code>my $y = lgamma($x)</code> assigns the <em>sign</em> (always
<code>1</code>), not the actual gamma. That is not what a Perl
programmer expects. pperl respects <code>GIMME_V</code>: scalar
context returns the value, list context returns the pair.</p>

<p>This is not a numerical-methods win. It is a Perl-semantics win
- the kind that only a Perl-aware reimplementation can notice. We
didn't set out to behave differently from upstream; we set out to
behave the way Perl specifies.</p>

<p><strong>Diagnostics.</strong> <code>pdl("nonsense blurb")</code>
should reject its input. Upstream does reject it - but the error
message reads <code>found 'e' as part of a larger word in nonsense
 blurb</code>, because the parser is reporting that it saw
 <code>e</code> (Euler's constant) embedded in a longer token, and
that is what it complains about. pperl reports <code>found
 disallowed character(s) 'nonsense,blurb'</code>, which is what an
actual user wanted to know. A smaller case of the same pattern:
<code>$x->slice('0:-14')</code> on a 10-element PDL silently
returns a garbage view upstream; pperl croaks with an out-of-bounds
error. Good diagnostics are a correctness property.</p>

<p>And in the interest of honest accounting: we have also found
places where our implementation was wrong and upstream was right,
and we conformed. The <code>float + 0.2</code> promotion rule
mentioned earlier was one of those: we were returning 
<code>float</code>, upstream correctly promoted to <code>double</code>,
we fixed ourselves to match. The flow of corrections is
bidirectional and that is the point - two independent
implementations of the same spec are a stronger guarantee of
correctness than either one alone.</p>

<h3>Performance</h3>

<p>The startup story from Part One holds and has tightened up. Here
is the harness output from the current connector test suite, picking
a representative sample:</p>

<pre>
PDL/010-constructor.t     ... ok  (17/17)   perl5: 84ms  / pperl: 9ms
PDL/110-core.t            ... ok  (121/121) perl5: 88ms  / pperl: 13ms
PDL/120-ops.t             ... ok  (75/75)   perl5: 94ms  / pperl: 12ms
PDL/150-primitive.t       ... ok  (60/60)   perl5: 86ms  / pperl: 11ms
PDL/190-matrixops.t       ... ok  (35/35)   perl5: 90ms  / pperl: 11ms
PDL/260-pdl-from-string.t ... ok  (60/60)   perl5: 86ms  / pperl: 13ms
PDL/300-niceslice.t       ... ok  (30/30)   perl5: 137ms / pperl: 75ms
PDL/330-flexraw-fastraw.t ... ok  (30/30)   perl5: 96ms  / pperl: 20ms
</pre>

<p>Across the full PDL suite, pperl's per-test wall time sits at
9–13ms for the typical path and climbs to 20–75ms for tests that do
real I/O or source filtering. Upstream's corresponding numbers are
82–137ms. The typical startup advantage is 8–10×; the fastest cases
reach 13×. This is not a compute-throughput benchmark - it is the
cost of <code>use PDL</code> plus a few operations. And it holds
because the entire PDL stack is compiled into the pperl binary.
There is no module loading, no PP compilation, no dynamic linking.</p>

<p>Compute-throughput numbers are what most readers will actually
care about, so here is a table from the current benchmark suite -
release build, microseconds per call, averaged over a warm loop.
Worth stating up front: these are <em>dry</em> numbers. No JIT
loop-fusion, no Rayon parallelism, no GPU offload - none of the
mechanisms that define pperl's architectural advantage are engaged
here. What is being measured is the raw kernel-versus-kernel cost
and the per-call dispatch overhead. The ceiling is considerably
higher than what these numbers show; the floor is what they
are.</p>

<pre>
op                    size    upstream µs    pperl µs    ratio
---------------   --------    -----------    --------    -----
pdl_from_str            10           66.6         3.4    20x
pdl_from_str           100          583.4        18.3    32x
pdl_from_str          1000         6667.7       183.4    36x

<p>stringify               10            8.6         1.5     5.6x<br />
stringify              100           60.0        13.1     4.6x<br />
stringify             1000          620.6       140.8     4.3x</p>

<p>add_scalar             100            3.4         1.2     2.8x<br />
matmult_sq              10            5.4         2.3     2.3x</p>

<p>slice                  100            2.0         1.3     1.5x<br />
slice                10000            2.1         1.3     1.7x<br />
slice              1000000            2.2         1.4     1.5x</p>

<p>add_vec            1000000         1860.4      1939.1     0.96x<br />
mul_vec            1000000         1879.3      1790.6     1.05x<br />
sum                1000000         1468.3      1344.1     1.09x<br />
exp                1000000         8174.9      8110.6     1.01x<br />
matmult_sq             200         6329.4      7070.6     0.90x</p>

<p>sumover            1000000         4723.4     13214.5     0.36x<br />
</pre></p>

<p>Three things stand out.</p>

<p><strong>Parsing and formatting win cleanly.</strong>
<code>pdl_from_str</code> - the <code>pdl("1 2 3; 4 5 6")</code>
literal parser - is 20-36× faster because ours is a native Rust
tokenizer where upstream's is a pure-Perl regex chain. Stringify is
4-6× faster for the same structural reason: a native formatter
rather than Perl-side string assembly. These gains are not tuning
artefacts; they will persist.</p>

<p><strong>Small-op dispatch wins.</strong> On small arrays
(100-element <code>add_scalar</code>, 10×10 <code>matmult</code>),
pperl is 2-3× faster because the XS overhead is gone. This is the
function-pointer overload dispatch from the connector walkthrough,
now with numbers: Perl → stash lookup → Rust kernel, no intervening
XSUB layer. On large arrays the kernel dominates and the dispatch
win disappears into the noise - which is the correct outcome, not
a disappointment.</p>

<p><strong>Slice is zero-copy and measurably so.</strong> A
<code>slice</code> operation on a 1M-element PDL takes about 1.4
µs on pperl, independent of input size - a constant-time affine
view construction, as it should be. Upstream is within the same
order of magnitude (about 2 µs); both implementations do the right
thing here.</p>

<p>Outliers do happen. <code>sumover</code> at 1M elements is
currently slower than upstream; it was detected, it is understood,
and it is being worked on. That is the expected shape of a young
engine against a thirty-year-old one.</p>

<p>These numbers are just a baseline. The "dry" performance without
JIT, Autoparallelization or GPU. Autovec by the compiler happens for both.
The numbers for JIT+Rayon and/or GPU will land in a dedicated post
once the relevant parts of the JIT are ready.</p>

<h3>How pperl PDL Compares to Upstream</h3>

<table border="1" cellpadding="6" cellspacing="0" style="border-collapse: collapse; margin: 1em 0;">
<thead>
<tr><th>Area</th><th>pperl + Rust-PDL</th><th>upstream + C PDL</th></tr>
</thead>
<tbody>
<tr>
  <td>Core numerics</td>
  <td>~98% parity with upstream; occasionally more precise</td>
  <td>The reference implementation</td>
</tr>
<tr>
  <td>Startup (<code>use PDL</code> + trivial op)</td>
  <td>~9–13ms typical</td>
  <td>~82–90ms typical</td>
</tr>
<tr>
  <td>Build dependency</td>
  <td><code>cargo build</code>, no C toolchain</td>
  <td>C compiler, XS, PP code-gen, Makefile toolchain</td>
</tr>
<tr>
  <td>Thread model</td>
  <td>Rayon (prototypes working)</td>
  <td>Perl ithreads + pthread-guarded globals</td>
</tr>
<tr>
  <td>Source filters</td>
  <td>Yes - NiceSlice works</td>
  <td>Yes</td>
</tr>
<tr>
  <td>Forward-flow dataflow</td>
  <td>Partial; a handful of known test failures</td>
  <td>Full</td>
</tr>
<tr>
  <td>PDL::PP compiler</td>
  <td>No - replaced by pd2multi (internal)</td>
  <td>Yes - runs at build time</td>
</tr>
<tr>
  <td>Inline::Pdlpp</td>
  <td>No (no in-process C compiler)</td>
  <td>Yes</td>
</tr>
<tr>
  <td>FITS / astronomy I/O</td>
  <td>Out of scope (user-space, on top of PDL)</td>
  <td>Yes, via Astro::FITS</td>
</tr>
<tr>
  <td>Perl ithreads</td>
  <td>No - Rayon replaces the model</td>
  <td>Yes</td>
</tr>
</tbody>
</table>

<h3>On Compatibility</h3>

<p>Our commitment is to upstream PDL's specified behaviour. When
the specification and the current implementation disagree, we side
with the specification. <code>lgamma</code> in scalar context is
the cleanest illustration: Perl's <code>GIMME_V</code> contract is
unambiguous; upstream's PP-generated XS happens not to honour it;
ours does. We did not set out to deviate - we set out to mirror
Perl.</p>

<p>Everywhere else, upstream is the reference. Thirty years of PDL
carry numerical decisions and edge-case wisdom that the
documentation does not always explain. Whenever we disagreed with
upstream during implementation, our default assumption was that we
were missing context. The <code>float + 0.2</code> promotion case
earlier in this post is exactly that: we thought we were right.
However, upstream was right - we conformed.</p>

<h3>What's Next</h3>

<p>Three threads, in rough priority order.</p>

<p><strong>Rayon-based parallelism.</strong> First prototypes are
running: PDL operations inside pperl's JIT-compiled while-loops
distribute across cores via work-stealing. Early results look
promising. Mandelbrot-scale workloads already parallelize cleanly;
the interesting work is in making parallelism show up for realistic
PDL pipelines without per-call overhead eating the win.</p>

<p><strong>OpenCL / GPU acceleration.</strong> This is a priority
goal, and it is the reason pd2multi exists as a proper compiler
rather than a template system. A GPU backend is one more emitter
walking the AST, not a separate codegen project. The engineering
cost is real but bounded.</p>

<p><strong>JIT fusion across PDL boundaries.</strong> Cranelift
compilation of surrounding Perl loops that call PDL operations, so
the JIT can see through the op and fuse it with the loop body.
This is the structural payoff of having a pure-Rust engine - the
JIT can look inside - and it is the thing that will make the case
for reimplementation in performance terms, not just compatibility
terms. Serious work on this is pending the Rayon and GPU threads
stabilizing.</p>

<p>No dates. We will post numbers when there are numbers to post.</p>

<p>In the meantime: <code>use PDL;</code> in pperl, today, at
<a href="https://perl.petamem.com">https://perl.petamem.com</a>.
Roughly three thousand assertions of work, about ninety-eight per
cent of them matching the upstream beacon, a double-digit startup
win, and a handful of cases where independent implementation caught
things that a single implementation had missed.</p>

<p><em>- Richard C. Jelinek, PetaMem s.r.o.</em></p>
]]>
    </content>
</entry>



<entry>
    <title>Compiling Google::ProtocolBuffers::Dynamic on Debian Trixie</title>
    <link rel="alternate" href="https://blogs.perl.org/users/dean/2026/04/compiling-googleprotocolbuffersdynamic-on-debian-trixie.html" />
    <id>tag:blogs.perl.org,2026:/users/dean//558.12027</id>
    <published>2026-04-16T23:52:36Z</published>
    <updated>2026-04-16T23:57:15Z</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>I found this sufficient of an obstacle that I wanted to post about it for future posterity.</p>

<p>I was able to <em>cpanm Google::ProtocolBuffers::Dynamic</em> after installing these packages on Debian Trixie.</p>

<ul>
	<li>build-essential (unsurprisingly)</li>
<li>cmake</li>
<li>libprotobuf-dev</li>
<li>libprotoc-dev</li>
</ul>

<p>The last library eluded me and caused the most frustration. Anyway on to other things.</p>]]>
        
    </content>
</entry>



<entry>
    <title>Faster UTF-8 Validation</title>
    <link rel="alternate" href="https://blogs.perl.org/users/chansen/2026/04/faster-utf-8-validation.html" />
    <id>tag:blogs.perl.org,2026:/users/chansen//165.12026</id>
    <published>2026-04-16T22:16:13Z</published>
    <updated>2026-04-17T12:11:17Z</updated>
    <author>
        <name>Christian Hansen</name>
        
    </author>
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/chansen/">
        <![CDATA[<p>A while back, I received a pull request suggesting that I update the performance comparison with Encode.pm in my module, <a href="https://metacpan.org/dist/Unicode-UTF8"><code>Unicode::UTF8</code></a>. When I originally wrote <code>Unicode::UTF8</code>, <code>Encode.pm</code> used its own UTF-8 validation implementation. Since then, Karl Williamson has done extensive work improving Perl, and Encode.pm now relies on those validation routines based on Björn Höhrmann&#8217;s <a href="https://bjoern.hoehrmann.de/utf-8/decoder/dfa/">UTF-8 DFA decoder</a>. It&#8217;s an elegant piece of code, widely adopted across many projects.</p>

<p>That said, I wasn&#8217;t particularly motivated to revisit the comparison, so I decided instead to look into faster scalar C implementations. While it has been suggested that Unicode::UTF8 should gain a SIMD implementation, and that may well be worthwhile, I wanted to first explore improvements to the scalar path, which would still be required as a fallback.</p>

<p>After some searching, I came across a <a href="https://x.com/_rsc/status/1413843059972923394">tweet</a> by Russ Cox showing performance numbers for a shift-based DFA implementation in Go, along with a link to a <a href="https://gist.github.com/pervognsen/218ea17743e1442e59bb60d29b1aa725">gist</a> by Per Vognsen describing the technique.</p>

<p>It turned out that this shift-based DFA approach was quite popular a few years ago. Several implementations appeared in different programming languages and even in some RDBMs. However, I couldn&#8217;t find any reusable code, so I decided to implement it as a header-only C library, updating a UTF-8 validator I originally wrote in 2017.</p>

<p>I won&#8217;t go into the details of shift-based DFA encoding here. For a thorough explanation, I recommend Per Vognsen&#8217;s article, and I cover my own implementation in more detail <a href="https://github.com/chansen/c-utf8#shift-based-dfa-encoding">here</a>.</p>

<p>The main difference from a traditional DFA, such as Björn Höhrmann&#8217;s implementation, lies in how table lookups are handled. A conventional byte-class + transition-table DFA requires two lookups per byte: one to map the byte to a character class, and another to determine the next state. The shift-based DFA combines both steps into a single lookup, at the cost of a larger table. Both DFAs have a serial chain dependency where each byte&#8217;s state transition depends on the previous one, which limits instruction-level parallelism.</p>

<p>Two functions are provided for UTF-8 validation:</p>

<p><code>utf8_valid</code> has no data-dependent branches. Each DFA step is a table lookup combined with a bitwise shift, with no conditional branches on the input byte value. Execution time scales with byte count, not byte values.</p>

<p><code>utf8_valid_ascii</code> adds an ASCII fast path. There are different approaches to this: Perl, for instance, scans a leading ASCII prefix and falls back to DFA validation upon encountering a non-ASCII byte, never re-entering the fast path. I am not particularly fond of that approach, since even predominantly ASCII content today tends to contain non-ASCII bytes. Instead, I opted for a 16-byte chunk fast path that skips the DFA for chunks consisting entirely of ASCII bytes, and resumes DFA validation in a non-clean state when a non-ASCII byte is encountered.</p>

<p>The implementation showed promising results, but can we go faster? Yes! By splitting the UTF-8 stream in two, we can break the serial chain dependency and run two independent DFA chains in a single interleaved loop. Since the chains share no data dependencies, the CPU&#8217;s out-of-order engine can overlap their shift operations on cores with multiple shift-capable execution ports. I did try three interleaved streams but saw no improvement on the hardware available to me, the added overhead of splitting the stream further likely offsets any potential gain.</p>

<p>Whether <code>utf8_valid_ascii</code> is profitable depends heavily on the content. The following benchmark output illustrates this with ASCII-heavy content.</p>

<p>The benchmark includes two reference implementations: <code>hoehrmann</code> and <code>utf8_valid_old</code>, the previous scalar implementation in the C library.</p>

<pre><code>en.txt: 80 KB; 82K code points; 1.00 units/point
  U+0000..U+007F          82K  99.9%
  U+0080..U+07FF           18   0.0%
  U+0800..U+FFFF           49   0.1%
  hoehrmann                    572 MB/s
  utf8_valid_old              3677 MB/s  (6.42x)
  utf8_valid                  6466 MB/s  (11.30x)
  utf8_valid_ascii           41001 MB/s  (71.63x)

sv.txt: 94 KB; 93K code points; 1.04 units/point
  U+0000..U+007F          90K  96.4%
  U+0080..U+07FF           3K   3.5%
  U+0800..U+FFFF          171   0.2%
  hoehrmann                    572 MB/s
  utf8_valid_old              1536 MB/s  (2.68x)
  utf8_valid                  6600 MB/s  (11.53x)
  utf8_valid_ascii            8581 MB/s  (15.00x)
</code></pre>

<p><code>units/point</code> is a rough content-mix indicator: <code>1.00</code> is near-pure ASCII, ~<code>1.7-1.9</code> is common for 2-byte-heavy text, and ~<code>2.7-3.0</code> for CJK-heavy text. The code point distribution breaks down the input by Unicode range. Results are sorted slowest to fastest; the multiplier shows throughput relative to the slowest implementation.</p>

<p>Output from multibyte-heavy content:</p>

<pre><code>ja.txt: 176 KB; 65K code points; 2.79 units/point
  U+0000..U+007F           7K  10.7%
  U+0080..U+07FF           30   0.0%
  U+0800..U+FFFF          58K  89.3%
  hoehrmann                    572 MB/s
  utf8_valid_old              2046 MB/s  (3.58x)
  utf8_valid_ascii            4200 MB/s  (7.34x)
  utf8_valid                  6492 MB/s  (11.34x)

ar.txt: 25 KB; 14K code points; 1.81 units/point
  U+0000..U+007F           3K  18.9%
  U+0080..U+07FF          12K  81.1%
  hoehrmann                    572 MB/s
  utf8_valid_old               984 MB/s  (1.72x)
  utf8_valid_ascii            4163 MB/s  (7.28x)
  utf8_valid                  6486 MB/s  (11.34x)
</code></pre>

<p><code>utf8_valid</code> reaches approximately 0.71 cycles per byte on wide-issue cores, that&#8217;s pretty good for a C scalar implementation. See the <a href="https://github.com/chansen/c-utf8#performance">Performance</a> section in the GitHub repository for a more in-depth analysis and comparison across different architectures and compiler optimizations.</p>

<p>I couldn&#8217;t stop at just a validator. With spare bits left in the DFA table, I went ahead and implemented a complete UTF-8 library covering validation, decoding, navigation, and transcoding. The code is available on <a href="https://github.com/chansen/c-utf8">GitHub</a>.</p>

<p><code>Unicode::UTF8</code> internally uses <code>utf8_valid_ascii</code> in its encode and decode routines.</p>

<p>Next steps: I have no further ideas for how to improve the scalar DFA implementation, and I am satisfied with the performance. A SIMD implementation may be worth exploring in the future, but the more immediate goal would be to get this incorporated into Perl core.</p>

<p>Enjoy! PS Now I can disregard the PR and tune up the numbers in the comparison ;o)</p>
]]>
        

    </content>
</entry>



<entry>
    <title>This week in PSC (221) | 2026-04-13</title>
    <link rel="alternate" href="https://blogs.perl.org/users/psc/2026/04/this-week-in-psc-220-2026-04-13.html" />
    <id>tag:blogs.perl.org,2026:/users/psc//4112.12025</id>
    <published>2026-04-14T06:52:41Z</published>
    <updated>2026-04-14T07:08:22Z</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 attended this long meeting consisting entirely of dealing with release blockers.</p>

<ul>
<li><p>We found no blockers among the issues and patches new since last week.</p></li>
<li><p>We weighed up <a href="https://github.com/Perl/perl5/issues/23676" title="#23676 BBC: Blead Breaks IO::ExplicitHandle">#23676</a> again and decided that it merits blocker status even though the breakage was the result of a C compiler change, not of a change to perl (in this dev cycle or otherwise).</p></li>
<li><p>We then reviewed all of the blockers we were already tracking and made decisions on how to proceed with all of them. Of particular note,</p></li>
<li><p>We agreed that <a href="https://github.com/Perl/perl5/issues/23131" title="#23131 Consider re-enabling coderef-in-stash optimization">#23131</a> simply cannot be pursued in this form – not just in this cycle but at all. We may eventually be able to virtualize the stash entirely, allowing much deeper optimization by using a more rational internal data structure while maintaing the Perl-land illusion that nothing has changed (effectively turning the stash hash tree into an API in the same way that a tied hash is an API that looks like a data structure). But until such time, Perl-visible changes to the stash data structure are simply too disruptive.</p></li>
<li><p><a href="https://github.com/Perl/perl5/issues/24340" title="#24340 BBC: Blead Breaks App::CPAN::Mini::Visit">#24340</a> is part of a series that constitutes a promising-looking fix which we want to pursue, but it didn’t get fully stabilized soon enough in this cycle, and because it’s in a dark and tricky corner of the codebase, we don’t want to take the risk of shipping with undiscovered new breakage there rather than a longstanding bug. Reapplying this patch series early in the next cycle should give it ample time to bake and settle down, and we hope it will eventually ship successfully.</p></li>
<li><p>We spent some time debating <a href="https://github.com/Perl/perl5/issues/24341" title="#24341 BBC: Blead Breaks Clone::AsUTF8Bytes">#24341</a>, tracing commit and issue tracker history to try to assess the correctness of the changes. We lean toward the view that this is breakage that core should fix, but discussion in the comments is ongoing.</p></li>
</ul>

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

    </content>
</entry>



<entry>
    <title>Happy sharing</title>
    <link rel="alternate" href="https://blogs.perl.org/users/egor/2026/04/happy-sharing.html" />
    <id>tag:blogs.perl.org,2026:/users/egor//29.12024</id>
    <published>2026-04-14T01:31:33Z</published>
    <updated>2026-04-14T01:36:57Z</updated>
    <author>
        <name>vividsnow</name>
        <uri>http://vividsnow.tumblr.com</uri>
    </author>
    <category term="ipc" label="ipc" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="mmap" label="mmap" scheme="http://www.sixapart.com/ns/types#tag" />
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/egor/">
        <![CDATA[<p>So you've got a bunch of Perl worker processes and they need to share state. A work queue, a counter, a lookup table - the usual. What do you reach for?</p>

<p>Perl has solid options here, and they've been around for a while. <strong><a href="https://metacpan.org/pod/File::Map">File::Map</a></strong> gives you clean zero-copy access to mmap'd files - <code>substr</code>, <code>index</code>, even regexes run directly on mapped memory. <strong><a href="https://metacpan.org/pod/LMDB_File">LMDB_File</a></strong> wraps the Lightning Memory-Mapped Database - mature, ACID-compliant, lock-free concurrent readers via MVCC, crash-safe persistence. <strong><a href="https://metacpan.org/pod/Hash::SharedMem">Hash::SharedMem</a></strong> offers a purpose-built concurrent hash with lock-free reads and atomic copy-on-write updates. <strong><a href="https://metacpan.org/pod/Cache::FastMmap">Cache::FastMmap</a></strong> is the workhorse for shared caching - mmap-backed pages with per-page fcntl locking, LRU eviction, and optional compression.</p>

<p>These are all good, proven tools. But they have something in common: they're about <strong>storage</strong>. You put data in, you get data out. They don't give you a queue that consumers can block on. They don't give you a pub/sub channel, a ring buffer, a semaphore, a priority heap, or a lock-free MPMC algorithm. They don't do atomic counters or futex-based blocking with timeouts.</p>

<p>That's the gap the <strong>Data::*::Shared</strong> family fills - fourteen Perl modules that give you proper, typed, concurrent data structures backed by <code>mmap</code>. Not better storage - <strong>concurrent data structures</strong> that happen to live in shared memory. Queues, hash maps, pub/sub, stacks, ring buffers, heaps, graphs, sync primitives - the works. All written in XS/C, all designed to work across <code>fork()</code>'d processes with zero serialization overhead.</p>

<p>Let me walk you through what's in the box.</p>

<h2>The Approach</h2>

<p>Every module in the family uses the same core recipe:</p>

<ul>
<li><strong><code>mmap(MAP_SHARED)</code></strong> for the actual shared memory - no serialization, no copies, just raw memory visible to all processes</li>
<li><strong>Linux futex</strong> for blocking/waiting - when a queue is empty and you want to wait for data, you sleep in the kernel, not in a spin loop</li>
<li><strong>CAS (compare-and-swap)</strong> for lock-free operations where possible - no mutex, no contention, just atomic CPU instructions</li>
<li><strong>PID-based crash recovery</strong> - if a process dies holding a lock, other processes detect the stale PID and recover automatically</li>
</ul>

<p>Requires Linux (futex, memfd), 64-bit Perl 5.22+. A deliberate tradeoff - portable it isn't, but fast it is.</p>

<p>Three ways to create the backing memory:</p>

<pre><code># File-backed - persistent, survives restarts
my $q = Data::Queue::Shared::Int-&gt;new('/tmp/myq.shm', 1024);

<p># Anonymous - fork-inherited, no filesystem footprint<br />
my $q = Data::Queue::Shared::Int-&gt;new(undef, 1024);</p>

<p># memfd - passable via Unix socket fd, no filesystem visibility<br />
my $q = Data::Queue::Shared::Int-&gt;new_memfd("my_queue", 1024);<br />
</code></pre></p>

<h2>The Modules</h2>

<p>Here's the full roster, grouped by use case.</p>

<h3>Message Passing</h3>

<p><strong>Data::Queue::Shared</strong> - Your bread-and-butter MPMC (multi-producer, multi-consumer) bounded queue. Integer variants use the Vyukov lock-free algorithm; string variant uses a mutex with a circular arena. Blocking and non-blocking modes, batch operations, the whole deal.</p>

<pre><code>use Data::Queue::Shared;

<p>my $q = Data::Queue::Shared::Int-&gt;new(undef, 4096);</p>

<p># In producer<br />
$q-&gt;push(42);<br />
$q-&gt;push_multi(1, 2, 3, 4, 5);</p>

<p># In consumer<br />
my $val = $q-&gt;pop_wait(1.5);    # block up to 1.5s<br />
my @batch = $q-&gt;pop_multi(100);<br />
</code></pre></p>

<p>Single-process throughput: <strong>~5M ops/s</strong> for integers. That's roughly 3x <code>MCE::Queue</code> and 6x POSIX message queues.</p>

<p><strong>Data::PubSub::Shared</strong> - Broadcast pub/sub over a ring buffer. Publishers write, subscribers each track their own cursor. If a subscriber falls behind, it auto-recovers to the oldest available message. No back-pressure on writers.</p>

<pre><code>my $ps = Data::PubSub::Shared::Int-&gt;new(undef, 8192);
$ps-&gt;publish(42);

<p>my $sub = $ps-&gt;subscribe;<br />
my $val = $sub-&gt;poll_wait(1.0);<br />
</code></pre></p>

<p>Batch publishing hits <strong>~170M msgs/s</strong> for integers. Yes, really. It's just writing to mapped memory.</p>

<p><strong>Data::ReqRep::Shared</strong> - Request-response pattern with per-request reply routing. Client acquires a response slot, sends a request carrying the slot ID, server replies to that specific slot. Supports both sync and async client styles.</p>

<pre><code># Server
my ($request, $id) = $rr-&gt;recv_wait(1.0);
$rr-&gt;reply($id, "processed: $request");

<p># Client (async)<br />
my $id = $rr-&gt;send("do something");<br />
my $response = $rr-&gt;get_wait($id, 2.0);<br />
</code></pre></p>

<p>Around <strong>200K req/s</strong> cross-process - competitive with Unix domain sockets but with true MPMC support.</p>

<h3>Key-Value</h3>

<p><strong>Data::HashMap::Shared</strong> - This is the big one. Concurrent hash map with elastic capacity, optional LRU eviction (clock algorithm with lock-free reads), optional per-key TTL, atomic counters, sharding, cursors. Eleven type variants from II (int-int) to SS (string-string).</p>

<pre><code>use Data::HashMap::Shared::SS;

<p>my $map = Data::HashMap::Shared::SS-&gt;new('/tmp/cache.shm', 100_000);<br />
$map-&gt;put("user:123", "alice");<br />
my $name = $map-&gt;get("user:123");</p>

<p># LRU cache with max 10K entries<br />
my $cache = Data::HashMap::Shared::SS-&gt;new('/tmp/lru.shm', 100_000, 10_000);</p>

<p># TTL - entries expire after 60 seconds<br />
my $ttl = Data::HashMap::Shared::II-&gt;new('/tmp/ttl.shm', 100_000, 0, 60);</p>

<p># Atomic counter (lock-free fast path under read lock)<br />
$map-&gt;incr("hits:page_a");<br />
</code></pre></p>

<p>Cross-process string reads: <strong>3.25M/s</strong>. Integer lookups hit <strong>~10M/s</strong>. And you get built-in LRU and TTL without an external cache layer.</p>

<h3>Sequential &amp; Positional</h3>

<p><strong>Data::Stack::Shared</strong> - Lock-free LIFO stack. Push, pop, peek. ~6.4M ops/s.</p>

<p><strong>Data::Deque::Shared</strong> - Double-ended queue. Push/pop from both ends. Lock-free CAS. ~6.3M ops/s.</p>

<p><strong>Data::RingBuffer::Shared</strong> - Fixed-size circular buffer that overwrites on wrap. No consumer tracking - you just read by position. Great for metrics windows and rolling logs. ~11.7M writes/s.</p>

<p><strong>Data::Log::Shared</strong> - Append-only log. Unlike Queue (consumed on read) or RingBuffer (overwritten), Log retains everything until explicitly truncated. CAS-based append, cursor-based reads. ~8.9M appends/s.</p>

<h3>Resource Management</h3>

<p><strong>Data::Pool::Shared</strong> - Object pool with allocate/free. CAS-based bitmap allocation, typed slots (I64, I32, F64, Str), scope guards for automatic cleanup, raw C pointers for FFI integration. PID-tracked slots are auto-recovered when a process dies.</p>

<pre><code>my $pool = Data::Pool::Shared::I64-&gt;new(undef, 256);
my $idx = $pool-&gt;alloc;
$pool-&gt;set($idx, 42);
# ...
$pool-&gt;free($idx);

<p># Or with auto-cleanup<br />
{<br />
    my $guard = $pool-&gt;alloc_guard;<br />
    $pool-&gt;set($$guard, 99);<br />
}  # auto-freed here<br />
</code></pre></p>

<p><strong>Data::BitSet::Shared</strong> - Fixed-size bitset with per-bit atomic CAS operations. Good for flags, membership tracking, allocation bitmaps. ~10.5M ops/s.</p>

<p><strong>Data::Buffer::Shared</strong> - Type-specialized arrays (I8 through F64, plus Str) with atomic per-element access. Seqlock for bulk reads, RW lock for bulk writes. Think shared sensor arrays or metric buffers.</p>

<h3>Graphs &amp; Priority</h3>

<p><strong>Data::Graph::Shared</strong> - Directed weighted graph with mutex-protected mutations. Node bitmap pool, adjacency lists, per-node data. ~3.9M node adds/s, ~13.3M lookups/s.</p>

<p><strong>Data::Heap::Shared</strong> - Binary min-heap for priority queues. Mutex-protected, futex blocking when empty. ~5.3M pushes/s.</p>

<h3>Synchronization Primitives</h3>

<p><strong>Data::Sync::Shared</strong> - Five cross-process sync primitives in one module: <strong>Semaphore</strong>, <strong>Barrier</strong>, <strong>RWLock</strong>, <strong>Condvar</strong>, and <strong>Once</strong>. All futex-based, all with PID-based stale lock recovery, all with scope guards.</p>

<pre><code>use Data::Sync::Shared;

<p>my $sem = Data::Sync::Shared::Semaphore-&gt;new(undef, 4);  # 4 permits<br />
{<br />
    my $guard = $sem-&gt;acquire_guard;<br />
    # at most 4 processes here concurrently<br />
}</p>

<p>my $barrier = Data::Sync::Shared::Barrier-&gt;new(undef, $num_workers);<br />
$barrier-&gt;wait;  # blocks until all workers arrive</p>

<p>my $once = Data::Sync::Shared::Once-&gt;new(undef);<br />
if ($once-&gt;enter) {<br />
    init_expensive_thing();<br />
    $once-&gt;done;<br />
}<br />
</code></pre></p>

<h3>At a Glance</h3>

<table>
<tr><th>Module</th><th>Pattern</th><th>Concurrency</th><th>Throughput</th></tr>
<tr><td>Queue::Shared</td><td>MPMC queue</td><td>lock-free (Int), mutex (Str)</td><td>~5M ops/s</td></tr>
<tr><td>PubSub::Shared</td><td>broadcast pub/sub</td><td>lock-free (Int), mutex (Str)</td><td>~170M/s batched</td></tr>
<tr><td>ReqRep::Shared</td><td>request-response</td><td>lock-free (Int), mutex (Str)</td><td>~200K req/s</td></tr>
<tr><td>HashMap::Shared</td><td>hash map + LRU/TTL</td><td>futex RW lock, seqlock reads</td><td>~10M gets/s</td></tr>
<tr><td>Stack::Shared</td><td>LIFO stack</td><td>lock-free CAS</td><td>~6.4M ops/s</td></tr>
<tr><td>Deque::Shared</td><td>double-ended queue</td><td>lock-free CAS</td><td>~6.3M ops/s</td></tr>
<tr><td>RingBuffer::Shared</td><td>circular buffer</td><td>lock-free CAS</td><td>~11.7M writes/s</td></tr>
<tr><td>Log::Shared</td><td>append-only log</td><td>lock-free CAS</td><td>~8.9M appends/s</td></tr>
<tr><td>Pool::Shared</td><td>object pool</td><td>lock-free bitmap</td><td>~3.3M alloc/s</td></tr>
<tr><td>BitSet::Shared</td><td>bitset</td><td>lock-free CAS</td><td>~10.5M ops/s</td></tr>
<tr><td>Buffer::Shared</td><td>typed arrays</td><td>atomic + seqlock</td><td>per-type</td></tr>
<tr><td>Graph::Shared</td><td>directed graph</td><td>mutex</td><td>~13.3M lookups/s</td></tr>
<tr><td>Heap::Shared</td><td>priority queue</td><td>mutex</td><td>~5.3M pushes/s</td></tr>
<tr><td>Sync::Shared</td><td>sem/barrier/rwlock/condvar/once</td><td>futex</td><td>-</td></tr>
</table>

<h2>Type Specialization</h2>

<p>Most modules come in typed variants - Int16, Int32, Int64, Str, and so on. This isn't just for type safety. An Int16 queue uses half the memory of an Int64 queue, which means double the cache density on the same hardware. When you're doing millions of operations per second, cache lines matter.</p>

<h2>Event Loop Integration</h2>

<p>Every module supports <code>eventfd()</code> for integration with event loops like EV, Mojo, or AnyEvent:</p>

<pre><code>my $fd = $q-&gt;eventfd;
# register $fd with your event loop
# on readable: $q-&gt;eventfd_consume; then poll/pop
</code></pre>

<p>Signaling is explicit (<code>$q-&gt;notify</code>) so you can batch writes before waking consumers.</p>

<h2>Playing Nice with Others: PDL, FFI::Platypus, OpenGL::Modern</h2>

<p>One thing I want to highlight is that these aren't isolated islands. Because everything lives in mmap'd memory with known layouts, you get natural interop with other systems that work with raw pointers and packed data.</p>

<p><strong><a href="https://metacpan.org/pod/PDL">PDL</a></strong> is the obvious one. If you're doing numerical work in Perl - signal processing, image manipulation, statistics - PDL is your workhorse. The Buffer module's <code>as_scalar</code> returns a zero-copy scalar reference directly over the mmap'd region. Feed that to PDL and you've got an ndarray backed by shared memory:</p>

<pre><code>use Data::Buffer::Shared::F64;
use PDL;

<p>my $buf = Data::Buffer::Shared::F64-&gt;new('/tmp/signal.shm', 10000);</p>

<p># one process fills the buffer with sensor data...<br />
# another process reads it as a PDL:<br />
my $pdl = PDL-&gt;new_from_specification(double, 10000);<br />
${$pdl-&gt;get_dataref} = ${$buf-&gt;as_scalar};<br />
$pdl-&gt;upd_data;</p>

<p>printf "mean=%.4f stddev=%.4f\n", $pdl-&gt;stats;<br />
</code></pre></p>

<p>For typed arrays you can also use <code>get_raw</code>/<code>set_raw</code> for bulk transfers - a single <code>memcpy</code> under the hood, seqlock-guarded for consistency. That means you can build a multiprocess image pipeline where one process captures frames into a shared U8 buffer, another runs PDL convolutions on it, and a third renders the result - all communicating through shared memory with eventfd notifications, no serialization anywhere.</p>

<p><strong><a href="https://metacpan.org/pod/FFI::Platypus">FFI::Platypus</a></strong> works just as naturally. Pool and Buffer both expose <code>ptr()</code> / <code>data_ptr()</code> - raw C pointers as unsigned integers, ready to hand to any C function through FFI. Need to call libc <code>qsort</code> directly on your shared data? Go ahead:</p>

<pre><code>use Data::Pool::Shared;
use FFI::Platypus;

<p>my $pool = Data::Pool::Shared::I64-&gt;new(undef, 1000);<br />
# ... alloc and fill slots ...</p>

<p>my $ffi = FFI::Platypus-&gt;new(api =&gt; 2);<br />
$ffi-&gt;lib(undef);  # libc<br />
$ffi-&gt;attach([qsort =&gt; 'c_qsort'] =&gt;<br />
    ['opaque', 'size_t', 'size_t', '(opaque,opaque)-&gt;int'] =&gt; 'void');</p>

<p>c_qsort($pool-&gt;data_ptr, 1000, 8, $comparator);<br />
# slots are now sorted in-place, visible to all processes<br />
</code></pre></p>

<p>Pool slots are contiguous in memory (<code>data_ptr + idx * elem_size</code>), so any C library that expects a flat array works out of the box.</p>

<p><strong><a href="https://metacpan.org/pod/OpenGL::Modern">OpenGL::Modern</a></strong> is where it gets fun. Buffer::F32 is essentially a shared vertex buffer. One process computes positions, another renders them - connected by a shared mmap region and eventfd:</p>

<pre><code># Compute process:
my $verts = Data::Buffer::Shared::F32-&gt;new('/tmp/verts.shm', 30000);
$verts-&gt;set_slice(0, @new_positions);
$verts-&gt;notify;

<p># Render process:<br />
my $ref = $verts-&gt;as_scalar;<br />
# on eventfd readable:<br />
glBufferSubData_p(GL_ARRAY_BUFFER, 0, $$ref);  # zero-copy upload<br />
</code></pre></p>

<p>Pool goes further - it's a natural fit for particle systems. Particles are dynamically spawned (<code>alloc</code>) and despawned (<code>free</code>), each with a fixed-size state struct. A spawner process allocates particles, a physics process updates them, and the renderer uploads the live slots to a VBO via <code>ptr()</code>. The raw pointer goes straight to <code>glBufferSubData_c</code> - no packing, no intermediate copies.</p>

<p>The common thread here is that the data is already in the format the consuming library expects. F32 buffers are packed floats. I64 pools are packed int64s. There's no Perl-side serialization layer to bypass because there was never one to begin with.</p>

<h2>Optional Keyword API</h2>

<p>If you install <a href="https://metacpan.org/pod/XS::Parse::Keyword">XS::Parse::Keyword</a>, several modules expose lexical keywords that bypass Perl method dispatch entirely:</p>

<pre><code>use Data::Queue::Shared;

<p>q_int_push $q, 42;<br />
my $v = q_int_pop $q;<br />
</code></pre></p>

<p>Zero dispatch overhead. The XS function gets called directly. It's optional - the method API works fine - but it's there when you need every last microsecond.</p>

<h2>The Big Picture</h2>

<p>Here's how the pieces fit together in a typical system:</p>

<ul>
<li><strong>Data::Queue::Shared</strong> distributes work from producers to a pool of workers</li>
<li><strong>Data::HashMap::Shared</strong> acts as a shared cache or config store that all workers read from</li>
<li><strong>Data::PubSub::Shared</strong> broadcasts events or status updates to whoever's listening</li>
<li><strong>Data::Sync::Shared</strong> coordinates startup (Barrier), limits concurrency (Semaphore), and protects shared initialization (Once)</li>
<li><strong>Data::Pool::Shared</strong> manages reusable resource slots</li>
<li><strong>Data::RingBuffer::Shared</strong> or <strong>Data::Log::Shared</strong> holds recent metrics or audit trails</li>
</ul>

<p>All of this running across <code>fork()</code>'d processes, communicating through shared memory at millions of operations per second, no serialization overhead.</p>

<h2>Getting Started</h2>

<p>Values are typed C scalars or fixed-length strings - no automatic serialization of arbitrary Perl structures. That's by design: raw mmap'd memory is what makes everything fast and FFI-friendly, but it means you won't be sharing hashrefs or blessed objects directly.</p>

<p>All modules follow the same pattern:</p>

<pre><code>use Data::Queue::Shared;

<p># Pick your backing: file, anonymous, or memfd<br />
my $q = Data::Queue::Shared::Int-&gt;new(undef, 4096);</p>

<p>if (fork() == 0) {<br />
    $q-&gt;push($$);  # child pushes its PID<br />
    exit;<br />
}</p>

<p>my $child_pid = $q-&gt;pop_wait(5.0);<br />
say "Child reported in: $child_pid";<br />
</code></pre></p>

<p>The modules are on GitHub under the <a href="https://github.com/vividsnow">vividsnow</a> account. Each one has its own repo, test suite, and benchmarks you can run yourself.</p>

<p>If you've ever wished Perl had something like Go's channels and sync primitives but for <code>fork()</code>'d processes - well, now it does. Fourteen of them, actually.</p>

<p>Happy sharing</p>
]]>
        
    </content>
</entry>



<entry>
    <title>CPAN Dependencies, static and dynamic</title>
    <link rel="alternate" href="https://blogs.perl.org/users/grinnz/2026/04/cpan-dependencies-static-and-dynamic.html" />
    <id>tag:blogs.perl.org,2026:/users/grinnz//2693.12023</id>
    <published>2026-04-12T02:45:28Z</published>
    <updated>2026-04-12T16:30:50Z</updated>
    <author>
        <name>Grinnz</name>
        
    </author>
    <category term="cpan" label="cpan" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="dependencies" label="dependencies" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="meta" label="meta" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="mymeta" label="mymeta" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="perl" label="perl" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="prereqs" label="prereqs" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="prerequisites" label="prerequisites" scheme="http://www.sixapart.com/ns/types#tag" />
    <content type="html" xml:lang="en" xml:base="https://blogs.perl.org/users/grinnz/">
        <![CDATA[<p>Dependencies or prerequisites are an integral feature of the CPAN software repository. They define what other CPAN modules are required for a particular CPAN distribution to be built, tested, or ultimately to function, as well as optionally to improve or add functionality. To define them properly for a distribution, it is helpful to understand exactly how they will be used, and what all the different distribution files like <code>Makefile.PL</code>, <code>Build.PL</code>, <code>META.json</code>, and <code>MYMETA.json</code> are for.</p>

<p><em>sidebar:</em> In this post, I will focus on the "requires" relationship for dependencies, which are hard dependencies that must be satisfied, but "recommends" and "suggests" relationships are also defined that indicate strongly and weakly optional dependencies respectively; CPAN installers may install these based on the options that are passed.</p>

<p>Most commonly and at a basic level, dependencies are defined in a (generated) file called <code>META.json</code> in the root of the CPAN distribution, but this may not be the complete picture. CPAN installers historically would determine what is needed at the time that the user requests to install the distribution ("install time"), and though there is now the formal concept of static prerequisites (the most common case where they are the same for every install environment), some distributions need to determine prerequisites at install time, using the original dynamic configuration process.</p>
]]>
        <![CDATA[<h2>The Configure Phase</h2>

<p>The very first step any CPAN installer takes is to check the primary <a href="https://metacpan.org/pod/CPAN::Meta::Spec">metadata file</a> <code>META.json</code>, or if that's not present, <code>META.yml</code> (which represents an older metadata specification). In this file, it finds the <a href="https://metacpan.org/pod/CPAN::Meta::Spec#Prereq-Spec">prereqs</a> key, and within it the <code>configure</code> sub-key. This contains the prerequisites for the "configure" phase of CPAN installation, which largely consists of running the configure script (a file called <code>Makefile.PL</code> or <code>Build.PL</code>), so these dependencies must be installed before doing anything else. This, like the installation of other prerequisites later, is a recursive process, which starts again at the configure phase for each dependency installed in the chain, until it makes its way back up the tree to complete the installation of the originally requested CPAN distribution.</p>

<p><em>sidebar:</em> The configure phase was not initially part of the specification, so ancient CPAN installers could only use core modules in the configure script; the introduction of configure phase dependencies enabled the configure script to use alternative installers like <a href="https://metacpan.org/pod/Module::Build::Tiny">Module::Build::Tiny</a> in place of the original <a href="https://perldoc.perl.org/ExtUtils::MakeMaker">ExtUtils::MakeMaker</a>. This also meant that <a href="https://metacpan.org/pod/Module::Build">Module::Build</a> could be removed from core, and <a href="https://metacpan.org/pod/Module::Install">Module::Install</a>'s overly clever bundling mechanism was no longer necessary.</p>

<p>The CPAN installer also can check two other metadata keys at this point: <code>dynamic_config</code> and <code>x_static_install</code>. <a href="https://metacpan.org/pod/CPAN::Meta::Spec#dynamic_config">dynamic_config</a>, as the name implies, was initially meant to indicate whether the configure phase needs to be run to dynamically configure the distribution, but unfortunately in practice only indicates whether the prereqs specifically are dynamic. In other words, if this key is present and set to 0 (false), this indicates that the full list of prerequisites in <code>META.json</code> can be used as-is. It does not, however, allow the CPAN installer to skip the configure phase, as the configure script may do anything else a Perl script can do relevant to configuring the distribution, such as determining whether it can be installed at all in the current environment, or even dynamically generating other source files from <code>.PL</code> scripts.</p>

<p>The newer <a href="https://metacpan.org/pod/CPAN::Static::Spec">x_static_install</a> is an unofficial metadata key that some installers are beginning to recognize as a second attempt to allow simple distributions to skip dynamic configuration; if this key is set to 1 (true) then the installer may choose to ignore the configure script and install the distribution with its own standard process. As of this writing, the <a href="https://metacpan.org/pod/cpm">cpm</a> installer and development releases of the <a href="https://metacpan.org/pod/cpanm">cpanm</a> installer implement their own installation procedure by default if this metadata key is set: installing static prerequisites, running tests if requested, and finally copying the files to the install location. Otherwise, installation proceeds through the dynamic process below.</p>

<p>Once the configure prerequisites are installed, the configure script (<code>Makefile.PL</code> or <code>Build.PL</code>) is run. For historical reasons, <code>Build.PL</code> is run if both are present. This script is expected to do a couple specific things to inform the rest of the installation process: create a <code>Makefile</code> or <code>Build</code> script to handle the testing and installation functionality, and create a <code>MYMETA.json</code> (or <code>MYMETA.yml</code>) secondary metadata file. When <code>dynamic_config</code> has not been disabled, this new metadata file will be consulted for the remaining set of prerequisites, so it is the job of the configure script to dynamically determine this new list of prerequisites. Note that, because this happens during the configure phase itself, configure prerequisites cannot be dynamic.</p>

<h2>Everything Else</h2>

<p>Once prereqs are gathered either statically or from <code>MYMETA</code>, the CPAN installer will then install the prereqs from the other three installation phases: <code>build</code>, <code>test</code>, and <code>runtime</code>. Though it will only be subsequently building and testing the module, the <code>runtime</code> phase is considered a subset that is needed for both of these operations, so these prereqs are also required at this point. The CPAN installer can then build, test, and install the distribution, using the <code>Makefile</code> or <code>Build</code> script (or its own implementation in the case of <code>x_static_install</code>).</p>

<p>Technically, only the <code>runtime</code> phase dependencies are needed after installation, but <code>configure</code> and <code>build</code> prereqs are usually relatively light and may also be present in the <code>runtime</code> phase, so it's usually not worth the effort to exclude these from the runtime environment, but a CPAN installer could decide to only install them temporarily. Any <code>test</code> phase prereqs can be excluded by doing "notest" installations with most CPAN installers, which will avoid installing them at all.</p>

<p>So what does this all mean, as a CPAN author? It largely depends on your choice of authoring tool, but for static dependencies they will often be defined as a straightforward list in a configuration or metadata file, used by the authoring tool to generate the <code>prereqs</code> key of the shipped <code>META.json</code> file. For dynamic dependencies, instead of the authoring tool, the chosen installer used in the configure script (like ExtUtils::MakeMaker or Module::Build) must assemble the final dependency list when it runs at install time. This is often a gritty manual process, but some tools like <a href="https://metacpan.org/pod/Dist::Zilla::Plugin::DynamicPrereqs">Dist::Zilla::Plugin::DynamicPrereqs</a> and <a href="https://metacpan.org/pod/Dist::Zilla::Plugin::DynamicPrereqs::Meta">Dist::Zilla::Plugin::DynamicPrereqs::Meta</a> can help to structure the necessary logic.</p>

<p><em>sidebar:</em> You may be familiar with the <a href="https://metacpan.org/pod/cpanfile">cpanfile</a> format for declaring dependencies, but this is not directly relevant to CPAN dependency specification. A <code>cpanfile</code> may be used as an input file to an authoring tool to assemble a distribution's prerequisites into <code>META.json</code> for shipping, or as input directly to applications like <code>cpanm</code> or <code>carton</code>, but CPAN installers won't reference it while installing a standard CPAN distribution (unless specifically used by the configure script such as with <a href="https://metacpan.org/pod/ExtUtils::MakeMaker::CPANfile">ExtUtils::MakeMaker::CPANfile</a>).</p>

<p>For further reading, I've written more about the structure and history of CPAN authoring in my <a href="https://metacpan.org/pod/Dist::Zilla::Starter#CPAN-DISTRIBUTIONS">Dist::Zilla::Starter</a> tutorial.</p>
]]>
    </content>
</entry>


</feed>