All downloads mentioned here are available from my
Wiki Haven:
a. Perl.Wiki V 1.45
b. cpan.metacurator.tree.html V 1.17
c. Mojo.Wiki V 1.18
d. PHP.Wiki V 1.02
CPAN::MetaCurator V 1.17 has already been released to MetaCPAN, although I can't see it yet.
-
App::ClusterSSH - Cluster administration tool
- Version: 4.19 on 2026-04-28, with 980 votes
- Previous CPAN version: 4.18_09 was released 1 month, 7 days before
- Author: DUNCS
-
App::cpm - a fast CPAN module installer
- Version: v1.0.3 on 2026-05-02, with 178 votes
- Previous CPAN version: v1.0.2 was released the same day
- Author: SKAJI
-
App::Music::ChordPro - A lyrics and chords formatting program
- Version: v6.101.0 on 2026-04-30, with 469 votes
- Previous CPAN version: v6.100.0 was released 8 days before
- Author: JV
-
Cache::FastMmap - Uses an mmap'ed file to act as a shared memory interprocess cache
- Version: 1.61 on 2026-04-30, with 25 votes
- Previous CPAN version: 1.60 was released 10 months, 12 days before
- Author: ROBM
-
CPANSA::DB - the CPAN Security Advisory data as a Perl data structure, mostly for CPAN::Audit
- Version: 20260430.001 on 2026-04-30, with 25 votes
- Previous CPAN version: 20260426.001 was released the same day
- Author: BRIANDFOY
-
DBD::Pg - DBI PostgreSQL interface
- Version: 3.20.2 on 2026-05-02, with 103 votes
- Previous CPAN version: 3.20.1 was released 2 days before
- Author: TURNSTEP
-
Encode - character encodings in Perl
- Version: 3.24 on 2026-04-29, with 65 votes
- Previous CPAN version: 3.23 was released 2 days before
- Author: DANKOGAI
-
MetaCPAN::Client - A comprehensive, DWIM-featured client to the MetaCPAN API
- Version: 2.043000 on 2026-04-29, with 29 votes
- Previous CPAN version: 2.042000 was released 3 days before
- Author: MICKEY
-
Plack - Perl Superglue for Web frameworks and Web Servers (PSGI toolkit)
- Version: 1.0053 on 2026-04-28, with 496 votes
- Previous CPAN version: 1.0052 was released 1 day before
- Author: MIYAGAWA
-
PPI - Parse, Analyze and Manipulate Perl (without perl)
- Version: 1.291 on 2026-04-25, with 64 votes
- Previous CPAN version: 1.290 was released the same day
- Author: MITHALDU
-
SPVM - The SPVM Language
- Version: 0.990168 on 2026-04-27, with 36 votes
- Previous CPAN version: 0.990167 was released 2 days before
- Author: KIMOTO
-
Starman - High-performance preforking PSGI/Plack web server
- Version: 0.4018 on 2026-04-27, with 297 votes
- Previous CPAN version: 0.4017 was released 2 years, 7 months, 13 days before
- Author: MIYAGAWA
-
Test::MockModule - Override subroutines in a module for unit testing
- Version: v0.183.0 on 2026-05-01, with 18 votes
- Previous CPAN version: v0.182.0 was released the same day
- Author: GFRANKS
-
Test::Most - Most commonly needed test functions and features.
- Version: 0.41 on 2026-04-30, with 37 votes
- Previous CPAN version: 0.40 was released the same day
- Author: DCANTRELL
-
Test2::Harness - A new and improved test harness with better Test2 integration.
- Version: 1.000172 on 2026-04-29, with 28 votes
- Previous CPAN version: 1.000171 was released 5 days before
- Author: EXODIST
-
Text::CSV_XS - Comma-Separated Values manipulation routines
- Version: 1.62 on 2026-04-29, with 104 votes
- Previous CPAN version: 1.61 was released 9 months, 2 days before
- Author: HMBRAND
-
YAML::LibYAML - Perl YAML Serialization using XS and libyaml
- Version: v0.906.0 on 2026-04-26, with 60 votes
- Previous CPAN version: v0.906.0 was released the same day
- Author: TINITA
https://www.papercall.io/perlcommunityconferencesummer26
Please share this post, the powers that be rage against us; you will not see any post regarding our activities in Perl Weekly!
Update: We got in Perl Weekly this week, 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.
If you wish to comment about this post, please do so at r/perlcommunity.
Perl Community / Science Perl Committee Impact in 2025
Talks Delivered at Winter 2025 Perl Community Conference in Austin, TX
Video editing in progress, will be released after the 2026 Summer PPC.
Each PPC has its own playlist on our YT channel!
Talks Delivered at Summer 2025 Perl Community Conference in Austin, TX
- Perl Community Conference Summer 2025 - Vincent Napiorkowski What I Learned About Perl & Catalyst
- Perl Community Conference Summer 2025 - Perl Types Will Braswell, Jr
- Perl Community Conference Summer 2025 - Valiant Update John Napiorkowski
- Perl Community Conference Summer 2025 - Will Braswell, Ohinoyi Moiza - Intern Report
- Perl Community Conference Summer 2025 - Brett Estrade wxPerl on Windows
- Perl Community Conference Summer 2025 - Virtues Update
- Perl Community Conference Summer 2025 - Science Perl Committee Report
- Perl Community Conference Summer 2025 - Privacy Preserving Applications
- Perl Community Conference Summer 2025 - Kai Baker, et al Shiny CMS
- Perl Community Conference Summer 2025 - John Napiorkowski Porting ASGI from Python to Perl
- Perl Community Conference Summer 2025 - Brett Estrade Building Beowulf Clusters with Perl
- Perl Community Conference Summer 2025 - Perl Can Dev Ops Better Than You Think
- Perl Community Conference Summer 2025 - State of the Onions
- Perl Community Conference Summer 2025 - Justin Kelly Perl Supabase
- Perl Community Conference Summer 2025 - Brett Estrade Review of John P Linderman's Quick Sort Paper
Talks Delivered at Winter 2024 Perl Community Conference in Austin, TX
- Perl Community Conference Winter 2024 - Intro - Dr. Christos Argyropoulos, MD
- Perl Community Conference Winter 2024 - State of the Noonien - Will Braswell, Jr
- Perl Community Conference Winter 2024 - Metamaterials - Dr. Luis Mochán
- Perl Community Conference Winter 2024 - Valiant - John Napiorkowski
- Perl Community Conference Winter 2024 - CPAN Ontologies - Dr Adam Russell
- Perl Community Conference Winter 2024 - Limits of Thread Safety in the Perl C API - Brett Estrade
- Perl Community Conference Winter 2024 - Perl FFI, Native C Options - Dr. Christos Argyropoulos, MD
- Perl Community Conference Winter 2024 - Chemometrics - Dr. Andrew O'Neil
- Perl Community Conference Winter 2024 - Perl Types - WIll Braswell, Jr
- Perl Community Conference Winter 2024 - Leadership Panel - WIll Braswell, Jr, et al.
- Perl Community Conference Winter 2024 - Lightning Talks - Various
- Perl Community Conference Winter 2024 - Closing - Dr. Christos Argyropoulos, MD
Science Track Paper-based Talks Delivered at Summer 2024 Perl & Raku Conference in Las Vegas
- Science Track Keynote & Diamond PERL Editor's Choice of Technical Excellence, Winner: Enhancing Non-Perl Bioinformatic Applications with Perl - Christos Argyropoulos, MD, PhD.
- Structure Based Structuring of Unstructured Data - Adam Russell, PhD.
- Chemometrics with Perl & Pharmaceutical Applications - Andrew O'Neil, PhD
- PerlGPT, A Code Llama LLM Fine-Tuned For Perl - William N. Braswell, Jr.
- Reasoning About the Rigor of Perl Programs - George Baugh - TPRC 2024
- Supporting Universal Dependencies in the Tree Editor TrEd - Jan Štěpánek, PhD.
- ASGS - A Real-Time Operational Storm Surge Forecasting Framework - Brett Estrade, MS
- Perl Cross-Compiler for Microcontrollers - Manickam Thanneermalai
Our Code of Virtues
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 HIGH. Therefore, we do not have a Code of Conduct. We have a Code of Virtues—virtues that have been present and part of Perl from the very beginning.
The concept of virtues is very old, dating back to Nicomachean Ethics. 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).
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.
Here we describe what Larry Wall meant in correct, virtuous, and perhaps chivalrous terms.
The 3 Virtues of a Perl Programmer, Properly Defined
Our Code of Virtues is made of the Three Virtues of a Perl Programmer, 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.
Practical Wisdom or Prudence, Not Laziness
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:
"...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."
Spiritedness, Not Impatience
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:
"...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."
Good Order, Not Hubris
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:
"...the quality that makes you write (and maintain) programs that other people won’t want to say bad things about."
embed.pl - Sv*MAGICAL macros are now documented
Hi,
I would like to send unencrypted matrix messages from a perl-script and wonder how to do that...
I have found a tool called patrix but I have not tried it yet as it is 9 years old or I could simply call a shell script (matrix.sh) that I have found and that basically cover my needs.
I need something that I can easily install on a small orange pi running debian.
Any suggestions?
Many thanks.
[link] [comments]
I have a small group of semantically named modules under MyName like MyName::CGI and MyName::Files that are almost ready for CPAN. Can I just start uploading them or do I need to supplicate some crotchety group of elders for access to the MyName namespace? If it's pertinent while the modules can be used individually and arguments can be made for why you'd use one in preference to an existing option, when used together they form the basis for a low-code framework for CRUD web site development like Dancer2 or Mojolicious.
On a related note I have a MyName::Datatable module for working with individual CSV-type files and a MyName::Datastore that extends datatable and lets you treat a directory of table files as a traditional database with joins and standard sql operations. I'm interested in merging them into one Data.pm file that declares the names MyName::Data::Table and MyName::Data::Base respectively. Is there any precedent on CPAN for single files that contain multiple namespaces? Or is it possible/allowed is maybe the better question.
[link] [comments]

TPRC is proud to announce the post-conference class on Monday, June 29. Steven Lembark is presenting: Teaching AI New Tricks: Perly MCP’s for Claude. The class will be from 9am to 4pm in the Palmetto Room at the Conference Hotel. SHORT DESCRIPTION: MCP definitions are not your mother’s API call. They are natural language descriptions meant for the tool to process and integrate into its own workflow. With proper description, the MCP’s are simple to use and can be daisy-chained to produce quite flexible results. Claude’s different models — Haiku, Sonnet, Opus — offer different options for cost, speed, and depth and require different definitions. In the spirit of the Perl Tester’s Notebook, this class covers a very basic tool then expands it for general use, one step at a time, looking at the necessary changes to Perl and the MCP. The result is a lightweight, fast interface that handles a common task easily and can be incorporated with other tools. For a more complete class description, please visit: https://tprc.us/tprc-2026-gsp/class/ Tickets for the class are $120 and are available at: https://perlfoundation.fcsuite.com/erp/donate/list/event?event_date_id=1002
| submitted by /u/cucarge [link] [comments] |
One of my modules report 99.999% coverage due to a single
// usage which I'm trying to understand. Simplifying
things, consider this program:
sub scalar_rhs
{
my $a;
my $b = 1;
$a // $b
}
sub hash_rhs
{
my $a;
my %b = ( x => 1 );
$a // $b{x}
}
die 'scalar failed' unless &scalar_rhs;
die 'hash failed' unless &hash_rhs;
Running:
perl -MDevel::Cover test.pl
cover -report text
Generates:
line err % l !l&&r !l&&!r expr
----- --- ------ ------ ------ ------ ----
6 *** 33 0 1 0 $a // $b
15 *** 33 0 0 1 $a // $b{'x'}
How come $b{x} is seen as false by Devel::Cover? How can I
make this pass as !l&&r?
Last week the Perl Toolchain Summit took place in Vienna (the second time, we also hosted the event in 2010). I participated mostly in the role of local orga, helping the international team finding a venue and a hotel, a place for the social event etc.
For the venue, Michael suggested Hauswirtschaft, where Geizhals sometimes hold meetings and events. This turned out to be an excellent choice. Not only did we get the rooms at a reasonable price (at least when compared to some of the more mainstream hotels), but the rooms and the whole venue were very nice and extremely accommodating: We were served vegetarian lunch each day, could bring our own snacks and drinks (the delicious apple juice Hauswirtschaft provided was not enough for some of the very specific Cola needs of some attendees; luckily there's a supermarket right across) and have a Pizza-and-Chartreuse-Party on Saturday (which I have been told ended quite late). Transporting 16 boxes of pizza on my bike trailer was also fun!
I had little time for technical contributions, as I was busy organizing dinners, answering peoples questions about Vienna and generally having a nice time. But I managed to code up a local development environment for PAUSE based on docker compose. Andreas merged it on Sunday, so if you want to have a local PAUSE (for testing and developing new features), you can now do so via a simple docker compose up pause paused. Detailed instructions can be found here and I would appreciate it if people give it a try and report back any problems they have. Having a working local dev setup is IMO crucial for getting meaningful contributions.
Together with Michael I was interviewed by Philippe for his podcast. We talked about the history and future of Vienna.pm, Geizhals, Koha and some other topics. The interview and the whole event motivated me to keep Vienna.pm going and maybe try to host a Perl-and-related-tech-events (again) in the future. We'll see...
To summarize, I really enjoyed the event, hanging around with Perl people & talking about basically everything. It was a nice reminder that this community is exactly my kind of crazy! Looking forward to the next PTS or similar event!
And no post about the PTS is complete without thanking the sponsors (is it ok to thank yourself, as I'm involved with two of the sponsors (Vienna.pm and HSK3)? I say it is...)
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.
This post is brought to you by Geizhals Preisvergleich, a Gold sponsor for the Perl Toolchain Summit 2026.
You can learn more about Geizhals at the end of this article.
Welcome to the sixteenth Perl Toolchain Summit 2026!
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.
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).
So I'd like to begin with a big thank you to the Vienna team:
You might know that the French are very proud of their croissants. 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 viennoiseries. This morning, thanks to Wikipedia, 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.
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.
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.
At the Diamond level, we are very lucky to have:
- The Perl and Raku Foundation
- a very generous anonymous donor
Gold sponsors:
Silver sponsors:
- SUSE
- Trans-Formed Media
Bronze sponsors:
We also received monetary support from the community:
- Harald Jörg
- Michele Beltrame, Sigmafin
- our anonymous donor who's been making a monthly donation for several years now
While the size of the group has stabilized around thirty people, and some of us are very regular attendees (heya Merijn!), we're also making a genuine effort to invite people to their first Perl Toolchain Summit. This year, our first timers are Robert Rothenberg, Karl Williamson and Andy Baugh.
As you've seen, new blood is not necessarily young 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.]
Speaking of regular attendees, you might have noticed a few people are missing. At least one of them has a good excuse: Ingy döt Net couldn't attend this year, because he got married yesterday to Ashley!
Once again, all Perl Steering Council members are attending, the MetaCPAN team has already annexed an appropriately sized room for themselves, and our whiteboard already has a few discussions listed.
A few announcements before we start:
- the group dinner will happen on Friday evening
- we'll do the annual group photo on Saturday
- I will be recording interviews for The Underbar podcast all of Sunday
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.
I expect that tomorrow, we'll already be talking about results!
About Geizhals Preisvergleich
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.
As already reported, I'm writing this color library. 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!
The Task
If you look into the GTC internals, you see instantly that the most common none OO data structure is a 'tuple' 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:
- Did I get an Array ?
- Does it have the right amount of values ?
- Check every value of the tuple for equality.
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 ok - 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 undef. 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.
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
is( $got, $expected, $test_name );
(which is a test function you all used at some point) and name it:
is_tuple( $got, $expected, $axis, $test_name );
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".
The Solution
The Module you need to create that is Test::Builder. So the smart ones among you have guessed you need to:
use Test::Builder;
my $tb = Test::Builder->new;
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:
sub is_tuple {
my ($got, $expected, $axis, $name) = @_;
my $pass = 0;
my $diag = '';
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:
$tb->diag( $diag ) unless $pass;
$tb->ok( $pass, $name );
Yes that is all. Do not forget to:
require Exporter;
our @ISA = qw(Exporter);
our @EXPORT = qw(is_tuple);
But this was really it. It didn't even hurt and we didn't have to see Detroit.
The Tests
More challenging I found it to write the test file that checks the logic code I didn't show in the above example.
use v5......;
use warnings;
use lib '.',...;
use Test::More tests => ...;
use Test::Builder::Tester;
use Test::Color;
This is mostly your usual start template of any test file. The lib pragma needs to receive the directory where the module lives, that contains is_tuple, which would be in my case Test::Color. And we need Test::Builder::Tester to test what we built with Test::Builder (the module names fit).
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");
Our first little smoke test seems trivial. We call is_tuple with the the result values of some operation ($got) and the values to check them against ($expected), 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 is_tuple 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 is_tuple.
Now we are ready for the juicy bit. How to test a failing test? I mean by that: is_tuple will fail because we gave it bad data by purpose. And if is_tuple 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:
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");
First we tell via test_out again what STDOUT suppose to receive. Then we tell via test_err 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: test_fail(+1); 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 test_err 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.
One last useful hack. You noticed I called the test that is_tuple 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.
CPAN Testers produce a lot of data. Every CPAN 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.
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 the CPAN Testers Matrix (by Slaven) or CPAN Testers Magpie (by Scott Baker). But this, too, is a lot of manual effort.
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 the 2026 Perl Toolchain Summit in Vienna Austria, I have built an interface so agents can easily discover and analyze the CPAN Testers data using the Model Context Protocol (MCP.)
By pointing your agent at https://mcp.cpantesters.org, 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 the CPAN Testers MCP project on Github.
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:
"list test reports for all of PREACTION's dists and check for fails"
That second query impressed me. With this, CPAN authors could remove a lot of the tedium from diagnosing failures from user test reports!
This work was made possible by the sponsors of the 2026 Perl Toolchain Summit in Vienna, Austria: The Perl and Raku Foundation, Grant Street Group, Geizhals Preisvergleich, Vienna.pm, SUSE, Trans-Formed Media LLC, Ctrl O, Simplelists, Harald Joerg, Michele Beltrame (Sigmafin, Laurent Boivin.
perlgov: streamline the (non-)currentness reminder
perlgov: make currentness of member lists explicit
perlgov: turn PSC/Core member lists into subsections
Another explosive weekly challenge. And by explosive I mean exponential complexity. Let's see what we've got. First I'll adjust the background music for equilibrium ... Comfortably Numb
Relax, I'll Need Some Information First
You are given an array of numbers. Write a script to find all subsets where the sum of elements equals the sum of their indices.
# Example 1 Input: @nums = (2, 1, 4, 3)
#. Output: (2, 1), (1, 4), (4, 3), (2, 3)
# Subset 1: (2, 1) Values: 2 + 1 = 3 Positions: 1 + 2 = 3
# Subset 2: (1, 4) Values: 1 + 4 = 5 Positions: 2 + 3 = 5
# Subset 3: (4, 3) Values: 4 + 3 = 7 Positions: 3 + 4 = 7
# Subset 4: (2, 3) Values: 2 + 3 = 5 Positions: 1 + 4 = 5
#
# Example 2 Input: @nums = (3, 0, 3, 0)
# Output: (3, 0), (3, 0, 3)
# Subset 1: (3, 0) Values: 3 + 0 = 3 Positions: 1 + 2 = 3
# Subset 2: (3, 0, 3) Values: 3 + 0 + 3 = 6 Positions: 1 + 2 + 3 = 6
#
# Example 3 Input: @nums = (5, 1, 1, 1)
# Output: (5, 1, 1)
# Subset 1: (5, 1, 1) Values: 5 + 1 + 1 = 7 Positions: 1 + 2 + 4 = 7
#
# Example 4 Input: @nums = (3, -1, 4, 2)
# Output: (3, 2), (3, -1, 4)
# Subset 1: (3, 2) Values: 3 + 2 = 5 Positions: 1 + 4 = 5
# Subset 2: (3, -1, 4) Values: 3 + (-1) + 4 = 6 Positions: 1 + 2 + 3 = 6
#
# Example 5 Input: @nums = (10, 20, 30, 40)
# Output: ()
Just the Basic Facts, Can You Show Me Where It Hurts?
Perusing the examples, I see that there are a couple of unstated requirements. From example 1, the improper subset (1,2,3,4) should work, but it's not in the given output, so apparently that should be excluded. And in example two, the subset (3) should work, but it's not in the output, so apparently subsets have to have at least two members.
I don't see any way of doing this other than generating all possible subsets and checking if they work. My approach is going to be to number the positions, and generate all possible subsets of the positions. Extracting members from the given list will be done with a hash slice. There's going to be a little annoyance that the task numbers positions starting from 1, while Perl array indexing is based from 0.
For a set of n members, there are 2n-1 possible subsets. I know that a way to generate subsets is to count from 1 to 2n-1 and check which bits are 1s in the binary representation. I know how to generate all possible subsets, but do I want to? Because I also know that there are CPAN modules that do this, and I have Algorithm::Combinatorics hanging around from previous problems, with its convenient subsets function, so let's use that.
I Do Believe It's Working, Good
sub sseq(@nums)
{
use Algorithm::Combinatorics qw/subsets/;
use List::Util qw/sum0/;
my @result;
my $s = subsets( [0 .. $#nums] );
while ( my $position = $s->next() )
{
my $size = scalar(@$position);
# Only proper subsets of size 2 or more
next if $size == scalar(@nums) || $size < 2;
# Subsets are indexed at zero, but positions at 1, so compensate in sum
my $sumPosition = $size + sum0 @$position;
my $sumNumbers = sum0 @nums[ @$position ];
if ( $sumNumbers == $sumPosition )
{
push @result, [@nums[@$position]]
}
}
return \@result;
}
Your Lips Move, But I Can't Hear What You're Saying
Explanatory notes:
use Algorithm::Combinatorics qw/subsets/-- This function creates an iterator to generate consecutive subsets from a given list of members. We're going to pass it a list of positions, from 0 to the last index of@nums.use List::Util qw/sum0/--sum0handles empty lists by returning zero instead of an error, assumdoes. That shouldn't come up in this particular function, but I generally usesum0as my default, because I invariably get a warning fromsumabout an empty array and end up changing it anyway.my $size = scalar(@$position)-- the size of thepositionarray comes up repeatedly, so let's get a handle on it.my @sumPosition = $size + sum0(...)-- The sum we're looking for is based on using positions numbered from 1, but we have them numbered from 0. We need to add 1 to each of the positions, which is the same as adding the number of positions.my @sumNumbers = sum0 @nums[ @$position ]-- Here's why I wanted positions to be indexed from zero: so that they could be used in a hash slice to extract the corresponding numbers.push @result, [@nums[@$position]]-- the result of this function is going to be a reference to an array of arrays (it's array reference turtles all the way down). Once again, the hash slice, now encased in[]to create an array reference.
That'll Keep You Going Through the Show
This function answers the question, but it leaves two things undone.
First, the order of the lists is unspecified. There's no apparent reason for the order in the examples, and I quickly found out that the subsets iterator doesn't return subsets in the same order as the examples.
To create unit tests, I want to check that arrays contain the same members, but order doesn't matter. In the Test2 suite, this can be accomplished by putting the expected elements into a bag data structure.
sub runTest
{
use Test2::V0;
use Test2::Tools::Compare qw/bag item/;
my $check;
$check = bag { item [1,4]; item [2,1]; item [2,3]; item [4,3]; end(); };
is( sseq(2,1,4,3), $check, "Example 1");
[...]
Our bags are checked, so ...
Come On, It's Time To Go
Second, to display the output, we have to unwrap the array references and render them as formatted strings.
say join ", ", map { "(" . join(", ", $_->@*) . ")" } sseq(@ARGV)->@*;
What's going on here?
-
sseq(@ARGV)->@*-- Call our function, using the command line arguments, and dereference it (which yields an array of array references). -
map { ... }-- Transform each of the array references -
"(" . join(", ", $_->@*) . ")"--$_is a reference to an array, so$_->@*is the list of elements in the array. Make it a comma-separated string, encased in parentheses. -
say join ", ", ...-- output each of those parenthesized strings, separated by commas
I Have Become Comfortably Numb
Equilibrium achieved.
Add missing _ in comment
I am currently trying to install this module with :
perl -MCPAN -e shell install XML::Parser::Style::Tree
It did not install it, but it installed cpan. Inside cpan, I issued again:
install XML::Parser::Style::Tree
At beginning, the process was going well. Then suddenly I receive these error messages:
Can't locate File/ShareDir/Install.pm in @INC (@INC contains: ./inc /etc/perl /usr/local/lib/perl/5.10.1 /usr/local/share/perl/5.10.1 /usr/lib/perl5 /usr/share/perl5 /usr/lib/perl/5.10 /usr/share/perl/5.10 /usr/local/lib/site_perl .) at Makefile.PL line 7.
BEGIN failed--compilation aborted at Makefile.PL line 7.
Warning: No success on command[/usr/bin/perl Makefile.PL INSTALLDIRS=site]
Warning (usually harmless): 'YAML' not installed, will not store persistent state
TODDR/XML-Parser-2.58.tar.gz
/usr/bin/perl Makefile.PL INSTALLDIRS=site -- NOT OK
Running make test
Make had some problems, won't test
Running make install
Make had some problems, won't install
Could not read '/home/leopoldo/.cpan/build/XML-Parser-2.58-jE9k8s/META.yml'. Falling back to other methods to determine prerequisites
Failed during this command:
TODDR/XML-Parser-2.58.tar.gz : writemakefile NO '/usr/bin/perl Makefile.PL INSTALLDIRS=site' returned status 512
So, I am open to suggestions on how to solve it.
Originally published at Perl Weekly 770
Hi there,
I like to believe that I belong to the old school of testing i.e. Test::More. That being said, every now and then I come across a magical test workflow. One of them is App::Yath. The biggest and pleasant surprise is that both were presented by none other than Chad Granum. Although I love the idea, I find it hard to adapt to it. I have to force myself otherwise it won't happen. I wrote a short blog post as a reminder to myself while working on this editorial. I can refer to it next time I am changing existing code or adding new code.
Speaking of testing, Herbert Breunung shared a blog post explaining how Test::Builder helped him create his own test suite. This reminded me how I did exactly that in 2010 to create Test::Map::Tube for my routing framework, Map::Tube. It makes me feel quite old now!
Enjoy rest of the newsletter.
--
Your editor: Mohammad Sajid Anwar.
Announcements
The London Perl and Raku Workshop 2026
Just a heads up: The next LPW is planned to be in November 2026.
PPC Summer 2026 - Call for Participation!
Brett Estrade, who is a member of the Perl Community Conference (PPC) organising committee, has released a call for participants for the Summer 2026 PPC. He notes the group's success in the areas of scientific research and community building through the use of independent Perl conferences and asks for speakers to submit proposals to present their work via the Papercall system to help keep the momentum going.
Articles
Who tests the tester? Me !!!
In the article, Lichtkind shows how to apply the Test::Builder::Tester module to check your own test functions for correctness. He shares real-life example of using the is_tuple() function that he has developed to aid in creating a test for the GTC project.
This week in PSC (222) | 2026-04-25
Ihe Perl Steering Council got together at the 2026 Perl Toolchain Summit (Vienna). During this meeting, the group discussed the current status of Perl 5.44. Most blockers have been resolved and EOL workflow has been put in place. Finalisation of details for the next stable release is currently taking place.
Importance of Repositories in Public
In this blog post, Mikko argues that keeping project repositories publicly accessible is an important practice even when no new contributions are anticipated. Having a project public on GitHub preserves historical context, serves as an example of quality work for others, and provides a source of useful data for the open-source community.
AI as a Chance - Opinion
In this article PetaMem discussed how through AI we will have access to new ways to enhance our creative processes (as opposed to replacing them), through developing a partnership with AI; allowing developers and creators alike use the technology of AI to automate repetitive processes and create opportunities for problems to be solved and new ideas developed through creative innovation.
Discussion
AI Contributions to CPAN: The Copyright Question
In his article, Todd explores some of the legal issues surrounding code that is generated by AI and available through CPAN, and whether or not these AI-generated contributions are eligible for copyright protection and/or a legal license. He also cautions maintainers about the use of black box LLMs, which do not make public their training data, and encourages the Perl community to develop guidelines for the proper protection of the Perl software ecosystem from future disputes arising from improper use of code generated by LLMs.
Web
Use Your Powers Only for Good, Clark
Dave dissects a really good looking SPAM email made by an AI LLM to show us all how the world will look in future with AI created outreach. Even though the spam had accurately scraped Dave's background and book titles, Dave believes that this creates the illusion of understanding but does not demonstrate the LLM has any real knowledge of either Dave's values or business model. Dave further states that this type of outreach is now cheaper and easier than ever though the deployment of AI to create large amounts of unsolicited SPAM.
The Weekly Challenge
The Weekly Challenge by Mohammad Sajid Anwar will help you step out of your comfort-zone. You can even win prize money of $50 by participating in the weekly challenge. We pick one champion at the end of the month from among all of the contributors during the month, thanks to the sponsor Marc Perry.
The Weekly Challenge - 371
Welcome to a new week with a couple of fun tasks "Missing Letter" and "Subset Equilibrium". If you are new to the weekly challenge then why not join us and have fun every week. For more information, please read the FAQ.
RECAP - The Weekly Challenge - 370
Enjoy a quick recap of last week's contributions by Team PWC dealing with the "Popular Word" and "Scramble String" tasks in Perl and Raku. You will find plenty of solutions to keep you busy.
Perl Weekly Challenge 370: Popular Word
Abigail offers a superb example of processing text for the "Popular Word" task using the ability of Perl's regex engine to process words without considering case and complex punctuation marks. This solution is unique because it demonstrates a high-performance solution for filtering out banned words while maintaining concise and easily readable code.
Perl Weekly Challenge 370: Scramble String
Abigail used a clever recursive method to take on the challenge of the "Scramble String" problem. The use of clear base cases and logic to explore possible points to split and swap all contribute to the final solution being an excellent example of how to solve complex problems with string transformations, using a valid solution and providing a framework that can be used to solve other complex problems involving string transformations.
Popular Scramble
Arne's experience in comparing Raku's expressive power vs. Perl with respect to the solutions of these two challenges are at the heart of this review. He also demonstrates how to use Raku's built-in Bag and Any types to simplify complex logic with clean, idiomatic code. This article is a great reference for developers who want to learn how to use modern functional programming patterns effectively.
PWC 370 Scramble On, Scramblin' Man
Bob has created a detailed breakdown of the Scramble String problem, looking at the need to use memoisation to avoid redundantly calculating recursive algorithms. Bob has developed a very clearly constructed solution to the Scramble String problem using test-first development which is a perfect demonstration of how to keep performance and readability on complex branching logic.
Perl Weekly Challenge: Week 370
In this article, there is a thorough comparison made between two languages: Perl and Raku. The many modern capabilities of Raku, such as having a Bag data structure and being natively supportive of methods for manipulating strings, resulted in solving the stated problem while producing significantly less code than what is generated by using Perl to do the same thing.
Scrambled Bans
Jörg has concentrated his efforts solely on providing a high quality Perl solution to this challenge. The way he solves "Scramble String" is especially impressive because he uses a recursive solution that processes challenging partitioning and swapping of strings, using clean and idiomatic Perl programming.
Perl Weekly Challenge 370
W. Luis Mochán offers a mathematically rigorous and elegant take on Challenge 370 using Perl. For Task 1, he utilises a compact approach by normalising the input with lc and regex, then applying a frequency count. His review is particularly positive about the clarity and efficiency of using a single hash to filter out banned words while identifying the maximum frequency in one pass. For Task 2, he implements a recursive solution and highlights the technical necessity of caching results to avoid the exponential growth of possibilities in longer strings.
Scrambling Back and Forth
Matthias's submission for Task 1, he has used a concise and efficient one-liner with List::UtilsBy::max_by to count how many times words appear and in doing so stores a reference to the 'most popular' result while processing all results in a single pass. With respect to Task 2, Matthias did not limit himself to performing a simple recursive search as in previous weeks; however, he developed a Reverse Scramble algorithm, which is performing this task as a sequence of sorting in which strings become indexes of streaks representing their sorted order to allow for efficient verification of scrambling operations performed.
The scramble can not stop you / from becoming popu-ler… lar
Packy's polyglot Challenge 370 features solid code samples in Perl, Raku, Python, and Elixir that showcase how to solve the problem multiple ways using different programming languages. He shows how using Elixir's pipe operator or Raku's Bag can help efficiently transform and filter the paragraph from "Most Popular Word". He also demonstrates a consistent, recursive approach to solving the problem using the same recursive logic, with code examples in each of the four languages, while adapting to the specific syntax and idioms of each language, in his example "Scramble String".
Words and more words
Peter implements a clean regular expression approach to normalising the text and eliminating any banned words in Popular Word, while also provides a completely recursive solution in Scramble String that clearly demonstrates splitting and swapping parts of the string in order to illustrate the property of being scrambled.
The Weekly Challenge - 370: Popular Word
Reinier solution includes a simple regular expression to remove punctuation characters and a hash to count the number of occurrences of each valid word. This makes it easy to identify the most frequently used word that is not on a banned list, and provides an easily readable model from which to analyze text.
The Weekly Challenge - 370: Scramble String
Reinier's demonstrates pruning as an optimisation technique. If the characters sorted for both strings do not match (meaning they are not anagrams), the function returns false without going through all remaining recursive levels.
The Weekly Challenge #370
Robbie's high-performance Perl solutions employ algorithmic efficiency and strong input validation. In Task 1, he uses a fast normalisation method along with a hash-based frequency counting scheme to isolate the most popular word. In Task 2, Robbie's "Scramble String" implementation is notable for being a recursive Divide-and-Conquer strategy, providing an additional speedup by implementing a pre-check anagram filter to eliminate expensive recursive calls to incompatible strings.
Popular Scramble
Roger shown how to use a specialised programming tool like Counter in Rust to find popular words with only a few lines of code. In addition, the example in PostScript is an interesting concept as it shows how to create a counted hash from scratch using the examples provided in the Rust program as a reference.
Popular Scrambling
Simon demonstrates Task 1 by methodically normalising strings and counting strings using hashes in order find the word with the highest occurrence. His solution for Task 2 highlights the use of recursion when solving the problem, and optimises by checking if two strings are anagrams with a "fail-fast" method; this way, all non-anagrams can be filtered out before going through the more complex process of recursion.
Perl Tutorial
A section for newbies and for people who need some refreshing of their Perl knowledge. If you have questions or suggestions about the articles, let me know and I'll try to make the necessary changes. The included articles are from the Perl Maven Tutorial and are part of the Perl Maven eBook.
Testing in Perl
The 'slides' with all the examples that are used in the online course.
Rakudo
2026.16 Selkie TUI Framework
Training
Testing in Perl
While we are still recording the live sessions of the Testing in Perl you can also watch the previous episodes on the Code Maven Academy web site. Usually it is for paying subscribers only, but for the next couple of day you can still access it free of charge. You only need to register on the web site.
Weekly collections
NICEPERL's lists
Great CPAN modules released last week.
Events
Perl Maven online: Testing in Perl - part 5
April 30, 2026
Boston Perl Mongers virtual monthly
May 12, 2026
The Perl and Raku Conference 2026
June 26-29, 2026, Greenville, SC, USA
You joined the Perl Weekly to get weekly e-mails about the Perl programming language and related topics.
Want to see more? See the archives of all the issues.
Not yet subscribed to the newsletter? Join us free of charge!
(C) Copyright Gabor Szabo
The articles are copyright the respective authors.
On this site and elsewhere, there are quite a few questions about how to handle errors when printing to a pipe in Perl. But none of the answers, solutions and explanations I have seen so far are applicable to my specific case. Please consider the following situation:
Under Linux (Debian 13), I have a folder /path where only the root user has write permissions. Further, I have a Perl script that includes the following snippet:
if (!(open($fh_Pipe, '|-:utf8', '/usr/bin/weasyprint',
'-',
'/path/test.pdf')))
{
# Perform error action here.
}
if (!(print($fh_Pipe 'Foo')))
{
# Perform error action here.
}
If I execute that script as root, everything works: A PDF file /path/test.pdf is created that contains the text "Foo".
But if I execute that script as another user, it does not behave as expected: Indeed, the PDF file /path/test.pdf is not created, and Perl outputs a warning regarding the missing write permission. But to my surprise, none of the error action blocks executes.
That is a problem because I'd like to detect all errors with writing to the pipe from within the script, of course with reasonable effort, and the behavior I have observed seems to contradict the documentation. From the documentation for print:
Prints a string or a list of strings. Returns true if successful. [...]
Although it is formally not correct, I interpret this as it would say "... and false if not successful ..." in addition. But then it contradicts what I observe.
So my question is:
Is there a general method to detect all errors that may occur when printing to a pipe, including situations where the user that executes the respective script simply does not have write permission in the respective directory?
And why does Perl not behave as documented?
[ Side note #1: To be clear, I'm not interested in which error exactly has occurred. I just would like to know whether or not the print has succeeded, completely regardless of the kind of the possible error. ]
[ Side note #2: The page linked above later on explains that a SIGPIPE signal will be raised if we try to print to a closed pipe or socket. However, I didn't try to implement an error handling for print based on signals, because that would be a high effort for a very simple thing, and because I am not sure if a missing write permission would effect the same as a closed pipe. ]
If your girlfriend has been kidnapped by Gollum and the Evil One, and there's nothing you can do, perhaps console yourself with a small programming problem while you listen to Ramble On or Ramblin' Man'.
PWC 370 Task 2: Scramble String
The Task
You are given two strings A and B of the same length. Write a script to return true if string B is a scramble of string A otherwise return false. String B is a scramble of string A if A can be transformed into B by a single (recursive) scramble operation. A scramble operation is:
- If the string consists of only one character, return the string.
- Divide the string X into two non-empty parts.
- Optionally, exchange the order of those parts.
- Optionally, scramble each of those parts.
- Concatenate the scrambled parts to return a single string.
Examples
-
Input:
$str1 = "abc",$str2 = "acb"Output:true- split: ["a", "bc"]
- split: ["a", ["b", "c"]]
- swap: ["a", ["c", "b"]]
- concatenate: "acb"
-
Input:
$str1 = "abcd",$str2 = "cdba"Output:true- split: ["ab", "cd"]
- swap: ["cd", "ab"]
- split: ["cd", ["a", "b"]]
- swap: ["cd", ["b", "a"]]
- concatenate: "cdba"
-
Input:
$str1 = "hello",$str2 = "hiiii"Output:false -
Input:
$str1 = "ateer",$str2 = "eater"Output:true -
Input:
$str1 = "abcd",$str2 = "bdac"Output:false
The Ramblin' Scramblin' Thinkin' Part
This could go two ways: (1) generate every possible scramble of str1 and then look for str2; or (2) do a breadth-first or depth-first search for str2.
The possible number of scrambles could explode. For a length of n, there will be n-1 splits, and then the n-1 string will have n-2 splits, and so on, so we're looking at factorial complexity. Let's not generate every possible scramble unless we have to.
For a search, we'd be trying to match the front or back halves of the two strings, and then recursing on the parts that didn't match. There are finite possibilities for partial matches.
- A simple swap gets us from
str1tostr2:
str1: --- ++++++++++
str2: ++++++++++ ---
- The head or tail of
str1matches the corresponding head or tail of str2, requiring us to recurse on the shorter string that didn't yet match.
str1: === [??????????] || [???] ==========
| scramble scramble |
| | | |
str2: === [.*#.*#.*#.] || [.*#] ==========
- The head or tail of
str1matches the back or front ofstr2. Similar to the case above, except that a swap is required before we recurse.
str1: === [??????????] || [???] =========
/ \
scramble scramble
/ \
str2: [.*#.*#.*#.] === || ========= [.*#]
A Bold Strategy, Cotton. Let's see if that works out for him.
After an embarrassing amount of trial and error (you might say I was scrambling), my solution converged on this longer-than-usual recursive function.
sub isScramble($str1, $str2, $depth = "")
{
state %remember;
%remember = () if $depth eq "";
my $key = ":$str1:$str2:";
if ( exists $remember{$key} )
{
$logger->debug("${depth}CACHED $key = ", ($remember{$key} ? "true":"false"));
return $remember{$key}
}
my $len = length($str1);
return ( $remember{$key} = false) if $len != length($str2);
return ( $remember{$key} = true) if $str1 eq $str2;
for ( 1 .. $len-1 )
{
# $_ is the length of the left substring (head), $len-$_ is length of right (tail)
my ( $head, $tail) = ( substr($str1, 0, $_), substr($str1, $_) );
my ($s2head, $s2tail) = ( substr($str2, 0, $_), substr($str2, $_) );
my $s2front = substr($str2, 0, $len-$_); # length of $tail, on left side
my $s2back = substr($str2, -$_); # length of $head, on right side
$logger->debug("${depth}Compare [$head/$tail] <> s2head/$s2tail] ($s2front/$s2back)");
if ( "$tail$head" eq $str2 )
{
$logger->debug("${depth}FOUND $str2");
return $remember{$key} = true;
}
elsif ( $head eq $s2head )
{
$logger->debug("${depth}HH, compare $tail <> $s2tail");
return true if $remember{$key} = isScramble($tail, $s2tail, " $depth");
}
elsif ( $head eq $s2back )
{
$logger->debug("${depth}HB, compare $tail <> s2front");
return true if $remember{$key} = isScramble($tail, $s2front, " $depth");
}
elsif ( $tail eq $s2tail )
{
$logger->debug("${depth}TT, compare $head <> s2head");
return true if $remember{$key} = isScramble($head, $s2head, " $depth");
}
elsif ( $tail eq $s2front )
{
$logger->debug("${depth}TF, compare $head <> s2back");
return true if $remember{$key} = isScramble($head, $s2back, " $depth");
}
else
{
$logger->debug("${depth}No pairs, recurse with $head<>$s2head and $tail<>$s2tail");
return $remember{$key} = true if (
( isScramble($head, $s2head, " $depth")
&& isScramble($tail, $s2tail, " $depth") )
|| ( isScramble($head, $s2back, " $depth")
&& isScramble($tail, $s2front, " $depth") ) );
}
}
$logger->debug("${depth}NOT FOUND $key = false (caching)");
return $remember{$key} = false;
}
Notes:
-
The function starts out with setting up a cache for partial results. During development, I added this near the end of the process, but it shows up at the top of the function, so let's discuss it.
- Recursive search algorithms usually benefit from caching known results. Our worst case is that
str1can't be scrambled intostr2, in which case we'll end up doing the whole exponential tree of possibilities. I expect caching partial results would pay off. - The
%rememberhash is my cache, and it's astatevariable so that it persists across recursive calls. But that means it also persists across invocations for different test cases, so it could yield wrong answers unless I initialize it at the top level of the recursion. - The cache is filled by using in-line assignment every time a result is returned, which is kind of cutely readable, I think.
- Should I have used
Memoize? Maybe. But that caches values based on the arguments passed to the function, and one of my arguments is thedepthto which I've recursed. That means cache misses depending on how I got down the search tree, so I opted for do-it-yourself memo-ization.
- Recursive search algorithms usually benefit from caching known results. Our worst case is that
The function begins by dispensing with the easy cases: having a result in the cache, matching strings, and strings of the wrong length.
Then, for each division of
str1, let's have at hand the substrings that we might be dealing with from bothstr1andstr2. There's a little tour-de-force ofsubstruses here, splitting the strings at a midpoint, and using negative offsets.Most of the body of the function is testing which pairs match up, if any, and doing the appropriate recursion of what remains.
The last case, where neither the head nor the tail of
str1has a matching complement instr2, requires us to recurse on both pieces, and in either order.I used logging liberally, because the function is only obvious in hindsight and took me longer than I'd like to admit. The
$depthargument is mostly a convenience for indenting the log statements appropriately -- notice that it gets longer by two spaces at each recursion. My setup for logging usesLog::Log4perlin easy mode. It's part of my boilerplate for PWC solutions and looks like this:
use Getopt::Long;
my $Verbose = false;
my $DoTest = false;
GetOptions("test" => \$DoTest, "verbose" => \$Verbose);
my $logger;
{
use Log::Log4perl qw(:easy);
Log::Log4perl->easy_init({ level => ($Verbose ? $DEBUG : $INFO ),
layout => "%d{HH:mm:ss.SSS} %p{1} %m%n" });
$logger = Log::Log4perl->get_logger();
}
-
App::Music::ChordPro - A lyrics and chords formatting program
- Version: v6.100.0 on 2026-04-21, with 468 votes
- Previous CPAN version: v6.090.1 was released 3 months, 18 days before
- Author: JV
-
App::Netdisco - An open source web-based network management tool.
- Version: 2.098002 on 2026-04-20, with 864 votes
- Previous CPAN version: 2.098001 was released 3 days before
- Author: OLIVER
-
App::perlimports - Make implicit imports explicit
- Version: 0.000059 on 2026-04-24, with 24 votes
- Previous CPAN version: 0.000058 was released 5 months, 26 days before
- Author: OALDERS
-
Bitcoin::Crypto - Bitcoin cryptography in Perl
- Version: 4.005 on 2026-04-23, with 18 votes
- Previous CPAN version: 4.004 was released 1 month, 5 days before
- Author: BRTASTIC
-
CPANSA::DB - the CPAN Security Advisory data as a Perl data structure, mostly for CPAN::Audit
- Version: 20260419.002 on 2026-04-19, with 25 votes
- Previous CPAN version: 20260412.001 was released 7 days before
- Author: BRIANDFOY
-
CryptX - Cryptographic toolkit
- Version: 0.088 on 2026-04-23, with 53 votes
- Previous CPAN version: 0.087_008 was released the same day
- Author: MIK
-
DateTime::TimeZone - Time zone object base class and factory
- Version: 2.68 on 2026-04-23, with 22 votes
- Previous CPAN version: 2.67 was released 1 month, 17 days before
- Author: DROLSKY
-
DBIx::Class::InflateColumn::Serializer - Inflators to serialize data structures for DBIx::Class
- Version: 0.10 on 2026-04-19, with 13 votes
- Previous CPAN version: 0.09 was released 9 years, 3 months, 4 days before
- Author: MRUIZ
-
Encode - character encodings in Perl
- Version: 3.22 on 2026-04-25, with 65 votes
- Previous CPAN version: 3.21 was released 2 years, 1 month, 28 days before
- Author: DANKOGAI
-
Google::Ads::GoogleAds::Client - Google Ads API Client Library for Perl
- Version: v32.0.0 on 2026-04-22, with 20 votes
- Previous CPAN version: v31.1.0 was released 27 days before
- Author: CHEVALIER
-
Log::Any - Bringing loggers and listeners together
- Version: 1.720 on 2026-04-25, with 69 votes
- Previous CPAN version: 1.719 was released 1 month, 8 days before
- Author: PREACTION
-
MetaCPAN::Client - A comprehensive, DWIM-featured client to the MetaCPAN API
- Version: 2.041000 on 2026-04-25, with 29 votes
- Previous CPAN version: 2.040000 was released 1 month, 16 days before
- Author: MICKEY
-
Module::CoreList - what modules shipped with versions of perl
- Version: 5.20260420 on 2026-04-20, with 45 votes
- Previous CPAN version: 5.20260330 was released 21 days before
- Author: BINGOS
-
PPI - Parse, Analyze and Manipulate Perl (without perl)
- Version: 1.290 on 2026-04-25, with 64 votes
- Previous CPAN version: 1.289 was released the same day
- Author: MITHALDU
-
SPVM - The SPVM Language
- Version: 0.990167 on 2026-04-24, with 36 votes
- Previous CPAN version: 0.990166 was released the same day
- Author: KIMOTO
-
Test2::Harness - A new and improved test harness with better Test2 integration.
- Version: 1.000171 on 2026-04-23, with 28 votes
- Previous CPAN version: 1.000170 was released 13 days before
- Author: EXODIST
-
YAML::LibYAML - Perl YAML Serialization using XS and libyaml
- Version: v0.905.0 on 2026-04-24, with 60 votes
- Previous CPAN version: v0.905.0 was released 1 day before
- Author: TINITA
-
YAML::PP - YAML 1.2 Processor
- Version: v0.40.0 on 2026-04-24, with 27 votes
- Previous CPAN version: v0.40.0 was released 1 day before
- Author: TINITA
-
YAML::Syck - Fast, lightweight YAML loader and dumper
- Version: 1.45 on 2026-04-23, with 18 votes
- Previous CPAN version: 1.44 was released 20 days before
- Author: TODDR
We know this will disappoint many of you, but it was not feasible to keep it running anymore with a reasonable quality.
Several factors led to the decision:
Communication has moved on. 25 years ago it was important to provide a consistent way for users to contact CPAN module authors. Today people use rt.cpan.org, forums, and other channels..
Over 99% of mail was spam. Even two layers of spam filtering let some through, hurting deliverability for our other outbound mail.
Modern email requires ARC, DKIM, DMARC, and SPF. Providing these correctly on a forwarding service is hard, so legitimate mail often fails to be delivered.
We approached several email providers, but were unable to find one to host the service.
The number of active users was small compared to the number of CPAN authors. During a recent extended, unannounced outage, we received only two inquiries.
We will work with the cpan-security team so they keep a way to reach authors.
Bounces will soon report that the MX cpan.org-email-is-gone-use-http-rt.cpan.org cannot be found as a breadcrumb pointing toward alternatives.
cpan.org email was one of the first services we ran for the Perl community; over the years we've added others, some for the wider internet. The perl.org mailing lists have been running for 28 years and continue to operate. We also still help run PAUSE and the CPAN archive system. rt.cpan.org remains available, thanks to The Perl Foundation and Request Tracker.
For most of its 25 years, cpan.org email was widely used, and running it was a pleasure. The last seven or eight became mostly spam fights and deliverability work — a big part of why we're stopping now.
Please don't ask us to reconsider. We recognize this change will affect some users more than others. We wrestled with this for a long time and are confident that it is the right call.
Sincerely,
The perl noc volunteers
The below codes I had been using for many years in order to replace a part of text string in multiple files.
It works super. This code runs at Perl version 5.32.1 around (Rocky9.6).
However, when I tried to more modernization of this code, which means that eliminating -w at the first Hash-Bang line, and includes [ strict; warnings; ] two lines, then the newer Perl interpreter shows errors, somewhere around foreach or opendir or somewhere.
Around this part of codes are came from old Perl interpreter version 5.8..8 (Fedora Code 7).
I just copied this part of codes from textbook or elsewhere.
Which part of the Old Codes doesn't work with the newer [ strict; and warning; ] standard beginning of nowadays?
#! /usr/bin/perl -w
## 2026-04-23
## Global Replacement a transition scr60a6_01_transition_to_rocky9.6.pl
## (-w を削除して )
## strict;
## warnings:
##
##
my $filename = "";
$MAGIC_PATH = "/home/mkido/bin/in";
# This recorgnizes Extention
$MAGIC_EXT1 = 0;
print "Type html, pl, txt OR php.\n";
$MAGIC_EXT1 = <STDIN>; # read the next line
chomp ($MAGIC_EXT1);
print "$MAGIC_EXT1\n"; ## DEBUG, CONFIRMATION
$MAGIC_FROM = "from_text_string";
$MAGIC_TO = "to_text_string";
print "\nhello world\n\n";
opendir DIRHANDLER, $MAGIC_PATH;
@allfiles = readdir DIRHANDLER;
foreach my $filename (sort @allfiles) {
print "$filename\n";
}
print "\n\n";
print "Now is the business\n";
foreach $filename (sort @allfiles) {
if( $filename =~ /$MAGIC_EXT1/ ){
print "$filename\n";
open (IN, "in/$filename") or die ("error:$!");
open (my $fh, '>', "/home/mkido/bin/RESULT/$filename" ) or die "cannot open $filename for writing:$!";
while ( my $line1 = <IN>) {
if( $line1 =~ /http/ ) {
## s/$MAGIC_FROM/$MAGIC_TO/g;
(my $newline1 = $line1 ) =~ s/$MAGIC_FROM/$MAGIC_TO/g;
print $fh $newline1;
} else { print $fh $line1; }
} ### closing while-loop
close(IN);
close($fh);
} ### closing if_filename_has_html
} ### closing a big FOREACH-loop
print "\n\n";
I am on a Windows machine trying to cross-compile with my cross-compiler to use x86_64 for a Linux target machine. I am trying to use a cross compiler to build msquic via a PowerShell command which uses OpenSSL, however I am failing in locating the Pod/Usage.pm.
I am running the command from PowerShell 7 using the command from the msquic/scripts folder:
.\build.ps1 -Platform linux -Arch x64 -ToolchainFile "C:\msquic-2.5.6\nilinuxrt.cmake" -Generator "Unix Makefiles" -Tls openssl -Clean
I have added the line use lib "/c/Strawberry/perl//lib/"; in the OpenSSL Configure file and and added a print statement showing @INC by adding the line: print "DEBUG: \@INC = @INC\\n";
I see my Strawberry folder contains the Pod/Usage.pm. However, I get an error that states the following:
\[ 32%\] OpenSSL configure
DEBUG: @INC =
/c/Strawberry/perl//lib/
/c/msquic-2.5.6/submodules/openssl/util/perl
/usr/lib/perl5/site_perl
/usr/share/perl5/site_perl
/usr/lib/perl5/vendor_perl
/usr/share/perl5/vendor_perl
/usr/lib/perl5/core_perl
/usr/share/perl5/core_perl
/c/msquic-2.5.6/submodules/openssl/external/perl/Text-Template-1.56/lib
Configuring OpenSSL version 3.5.6-dev for target mingw64
Using os-specific seed configuration
Created configdata.pm
Running configdata.pm
Can't locate Pod/Usage.pm in @INC (you may need to install the Pod::Usage module) (@INC entries checked:
/usr/lib/perl5/site_perl
/usr/share/perl5/site_perl
/usr/lib/perl5/vendor_perl
/usr/share/perl5/vendor_perl
/usr/lib/perl5/core_perl
/usr/share/perl5/core_perl) at configdata.pm line 22487.
(Line breaks added for readability)
As we can see that the @INC later when running configdata.pm no longer contains the /c/Strawberry/perl/lib.
I have tried adding PERL5LIB as an environment variable which then produces this error:
Cwd.c: loadable library and perl binaries are mismatched (got second handshake key 0000000a00000890, needed 0000000000000000)Cwd.c: loadable library and perl binaries are mismatched (got second handshake key 0000000a00000890, needed 0000000000000000)
I believe what is happening is submake files are reverting to use my GIT perl instead and I am not sure how to fix this.
For debugging, I am using msquic.2.5.6 and strawberry perl 5.38.2.2.
TL;DR
This is a rant, a developer war story of how to use docker to work around a problem that shouldn’t exist in the first place. Configure your PHP project with a different, lower version, of PHP than you are using yourself. Read how I spent an evening fighting PHP.
The bare minimum
Can someone in PHP land please tell me why composer is unable to create a project on PHP version 8.2 when I’m running PHP version 8.4? I’m creating a PHP app that needs to run on bookworm, aka, Debian 12, which ships PHP 8.2.
Week 370
My Solutions
Task 1 : Popular Word (By M. Anwar)
You are given a string paragraph and an array of the banned words. Write a script to return the most popular word that is not banned. It is guaranteed there is at least one word that is not banned and the answer is unique. The words in paragraph are case-insensitive and the answer should be in lowercase. The words can not contain punctuation symbols.
For frequency of words I immediately plan to use a hash. My only hangup on this one was when the input paragraph was not space separated but used punctuation marks instead. This could be more robust, but that's an abnormal input. I turn everything lowercase, check for spaces, strip out the punctuation marks, and put the word count in the hash %h. Then we ignore banned words.
sub strip($w) {
my $out;
for my $letter (split '', $w) {
$out .= $letter if ($letter =~ /\w/);
}
return $out;
}
sub proc($paragraph, @banned) {
say "Input: \$paragraph = $paragraph\n\t\@banned = @banned";
my @words;
if ($paragraph =~ /\s/) {
@words = split ' ', lc $paragraph;
} else {
my $word;
for my $letter (split '', lc $paragraph) {
if ($letter =~ /[a-zA-Z]/) {
$word .= $letter;
} else {
push @words, $word;
$word = "";
}
}
}
my %h;
foreach my $word (@words) {
$word = strip($word);
$h{$word}++;
}
my $max = 0;
my $max_word;
for my $w (keys %h) {
my $ban = 0;
for my $banned_word (@banned) {
if ($w eq $banned_word) {
$ban = 1;
last;
}
}
next if ($ban);
if ($max < $h{$w}) {
$max_word = $w;
$max = $h{$w};
}
}
say "Output: $max_word";
}
Task 2: Scramble String (By R. B-W)
You are given two strings $str1 and $str2 of the same length. Write a script to return true if $str2 is a scramble of $str1 otherwise return false. String B is a scramble of string A if A can be transformed into B by a single (recursive) scramble operation.
Mr. Roger Bell-West then goes on to explain what a scramble is.
- If the string consists of only one character, return the string.
- Divide the string X into two non-empty parts.
- Optionally, exchange the order of those parts.
- Optionally, scramble each of those parts.
- Concatenate the scrambled parts to return a single string.
When the task was first described, he had suggested choosing a random location for the split, and randomly deciding to perform the scrambles or swaps. I decided to embrace that approach.
I loop the scramble 10,000 times to increase the odds of success.
sub scramble($s) {
my $len = length($s);
if ($len == 1) {
return $s;
} else {
my $pt = 1 + ($len - 1) * rand();
my $a = substr $s, 0, $pt;
my $b = substr $s, $pt;
my $a_new = (int 2*rand() == 0) ? $a : scramble($a);
my $b_new = (int 2*rand() == 0) ? $b : scramble($b);
my $out = (int 2*rand() == 0) ? $a_new.$b_new : $b_new.$a_new;
}
}

There are only 2 days left to submit a talk for TPRC! The cutoff is April 21. If you have an idea for a talk, it is definitely time to get it submitted. We need a wide variety of speakers and topics, so give it a try! Go to https://tprc.us/ to make your submission.
-
App::Netdisco - An open source web-based network management tool.
- Version: 2.098001 on 2026-04-16, with 859 votes
- Previous CPAN version: 2.098000 was released the same day
- Author: OLIVER
-
Authen::Passphrase - hashed passwords/passphrases as objects
- Version: 0.009 on 2026-04-15, with 14 votes
- Previous CPAN version: 0.008 was released 14 years, 2 months, 11 days before
- Author: LEONT
-
Convert::Pheno - A module to interconvert common data models for phenotypic data
- Version: 0.31 on 2026-04-17, with 15 votes
- Previous CPAN version: 0.30 was released 2 days before
- Author: MRUEDA
-
CPANSA::DB - the CPAN Security Advisory data as a Perl data structure, mostly for CPAN::Audit
- Version: 20260412.001 on 2026-04-12, with 25 votes
- Previous CPAN version: 20260405.001 was released 7 days before
- Author: BRIANDFOY
-
Finance::Quote - Get stock and mutual fund quotes from various exchanges
- Version: 1.69 on 2026-04-18, with 149 votes
- Previous CPAN version: 1.68_02 was released 1 month, 5 days before
- Author: BPSCHUCK
-
Imager - Perl extension for Generating 24 bit Images
- Version: 1.030 on 2026-04-13, with 68 votes
- Previous CPAN version: 1.029 was released 6 months, 7 days before
- Author: TONYC
-
JSON::Schema::Modern - Validate data against a schema using a JSON Schema
- Version: 0.638 on 2026-04-18, with 16 votes
- Previous CPAN version: 0.637 was released 10 days before
- Author: ETHER
-
SPVM - The SPVM Language
- Version: 0.990162 on 2026-04-18, with 36 votes
- Previous CPAN version: 0.990161 was released the same day
- Author: KIMOTO
-
version - Structured version objects
- Version: 0.9934 on 2026-04-12, with 22 votes
- Previous CPAN version: 0.9933 was released 1 year, 7 months, 17 days before
- Author: LEONT
Zsh has regexp-replace, you don’t need sed:
Also, instead of hard coding values, use zstyle:

Tony writes:
``` [Hours] [Activity] 2026/03/02 Monday 1.55 #24228 follow-up comment, check updates, research and comment 0.75 #24187 review updates, mark comment resolved, research 0.97 #24242 review, research 0.40 #24242 debugging and comment
1.02 #24001 debugging, research, testing
4.69
2026/03/03 Tuesday 0.15 #24242 review dicsussion 0.10 #24211 review discussion and apply to blead 0.53 #24242 comment 0.23 #24239 review and comment 0.18 #24223 review and approve 0.40 #24244 review and comment 0.58 #24245 review and approve 0.07 #24247 review, existing comments seem fine 0.50 #24187 review more, comments 0.08 #24244 review update and approve
0.23 #24195 research
3.05
2026/03/04 Wednesday 0.88 #24252 review, research and comments 0.75 #24251 review, research and comments 0.90 #24253 review, comments 0.12 #24239 review updates and approve 0.28 #24208 comment with guide to update
0.15 #24208 review update and approve
3.08
2026/03/05 Thursday 0.68 #24254 review and comments 0.18 #24256 review and approve 0.13 #24247 check CI results and restart an apparent spurious failure 0.18 #24241 review CI failures and comment
0.40 #24228 compare to #24252 behaviour, testing
1.57
2026/03/09 Monday 0.33 #24254 review updates and approve 0.40 #24253 review updates and comment 1.38 #24252 review updates and comments, research, testing and follow-up
0.68 #24105 rebase, testing
2.79
2026/03/10 Tuesday 0.57 test 5.42.1 on fedora, looks ok, message on list indicates likely a local problem 2.70 #24105 check everything covered, various fixes, testing,
push for CI
3.27
2026/03/11 Wednesday 0.88 #24105 check CI results, fixes, push for more CI 0.57 #24187 review discussion, research and comment 0.13 #24253 review updates and approve 0.12 #24252 review updates and approve with comment 0.23 #24228 review updates and approve 0.15 #24252 approve with perldelta update 1.15 #24001 debugging (what is PL_curcopdb?)
1.20 #24001 debugging, research
4.43
2026/03/12 Thursday 1.12 #24265 review, research and comment
1.33 #24001 research, testing, needs some thought
2.45
2026/03/13 Friday
0.82 research, email to list about benchmarking
0.82
2026/03/16 Monday 0.10 #24208 review updates and apply to blead 2.47 #24272 profiling, benchmarking, comment and work on bisect 0.75 #24272 review bisect results, confirm bisect results, briefly try to work out cause, long comment with results
0.32 #24287 review and approve
3.64
2026/03/17 Tuesday 0.23 #24265 recheck and approve 0.92 #24001 re-work, research and testing 0.57 #24105 rebase and testing, minor fix and push for CI 1.13 #24272 try to diagnose
0.45 #24056 re-work commit message
3.30
2026/03/18 Wednesday 0.40 #24105 check CI results, re-check, make PR #24294 0.75 #24099 review, research and comment 0.22 #24296/#24295 research and comment (both have the same problem)
1.37 #24277 review, testing, comment
2.74
2026/03/19 Thursday 2.05 #24227 research and comments
1.22 #24272 debugging
3.27
2026/03/20 Friday
0.53 #24251 research and follow-up
0.53
2026/03/23 Monday 0.40 #24251 review updates, research and approve with comment 1.22 #24304 review, comment 0.15 #24313 review, research and apply to blead 0.10 #24310 review (nothing to say) 0.17 #24309 review, research and approve 0.13 #24305 review and approve 0.53 #24290 review 1.32 #24056 more update commit message, simplify perldelta
note, push and update OP on PR
4.02
2026/03/24 Tuesday 0.32 #24318 review and review ticket, start workflow, research and comment 0.08 #24301 review and approve 0.37 #24290 more review and comments 0.53 #24289 review, research current PSC and approve with comment 0.38 #24288 review, research and comment 0.18 #24285 review, research and approve 0.72 #24282 review, research and comment
1.23 #24290 review updates, testing and more comment
3.81
2026/03/25 Wednesday 0.15 #24056 check rules, apply to blead 0.30 #24308 review, research and comments 0.82 #24304 review, research and comment, consider Paul’s reply 1.55 #23918 string comparison APIs, research, open #24319 0.13 #24290 review updates and follow-up
1.50 #24005 start on perldebapi, research
4.45
2026/03/26 Thursday 1.25 #24326 review and comment 0.47 #24290 review updates, comment and approve with comment 0.40 #24326 review, comment on side issue and approve 0.28 #24323 review, try to find the referenced documentation, comment 0.10 #24324 review and approve
0.13 #24323 review update and approve
2.63
2026/03/30 Monday 0.80 #24308 review updates and comments 0.08 #24290 review discussion and apply to blead 1.05 #24304 review updates and comment, long comment 1.55 #23676 research, make APIs public and document, testing and push for CI 0.60 #24187 review updates
1.08 #24187 testing, comment
5.16
2026/03/31 Tuesday 1.22 #23676 comment, comment on PR regarding qerror() name, research, work on perldelta 0.47 github notifications, minor updates 0.53 #24332 review original ticket discussion and the change, approve with comment 0.23 #24329 review, research and apply to blead 0.32 #24281 review, try to get a decent view, given github’s tab mis-handling, comment 0.12 #24280 review, comments 0.35 #23995 research and comment 0.08 #24105 follow-up on PR 24294
0.50 #24251 follow-up comment
3.82
Which I calculate is 63.52 hours.
Approximately 51 tickets were reviewed or worked on, and 6 patches were applied. ```

Paul writes:
A couple of bugfixes in March, combined with starting to line up a few development ideas to open 5.45 with.
- 2 = Bugfix for
fieldrefalias memory leak- https://github.com/Perl/perl5/pull/24254
- 2 = Improved
fieldperformance- https://github.com/Perl/perl5/pull/24265
- 3 = Continue progress on implementing PPC0030
- https://github.com/Perl/perl5/pull/24304 (draft)
- 2 = Bugfix for deferred class seal
- https://github.com/Perl/perl5/pull/24326
Total: 9 hours
Besides working up to the 5.44 release, my main focus now will be
getting things like PPC0030, magic-v2, attributes-v2, and various
class feature improvements lined up ready for the 5.45 development
cycle.

Dave writes:
Last month was spent looking into race conditions in threads and threads::shared. I initially started looking at a specific ticket, where (with effort) I could reproduce a specific crash by running many instances in parallel for several hours. I think I have fixed that specific bug, but that led me to the rabbit hole of dynamic thread-safety checkers such as helgrind, and I am currently plunging down the rabbit hole of issues which that tools is flagging up.
Nothing has been pushed yet.
Summary:
- 17:06 GH #24258 dist/threads/t/free.t: Rare test failure in debugging build on FreeBSD
Total:
- 17:06 TOTAL (HH::MM)
Let’s Make a Drum Machine application! Yeah! :D
There are basically two important things to handle: A MIDI “clock” and a groove to play.
Why asynchronous? Well, a simple while (1) { Time::HiRes::sleep($interval); ... } will not do because the time between ticks will fluctuate, often dramatically. IO::Async::Timer::Periodic is a great timer for this purpose. Its default scheduler uses system time, so intervals happen as close to the correct real-world time as possible.
Clocks
A MIDI clock tells a MIDI device about the tempo. This can be handed to a drum machine or a sequencer. Each clock tick tells the device to advance a step of a measured interval. Usually this is very short, and is often 24 pulses per quarter-note (four quarter-notes to a measure of four beats).
Here is code to do that, followed by an explanation of the parts:
#!/usr/bin/env perl
use v5.36;
use feature 'try';
use IO::Async::Loop ();
use IO::Async::Timer::Periodic ();
use MIDI::RtMidi::FFI::Device ();
my $name = shift || 'usb'; # MIDI sequencer device
my $bpm = shift || 120; # beats per minute
my $interval = 60 / $bpm / 24; # time / bpm / clocks-per-beat
# open the named midi device for output
my $midi_out = RtMidiOut->new;
try { # this will die on Windows but is needed for Mac
$midi_out->open_virtual_port('RtMidiOut');
}
catch ($e) {}
$midi_out->open_port_by_name(qr/\Q$name/i);
$midi_out->start; # start the sequencer
$SIG{INT} = sub { # halt gracefully
say "\nStop";
try {
$midi_out->stop; # stop the sequencer
$midi_out->panic; # make sure all notes are off
}
catch ($e) {
warn "Can't halt the MIDI out device: $e\n";
}
exit;
};
my $loop = IO::Async::Loop->new;
my $timer = IO::Async::Timer::Periodic->new(
interval => $interval,
on_tick => sub { $midi_out->clock }, # send a clock tick!
);
$timer->start;
$loop->add($timer);
$loop->run;
The above code does a few things. First it uses modern Perl, then the modules that will make execution asynchronous, and finally the module that makes real-time MIDI possible.
Next up, a $name variable is captured for a unique MIDI device. (And to see what the names of MIDI devices on the system are, use JBARRETT’s little list_devices script.) Also, the beats per minute is taken from the command-line. If neither is given, usb is used for the name, and the BPM is set to “dance tempo.”
The clock needs a time interval to tick off. For us, this is a fraction of a second based on the beats per minute, and is assigned to the $interval variable.
To get the job done, we will need to open the named MIDI device for sending output messages to. This is done with the $name provided.
In order to not just die when we want to stop, $SIG{INT} is redefined to gracefully halt. This also sends a stop message to the open MIDI device. This stops the sequencer from playing.
Now for the meat and potatoes: The asynchronous loop and periodic timer. These tell the program to do its thing, in a non-blocking and event-driven manner. The periodic timer ticks off a clock message every $interval. Pretty simple!
As an example, here is the above code controlling my Volca Drum drum machine on a stock, funky groove. We invoke it on the command-line like this:
perl clock-gen-async.pl
Grooves
What we really want is to make our drum machine actually play something of our own making. So it’s refactor time… Let’s make a 4/4 time groove, with 16th-note resolution, that alternates between two different parts. “4/4” is a “time signature” in music jargon and means that there are four beats per measure (numerator), and a quarter note equals one beat (denominator). Other time signatures like the waltz’s 3/4 are simple, while odd meters like 7/8 are not.
In order to generate syncopated patterns, Math::Prime::XS and Music::CreatingRhythms are added to the use statements. “What are syncopated patterns?”, you may ask. Good question! “Syncopated” means, “characterized by displaced beats.” That is, every beat does not happen evenly, at exactly the same time. Instead, some are displaced. For example, a repeated [1 1 1 1] is even and boring. But when it becomes a repeated [1 1 0 1] things get spicier and more syncopated.
The desired MIDI channel is added to the command-line inputs. Most commonly, this will be channel 9 (in zero-based numbering). But some drum machines and sequencers are “multi-timbral” and use multiple channels simultaneously for individual sounds.
Next we define the drums to use. This is a hash-reference that includes the MIDI patch number, the channel it’s on, and the pattern to play. The combined patterns of all the drums, when played together at tempo, make a groove.
Now we compute intervals and friends. Previously, there was one $interval. Now there are a whole host of measurements to make before sending MIDI messages.
Then, as before, a named MIDI output device is opened, and a graceful stop is defined.
Next, a Music::CreatingRhythms object is created. And then, again as before, an asynchronous loop and periodic timer are instantiated and set in motion.
The meaty bits are in the timer’s on_tick callback. This contains all the logic needed to trigger our drum grooves.
As was done in the previous clock code, a clock message is sent, but also we keep track of the number of clock ticks that have passed. This number of ticks is used to trigger the drums. We care about 16 beats. So every 16th beat, we construct and play a queue of events.
Adjusting the drum patterns is where Math::Prime::XS and Music::CreatingRhythms come into play. The subroutine that does that is adjust_drums() and is fired every 4th measure. A measure is equal to four quarter-notes, and we use four pulses for each, to make 16 beats per measure. This routine reassigns either Euclidean or manual patterns of 16 beats to each drum pattern.
Managing the queue is next. If a drum is to be played at the current beat (as tallied by the $beat_count variable), it is added to the queue at full velocity (127). Then, after all the drums have been accounted for, the queue is played with $midi_out->note_on() messages. Lastly, the queue is “drained” by sending $midi_out->note_off() messages.
#!/usr/bin/env perl
use v5.36;
use feature 'try';
use IO::Async::Loop ();
use IO::Async::Timer::Periodic ();
use Math::Prime::XS qw(primes);
use MIDI::RtMidi::FFI::Device ();
use Music::CreatingRhythms ();
my $name = shift || 'usb'; # MIDI sequencer device
my $bpm = shift || 120; # beats-per-minute
my $chan = shift // 9; # 0-15, 9=percussion, -1=multi-timbral
my $drums = {
kick => { num => 36, chan => $chan < 0 ? 0 : $chan, pat => [] },
snare => { num => 38, chan => $chan < 0 ? 1 : $chan, pat => [] },
hihat => { num => 42, chan => $chan < 0 ? 2 : $chan, pat => [] },
};
my $beats = 16; # beats in a measure
my $divisions = 4; # divisions of a quarter-note into 16ths
my $clocks_per_beat = 24; # PPQN
my $clock_interval = 60 / $bpm / $clocks_per_beat; # time / bpm / ppqn
my $sixteenth = $clocks_per_beat / $divisions; # clocks per 16th-note
my %primes = ( # for computing the pattern
all => [ primes($beats) ],
to_5 => [ primes(5) ],
to_7 => [ primes(7) ],
);
my $ticks = 0; # clock ticks
my $beat_count = 0; # how many beats?
my $toggle = 0; # part A or B?
my @queue; # priority queue for note_on/off messages
# open the named midi output device
my $midi_out = RtMidiOut->new;
try { # this will die on Windows but is needed for Mac
$midi_out->open_virtual_port('RtMidiOut');
}
catch ($e) {}
$midi_out->open_port_by_name(qr/\Q$name/i);
$SIG{INT} = sub { # halt gracefully
say "\nStop";
try {
$midi_out->stop; # stop the sequencer
$midi_out->panic; # make sure all notes are off
}
catch ($e) {
warn "Can't halt the MIDI out device: $e\n";
}
exit;
};
# for computing the pattern
my $mcr = Music::CreatingRhythms->new;
my $loop = IO::Async::Loop->new;
my $timer = IO::Async::Timer::Periodic->new(
interval => $clock_interval,
on_tick => sub {
$midi_out->clock;
$ticks++;
if ($ticks % $sixteenth == 0) {
# adjust the drum pattern every 4th measure
if ($beat_count % ($beats * $divisions) == 0) {
adjust_drums($mcr, $drums, \%primes, \$toggle);
}
# add simultaneous drums to the queue
for my $drum (keys %$drums) {
if ($drums->{$drum}{pat}[ $beat_count % $beats ]) {
push @queue, { drum => $drum, velocity => 127 };
}
}
# play the queue
for my $drum (@queue) {
$midi_out->note_on(
$drums->{ $drum->{drum} }{chan},
$drums->{ $drum->{drum} }{num},
$drum->{velocity}
);
}
$beat_count++;
}
else {
# drain the queue with note_off messages
while (my $drum = pop @queue) {
$midi_out->note_off(
$drums->{ $drum->{drum} }{chan},
$drums->{ $drum->{drum} }{num},
0
);
}
@queue = (); # ensure the queue is empty
}
},
);
$timer->start;
$loop->add($timer);
$loop->run;
sub adjust_drums($mcr, $drums, $primes, $toggle) {
# choose random primes to use by the hihat, kick, and snare
my ($p, $q, $r) = map { $primes->{$_}[ int rand $primes->{$_}->@* ] } sort keys %$primes;
if ($$toggle == 0) {
say 'part A';
$drums->{hihat}{pat} = $mcr->euclid($p, $beats);
$drums->{kick}{pat} = $mcr->euclid($q, $beats);
$drums->{snare}{pat} = $mcr->rotate_n($r, $mcr->euclid(2, $beats));
$$toggle = 1; # set to part B
}
else {
say 'part B';
$drums->{hihat}{pat} = $mcr->euclid($p, $beats);
$drums->{kick}{pat} = [qw(1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1)];
$drums->{snare}{pat} = [qw(0 0 0 0 1 0 0 0 0 0 0 0 1 0 1 0)];
$$toggle = 0; # set to part A
}
}
(You may notice the inefficiency of attempting to drain an empty queue 23 times every 16th note. Oof! Fortunately, this doesn’t fire anything other than a single while loop condition. A more efficient solution would be to only drain the queue once, but this requires a bit more complexity that we won’t be adding, for brevity’s sake.)
On Windows, this works fine:
perl clocked-euclidean-drums.pl "gs wavetable" 90
To run with fluidsynth and hear the General MIDI percussion sounds, open a fresh new terminal session, and start up fluidsynth like so (mac syntax):
fluidsynth -a coreaudio -m coremidi -g 2.0 ~/Music/soundfont/FluidR3_GM.sf2
The FluidR3_GM.sf2 is a MIDI “soundfont” file and can be downloaded for free.
Next, enter this on the command-line (back in the previous terminal session):
perl clocked-euclidean-drums.pl fluid 90
You will hear standard kick, snare, and closed hihat cymbal. And here is a poor recording of this with my phone:
To run the code with my multi-timbral drum machine, I enter this on the command-line:
perl clocked-euclidean-drums.pl usb 90 -1
And here is what that sounds like:
The Module
I have coded this logic, and a bit more, into a friendly CPAN module. Check out the eg/euclidean.pl example program in the distribution. It is a work in progress. YMMV.
Credits
Thank you to Andrew Rodland (hobbs), who helped me wrap my head around the “no-sleeping asynchronous” algorithm.
To-do Challenges
-
Make patterns other than prime number based Euclidean phrases.
-
Toggle more than two groove parts.
-
Add snare fills to the (end of the) 4th bars. (here’s my version)
-
Make this code handle odd meter grooves.
Resources
-
The IO::Async::Loop module
-
The IO::Async::Timer::Periodic module
-
The Math::Prime::XS module
-
The MIDI::RtMidi::FFI::Device module
-
The Music::CreatingRhythms module
-
The Music::SimpleDrumMachine WIP module based on this logic
-
The cross-platform fluidsynth application
-
My original music: https://www.youtube.com/@GeneBoggs
Every month, I write a newsletter which (among other things) discusses some of the technical projects I’ve been working on. It’s a useful exercise — partly as a record for other people, but mostly as a way for me to remember what I’ve actually done.
Because, as I’m sure you’ve noticed, it’s very easy to forget.
So this month, I decided to automate it.
(And, if you’re interested in the end result, this is also a good excuse to mention that the newsletter exists. Two birds, one stone.)
The Problem
All of my Git repositories live somewhere under /home/dave/git. Over time, that’s become… less organised than it might be. Some repos are directly under that directory, others are buried a couple of levels down, and I’m fairly sure there are a few I’ve completely forgotten about.
What I wanted was:
- Given a month and a year
- Find all Git repositories under that directory
- Identify which ones had commits in that month
- Summarise the work done in each repo
The first three are straightforward enough. The last one is where things get interesting.
Finding the Repositories
The first step is walking the directory tree and finding .git directories. This is a classic Perl task — File::Find still does exactly what you need.
use v5.40;
use File::Find;
sub find_repos ($root) {
my @repos;
find(
sub {
return unless $_ eq '.git';
push @repos, $File::Find::dir;
},
$root
);
return @repos;
}This gives us a list of repository directories to inspect. It’s simple, robust, and doesn’t require any external dependencies.
(There are, of course, other ways to do this — you could shell out to fd or find, for example — but keeping it in Perl keeps everything nicely self-contained.)
Getting Commits for a Month
For each repo, we can run git log with appropriate date filters.
sub commits_for_month ($repo, $since, $until) {
my $cmd = sprintf(
q{git -C %s log --since="%s" --until="%s" --pretty=format:"%%s"},
$repo, $since, $until
);
my @commits = `$cmd`;
chomp @commits;
return @commits;
}Where
$since and $until define the month we’re interested in. I’ve been using something like:
my $since = "$year-$month-01"; my $until = "$year-$month-31"; # good enough for this purpose
Yes, that’s a bit hand-wavy around month lengths. No, it doesn’t matter in practice. Sometimes “good enough” really is good enough.
A Small Gotcha
It turns out I have a few repositories where I never got around to making a first commit. In that case, git log helpfully explodes with:
fatal: your current branch ‘master’ does not have any commits yet
The fix is simply to ignore failures:
my @commits = `$cmd 2>/dev/null`;
If there are no commits, we just get an empty list and move on. No warnings, no noise.
This is one of those little bits of defensive programming that makes the difference between a script you run once and a script you’re happy to run every month.
Summarising the Work
Once we have a list of commit messages, we can summarise them.
And this is where I cheated slightly.
I used OpenAPI::Client::OpenAI to feed the commit messages into an LLM and ask it to produce a short summary.
Something along these lines:
use OpenAPI::Client::OpenAI;
sub summarise_commits ($commits) {
my $client = OpenAPI::Client::OpenAI->new(
api_key => $ENV{OPENAI_API_KEY},
);
my $text = join "\n", @$commits;
my $response = $client->chat->completions->create({
model => 'gpt-4.1-mini',
messages => [{
role => 'user',
content => "Summarise the following commit messages:\n\n$text",
}],
});
return $response->choices->[0]->message->content;
}Is this overkill? Almost certainly.
Could I have written some heuristics to group and summarise commit messages? Possibly.
Would it have been as much fun? Definitely not.
And in practice, it works remarkably well. Even messy, inconsistent commit messages tend to turn into something that looks like a coherent summary of work.
Putting It Together
For each repo:
- Get commits for the month
- Skip if there are none
- Generate a summary
- Print the repo name and summary
The output looks something like:
my-project ----------- Refactored database layer, added caching, and fixed several edge-case bugs. another-project --------------- Initial scaffolding, basic API endpoints, and deployment configuration.
Which is already a pretty good starting point for a newsletter.
A Nice Side Effect
One unexpected benefit of this approach is that it surfaces projects I’d forgotten about.
Because the script walks the entire directory tree, it finds everything — including half-finished experiments, abandoned ideas, and repos I created at 11pm and never touched again.
Sometimes that’s useful. Sometimes it’s mildly embarrassing.
But it’s always interesting.
What Next?
This is very much a first draft.
It works, but it’s currently a script glued together with shell commands and assumptions about my directory structure. The obvious next step is to:
- Turn it into a proper module
- Add tests
- Clean up the API
- Release it to CPAN
At that point, it becomes something other people might actually want to use — not just a personal tool with hard-coded paths and questionable date handling.
A Future Enhancement
One idea I particularly like is to run this automatically using GitHub Actions.
For example:
- Run monthly
- Generate summaries for that month
- Commit the results to a repository
- Publish them via GitHub Pages
Over time, that would build up a permanent, browsable record of what I’ve been working on.
It’s a nice combination of:
- automation
- documentation
- and a gentle nudge towards accountability
Which is either a fascinating historical archive…
…or a slightly alarming reminder of how many half-finished projects I have.
Closing Thoughts
This started as a small piece of automation to help me write a newsletter. But it’s turned into a nice example of what Perl is still very good at:
- Gluing systems together
- Wrapping command-line tools
- Handling messy real-world data
- Adding just enough intelligence to make the output useful
And, occasionally, outsourcing the hard thinking to a machine.
The code (such as it is currently is) is on GitHub at https://github.com/davorg/git-month-summary.
If you’re interested in the kind of projects this helps summarise, you can find my monthly newsletter over on Substack.
And if I get round to turning this into a CPAN module, I’ll let you know – well, if you’re subscribed to the newsletter!
The post Summarising a Month of Git Activity with Perl (and a Little Help from AI) first appeared on Perl Hacks.
-
App::DBBrowser - Browse SQLite/MySQL/PostgreSQL databases and their tables interactively.
- Version: 2.440 on 2026-04-11, with 18 votes
- Previous CPAN version: 2.439 was released 1 month, 16 days before
- Author: KUERBIS
-
Attean - A Semantic Web Framework
- Version: 0.036 on 2026-04-06, with 19 votes
- Previous CPAN version: 0.035_01 was released the same day
- Author: GWILLIAMS
-
Bio::EnsEMBL - Bio::EnsEMBL - Ensembl Core API
- Version: 114.0.0 on 2026-04-07, with 83 votes
- Previous CPAN version: 114.0.0_50 was released 12 days before
- Author: TAMARAEN
-
CPANSA::DB - the CPAN Security Advisory data as a Perl data structure, mostly for CPAN::Audit
- Version: 20260405.001 on 2026-04-05, with 25 votes
- Previous CPAN version: 20260329.001 was released 6 days before
- Author: BRIANDFOY
-
Exporter - Implements default import method for modules
- Version: 5.79 on 2026-04-06, with 28 votes
- Previous CPAN version: 5.78 was released 2 years, 3 months, 6 days before
- Author: TODDR
-
Image::ExifTool - Read and write meta information
- Version: 13.55 on 2026-04-07, with 44 votes
- Previous CPAN version: 13.50 was released 2 months before
- Author: EXIFTOOL
-
JSON::Schema::Modern - Validate data against a schema using a JSON Schema
- Version: 0.637 on 2026-04-08, with 16 votes
- Previous CPAN version: 0.636 was released the same day
- Author: ETHER
-
Mail::Box - complete E-mail handling suite
- Version: 4.02 on 2026-04-10, with 16 votes
- Previous CPAN version: 4.01 was released 3 months, 28 days before
- Author: MARKOV
-
PDL - Perl Data Language
- Version: 2.104 on 2026-04-08, with 102 votes
- Previous CPAN version: 2.103 was released 1 month, 5 days before
- Author: ETJ
-
Pod::Simple - framework for parsing Pod
- Version: 3.48 on 2026-04-05, with 20 votes
- Previous CPAN version: 3.48 was released the same day
- Author: KHW
-
SPVM - The SPVM Language
- Version: 0.990156 on 2026-04-08, with 36 votes
- Previous CPAN version: 0.990155 was released 1 day before
- Author: KIMOTO
-
Term::Choose - Choose items from a list interactively.
- Version: 1.782 on 2026-04-09, with 15 votes
- Previous CPAN version: 1.781 was released 15 days before
- Author: KUERBIS
-
Test2::Harness - A new and improved test harness with better Test2 integration.
- Version: 1.000170 on 2026-04-10, with 28 votes
- Previous CPAN version: 1.000169 was released 1 day before
- Author: EXODIST
TL;DR
Searching for CLI modules on MetaCPAN returns 1690 results. And still I wrote another, Yet Another CLI framework. This is about why mine is the one you want to use.
Introduction
CLI clients, we all write them, we all want them, but the boilerplate is just horrible. I wanted to get rid of it: Burn it with 🔥.
At my previous dayjob we had something I wrote, or co-wrote, my boss wanted a
sort of chef like API. It became zsknife, but had a huge downside. You needed
to register each command in the main module and I didn’t like that at all. I
forked the concept internally, made it so you didn’t needed to register, but it
lacked discovery.
-
App::Staticperl - perl, libc, 100 modules, all in one standalone 500kb file
- Version: 1.5 on 2026-04-04, with 21 votes
- Previous CPAN version: 1.46 was released 4 years, 1 month, 16 days before
- Author: MLEHMANN
-
Catalyst::Action::REST - Automated REST Method Dispatching
- Version: 1.22 on 2026-03-30, with 13 votes
- Previous CPAN version: 1.21 was released 8 years, 3 months, 25 days before
- Author: ETHER
-
CPANSA::DB - the CPAN Security Advisory data as a Perl data structure, mostly for CPAN::Audit
- Version: 20260329.001 on 2026-03-29, with 25 votes
- Previous CPAN version: 20260327.002 was released 1 day before
- Author: BRIANDFOY
-
Devel::NYTProf - Powerful fast feature-rich Perl source code profiler
- Version: 6.15 on 2026-03-31, with 199 votes
- Previous CPAN version: 6.14 was released 2 years, 5 months, 12 days before
- Author: JKEENAN
-
Devel::Size - Perl extension for finding the memory usage of Perl variables
- Version: 0.87 on 2026-03-31, with 22 votes
- Previous CPAN version: 0.86_50 was released 1 month, 19 days before
- Author: NWCLARK
-
Dios - Declarative Inside-Out Syntax
- Version: 0.002014 on 2026-04-01, with 24 votes
- Previous CPAN version: 0.002013 was released 1 year, 7 months, 14 days before
- Author: DCONWAY
-
Inline::Module - Support for Inline-based CPAN Extension Modules
- Version: 0.35 on 2026-03-30, with 14 votes
- Previous CPAN version: 0.34 was released 11 years, 1 month, 12 days before
- Author: INGY
-
IPC::Run - system() and background procs w/ piping, redirs, ptys (Unix, Win32)
- Version: 20260402.0 on 2026-04-02, with 39 votes
- Previous CPAN version: 20260401.0 was released the same day
- Author: TODDR
-
LWP - The World-Wide Web library for Perl
- Version: 6.82 on 2026-03-29, with 212 votes
- Previous CPAN version: 6.81 was released 5 months, 6 days before
- Author: OALDERS
-
Module::CoreList - what modules shipped with versions of perl
- Version: 5.20260330 on 2026-03-29, with 45 votes
- Previous CPAN version: 5.20260320 was released 8 days before
- Author: BINGOS
-
Module::Metadata - Gather package and POD information from perl module files
- Version: 1.000039 on 2026-04-03, with 14 votes
- Previous CPAN version: 1.000038 was released 2 years, 11 months, 5 days before
- Author: ETHER
-
Mouse - Moose minus the antlers
- Version: v2.6.2 on 2026-04-04, with 63 votes
- Previous CPAN version: v2.6.1 was released 3 months, 14 days before
- Author: SYOHEX
- perl - The Perl 5 language interpreter
- Version: 5.042002 on 2026-03-29, with 2251 votes
- Previous CPAN version: 5.042001 was released 21 days before
- Author: SHAY
-
Pod::Simple - framework for parsing Pod
- Version: 3.48 on 2026-04-04, with 20 votes
- Previous CPAN version: 3.47 was released 10 months, 19 days before
- Author: KHW
-
Sidef - The Sidef Programming Language - A modern, high-level programming language
- Version: 26.04 on 2026-04-01, with 122 votes
- Previous CPAN version: 26.01 was released 2 months, 18 days before
- Author: TRIZEN
-
SPVM - The SPVM Language
- Version: 0.990153 on 2026-03-28, with 36 votes
- Previous CPAN version: 0.990152 was released 2 days before
- Author: KIMOTO
-
Sys::Virt - libvirt Perl API
- Version: v12.2.0 on 2026-04-01, with 17 votes
- Previous CPAN version: v12.1.0 was released 29 days before
- Author: DANBERR
-
Test2::Harness - A new and improved test harness with better Test2 integration.
- Version: 1.000164 on 2026-04-01, with 28 votes
- Previous CPAN version: 1.000164 was released the same day
- Author: EXODIST
-
WebService::Fastly - an interface to most facets of the [Fastly API](https://www.fastly.com/documentation/reference/api/).
- Version: 14.01 on 2026-03-31, with 18 votes
- Previous CPAN version: 14.00 was released 1 month, 14 days before
- Author: FASTLY
-
YAML::Syck - Fast, lightweight YAML loader and dumper
- Version: 1.44 on 2026-04-02, with 18 votes
- Previous CPAN version: 1.43 was released 1 day before
- Author: TODDR
