Originally published at Perl Weekly 771
Hi there,
I put the 'Testing in Perl' course on hold for now. Instead of that we are going to explore the use of some of the mocking libraries we saw during the course. In the next session we'll pick one of the Perl modules used for mocking and we'll look for modules that use it. We'll try to understand how it is being used and we'll try to contribute something to at least one of the modules.
For background you can check the OSDC Perl page where we have a listing of modules for 'Code reading'.
You can also watch the recording of the Testing in Perl course. (Registration required but these videos are currently free of charge.)
Enjoy your week!
--
Your editor: Gabor Szabo.
Articles
ANNOUNCE: Perl.Wiki V 1.45 etc
TPRC Announces Post Conference Class
Steven Lembark is presenting: Teaching AI New Tricks: Perly MCP's for Claude.
Who tests the tester? Me !!!
I was just showing the participants of the 'Testing in Perl' course how to write and test a Test::* module. It is nice to see that about the same time Lichtkind wrote an article about the same topic.
Reading CPAN Testers Reports Using AI Agents
A very interesting and useful use of AI.
Discussion
sending matrix messages
Module naming vs CPAN conventions
Perl in Ubuntu 26-04 LTS (vs 24-04 LTS)
Some Perl modules need a dev package to be installed using 'apt'.
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 - 372
Welcome to a new week with a couple of fun tasks "Rearrange Spaces" and "Largest Substring". 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 - 371
Enjoy a quick recap of last week's contributions by Team PWC dealing with the "Missing Letter" and "Subset Equilibrium" tasks in Perl and Raku. You will find plenty of solutions to keep you busy.
Perl Weekly Challenge 371: Missing Letter
Abigail gives us an efficient solution, O(n), to the missing letter problem by determining the difference between the sum of all letters in a full range of letters and the sum of the letters in the given array. This eliminates unnecessary loops, which is a big advantage over an iterative search.
Perl Weekly Challenge 371: Subset Equilibrium
This solution is a well-organised that defines the NP-complete status of this problem and also implements an efficient bitmasking solution in numerous programming languages (e.g., Perl, Python, AWK, C, etc.) and serves as an excellent reference for other programmers to learn about and use for comparison purposes.
Missing Equilibrium
The Raku Solution implements this approach using combinations to easily create subsets in an idiomatic and very readable way. It also provides an elegant way of compensating for zero-based indices when calculating the sum of the positions in each subset by the calculated subsets.
Subset Equilibrium (Just nod if you can hear me)
Bob addressed the exponential number of complex solutions from the outset and provided a practical implementation based on Algorithm::Combinatorics. The use of a positional-based iterator with zero-index compensation allowed him to effectively handle the off-by-one requirement. In addition to that, Bob supplied practical testing strategies using the Test2::Tools::Compare package.
Perl Weekly Challenge: Week 371
This solution gives a clean, efficient implementation that fulfills both the requirement of a proper subset and utilising 1-based indexing by adding + @combo.elems to the index sum. By making use of combinations from Algorithm::Combinatorics in Perl (and using built-in combinations in Raku), only the necessary subset sizes are generated; the output formatting handles both the non-empty and empty result cases in a very clean manner via a clear ternary expression.
Missing Equilibria
This new solution offers an evaluation of the subset equilibrium problem that is a new and interesting technical perspective and acknowledges the shared views of the two demonstrated examples, while not overlooking those cases where the two may have disagreement along the edges of each set of criteria. The Perl code produced using Math::Prime::Util::forcomb and List::Gather is exceptionally simple but has an artistic style as well.
Perl Weekly Challenge 371
The post describes a simple yet effective way to generate all proper subsets of a set. It uses the subset function of the Algorithm::Combinatorics module to create all proper subsets, and then filters them based on a very concise expression written as a single line. The script contains both a command-line version for easy use and a complete version that includes error handling and has been thoroughly tested.
The Missing Equilibrium
By mathematically restructuring the main condition in the problem, i.e. equality of the total values for elements and their corresponding indices. Additionally, utilising the combination iterators from Algorithm::Combinatorics with subset sizes of 2 through n-1 provides a memory efficient approach to solving this exponential complexity problem while utilizing an optimally prepared array of pre-computed offsets, or at least the offsets for an individual index, gives evidence of thoughtful programming.
My subset is-a missing a letterā¦
The solution has a nice, clean, multi-language implementation (Raku, Perl, Python, Elixir) and uses indexed pairs of values to keep track of the relationship between the two elements and the index position of the two elements, thus having no off-by-one errors. The solution meets the "proper subset" constraints by filtering out empty sets, singletons, and the full set as well as providing lots of detail in verbose output to help educate the user by giving exact values and sums of values for each match.
Solve the question and balance the subset
By using established neighboring letter pairs, the algorithm determines the steps $a and $b in an efficient manner by deducing which are the next alternately patterned letter combinations in relation to each of the five positions for question marks. The entire algorithm utilises a single conditional expression to account for all five possible question mark positions. Additionally, the use of the modulus operator and the mapping of the patterning logic from the analysis to the assignment of steps makes this implementation both very efficient and very easy to read.
The Weekly Challenge - 371: Missing Value
This solution requires formalization of the detection of repeating patterns through a comprehensive and well-documented approach by treating them as a repeating two-stage model (i.e., d1 = d3 and d2 = d4). Special consideration is given to both constant and alternating sequences. The use of defined-or (i.e., //=) to unify step values, along with the straightforward relationship between each possible question mark position and its simple arithmetic reconstruction, contributes to creating a code that is highly reliable and easy to follow. Additionally, the thorough input validation provided demonstrates significant consideration for actually implementing in a real world scenario.
The Weekly Challenge - 371: Subset Equilibrium
This document includes an excellent comparison of two CPAN modules (Algorithm::Combinatorics and Data::PowerSet) that correctly identifies the combinatorically generated subsets by size (2 to n) are more efficient than generating the power set and filtering out the subsets to produce the same result for this specific task. The code base is very clean and well-structured; it also does a great job of utilising list slicing (@nums[@$subset]) and map to convert from 0-based to 1-based position, while making use of the helper function print_result to ensure that all output for all test cases is consistent and easy to read.
The Weekly Challenge #371
This document offers an innovative and realistic perspective on the subset equilibrium issue by acknowledging there is no "smart" optimisation to the subset equilibrium solution and then using a simple combination-based search on the two to n-1 sizes. The solution was implemented correctly with a summation on 1-indexed values for each of those combinations.
Missing Equilibrium
Roger demonstrates a very clever way of optimizing the solution through the precomputation of one list of differences so that only the check for the sum of the selected differences equals zero must occur, which substantially reduces the amount of computing needed within the combinatorial loops. Roger does a great job of providing a perspective of how to write this in many programming languages (Raku, Kotlin, Crystal, etc.) and provides many practical examples (like sorting the list to create a consistent order for output), thereby making this solution efficient and easily transferable across many different programming languages.
Question the bits
Using bit manipulation, this solution works well to create all combinations of subsets by iterating through all integers from 1 to 2^n-2, thus avoiding the empty and full subsets. The inner loop adds pos+1 because the indices in a array have a base index of 1, while the implementation does not require any third-party modules, making it portable and easy to use for individuals who are already comfortable using bit mask manipulation.
Weekly collections
NICEPERL's lists
Great CPAN modules released last week.
Event reports
Welcome to the Perl Toolchain Summit 2026!
That's how it started
Perl Toolchain Summit in Vienna
A little review by the local orga.
Events
Exploring Perl Modules
May 7, 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.
Weekly Challenge 372
Each week Mohammad S. Anwar sends out The Weekly Challenge, a chance for all of us to come up with solutions to two weekly tasks. My solutions are written in Python first, and then converted to Perl. Unless otherwise stated, Copilot (and other AI tools) have NOT been used to generate the solution. It's a great way for us all to practice some coding.
Task 1: Rearrange Spaces
Task
You are given a string text of words that are placed among number of spaces.
Write a script to rearrange the spaces so that there is an equal number of spaces between every pair of adjacent words and that number is maximized. If you canāt distribute, place the extra spaces at the end. Finally return the string.
My solution
This is a little more straight forward than last weeks challenge one task. For this task, I take start by separating the words on whitespace, and count the number of spaces. This is stored in the words list (array in Perl) and spaces variable.
def rearrange_space(input_string: str) -> str:
words = input_string.split()
spaces = input_string.count(" ")
To avoid a division by zero error, I handle the case where there is a single word. For this I return the word followed by the required number of spaces. In Python, a string multiple by an integer will repeat the string the specified number of times.
if len(words) == 1:
return words[0] + " " * spaces
If there is more than one word, I calculate the spaces_between_words and spaces_at_end using the divmod function. I return the string with the spaces in the appropriate places.
spaces_between_words, spaces_at_end = divmod(spaces, len(words)-1)
return (" " * spaces_between_words).join(words) + " " * spaces_at_end
The Perl solution uses the same logic. As it does not have a divmod function, I calculate the two values separately. The x operator in Perl is used for repetition. The expression $#words returns one less than the length of the words array.
sub main ($input_string) {
my @words = grep { $_ ne "" } split /\s+/, $input_string;
my $spaces = ( $input_string =~ tr/ / / );
if ( $#words == 0 ) {
say '"' . $words[0] . " " x $spaces . '"';
}
else {
my $spaces_between_words = int( $spaces / $#words );
my $spaces_at_end = $spaces % $#words;
say '"'
. join( " " x $spaces_between_words, @words )
. " " x $spaces_at_end . '"';
}
}
Examples
$ ./ch-1.py " challenge "
"challenge "
$ ./ch-1.py "coding is fun"
"coding is fun"
$ ./ch-1.py "a b c d"
"a b c d "
$ ./ch-1.py " team pwc "
"team pwc"
$ ./ch-1.py " the weekly challenge "
"the weekly challenge "
Task 2: Largest Substring
Task
You are given a string.
Write a script to return the length of the largest substring between two equal characters excluding the two characters. Return -1 if there is no such substring.
My solution
For this task, I start by creating a dict (hash in Perl) called freq with the frequency of each letter. In Python, this is achieved with the Counters function from the collections module.
I then iterate through the dict. If the letter occurs more than once, I calculate the difference between position of the first and last occurrence, minus one. Both Python and Perl have the index and rindex methods to do this. I update the largest value if this is greater than previously found values.
from collections import Counter
def largest_substring(input_string: str) -> int:
freq = Counter(input_string)
largest = -1
for letter, count in freq.items():
if count > 1:
substr = input_string.rindex(letter) - input_string.index(letter) - 1
if substr > largest:
largest = substr
return largest
The Perl solution follows the same logic.
sub main ($input_string) {
my %freq = ();
$freq{$_}++ foreach ( split //, $input_string );
my $largest = -1;
while ( my ( $letter, $count ) = each %freq ) {
if ( $count > 1 ) {
my $substr = rindex( $input_string, $letter ) -
index( $input_string, $letter ) - 1;
$largest = $substr if ( $substr > $largest );
}
}
say $largest;
}
Examples
$ ./ch-2.py aaaaa
3
$ ./ch-2.py abcdeba
5
$ ./ch-2.py abbc
0
$ ./ch-2.py abcaacbc
4
$ ./ch-2.py laptop
2
$ ./ch-2.py abc
-1
Introduction
Perl is often referred to as the "duct tape of the Internet,ā due to its versatility and practical applications in various domains. Developed by Larry Wall in 1987, Perl is a high-level programming language that's well-suited for text processing, system administration, web development, and network programming. This article explores its unique features, practical tips, and why you should consider learning Perl today.
Key Features of Perl
Perl is known for several unique features that make it appealing for both beginners and experienced programmers:
- Easy to Learn: Perlās syntax is flexible and forgiving, which allows new programmers to grasp concepts quickly.
- Powerful Text Processing: With robust regular expressions and string manipulation capabilities, Perl excels in tasks involving text.
- CPAN Repository: The Comprehensive Perl Archive Network (CPAN) hosts thousands of modules that simplify development.
- Cross-Platform: Perl runs on various platforms, including Unix, Linux, Windows, and macOS, increasing its usability across different environments.
- Community Support: Perl has a large, active community that creates resources, tutorials, and forums to help learners.
Practical Tips for Learning Perl
If you're interested in beginning your journey with Perl, consider the following tips:
-
Start with the Basics:
- Learn about variables, scalars, arrays, and hashes.
- Understand control structures like loops and conditionals.
-
Utilize Online Resources:
- Explore Perl training programs or tutorials for structured learning.
- Leverage communities such as PerlMonks and the Perl subreddit for advice and problem-solving.
-
Practice Regularly:
- Write small scripts to automate mundane tasks, such as file manipulation and text parsing.
- Contribute to open-source projects that utilize Perl; this will boost your practical skills.
-
Embrace Debugging:
- Learn to use Perl's built-in debugging tools, such as the
-dflag, to troubleshoot and enhance your scripts.
- Learn to use Perl's built-in debugging tools, such as the
-
Explore CPAN:
- Familiarize yourself with CPAN and incorporate various modules into your projects to save time and effort.
-
Read Perl Books and Documentation:
- Books like "Learning Perl" and "Programming Perl" are solid resources for in-depth understanding.
- Official Perl documentation provides a wealth of information on syntax and modules.
Use Cases for Perl
Understanding where Perl is commonly applied can help you focus your learning efforts:
-
Web Development:
- Perl is used in server-side programming, notably with frameworks like Catalyst and Dancer.
-
System Administration:
- Admins rely on Perl for automation scripts that manage file systems, users, and processes.
-
Bioinformatics:
- Perlās text processing capabilities make it suitable for analyzing biological data.
-
Network Programming:
- Perl's sockets provide the tools for network communication and building client/server applications.
-
Game Development:
- Although less common, Perl can be utilized in developing games or game-related tools.
Conclusion
Perl may not be the most publicized language, but its strengths in text processing and quick script development make it invaluable in various tech fields.
By investing time in learning Perl, you equip yourself with skills that enhance your programming capabilities and open doors to diverse opportunities. Engage with communities, practice regularly, and consider structured training to master this powerful language.
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
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.
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. ]
-
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";
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.

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.
I am confused.
cat << EOF > test.txt
this
is
a
text
file
EOF
cat test.txt | perl -0pe 's/.\n^text/hellohello/smg'
works, but
cat test.txt | perl -0pe 's/.$\n^text/hellohello/smg'
doesn't.
Annoyingly https://regex101.com/ doesn't detect this quirk. Is it to do with -0? And why would $\n^ syntax (which is usually perfectly fine) be wrong anyway?
-
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