Shortcuts: s show h hide n next p prev

0xblog — About “Perl”, in 2022

Perl on Medium
cpan/Time-Piece - Update to version 1.38

1.38  2025-10-18
        - Doc updates
        - Fix Windows-2025 crash with %P
        - XS clean up
        - strptime: %z and %Z fixes (GH52,32,73,RT93095,168828)

Perl and Raku Community Roundtable, October 2025 edition

r/perl
Perl and Raku Community Roundtable, October 2025 edition

We're recording the Community Roundtable meetings now, so you can find out what's going on around the Perl and Raku communities. Our community isn't a single, monolithic entity, but an archipelago of activity. If you'd like your "island" represented, come join us at next month's meeting, Friday, 21 November via Zoom. Contact me for the details, or join us on the TPRF slack in the #community-roundtable channel.

In this month's meeting, we had an update from the Board about fundraising and TPRC 2026 planning, some notes from the Mongueurs de Perl about PTS 2026. A call for more participation for FOSDEM 2026 in the event that a devroom for Perl/Raku is awarded. Salve also brought news about CPANSec activities, and Bruce rounded up some news from the Perl & Raku communities.

submitted by /u/GeekRuthie
[link] [comments]

I have released Test2::Plugin::SubtestFilter for Perl tests, which allows filtering test targets by subtest name, similar to --testNamePattern in jest and vitest. I think it is useful when you want to run only specific tests.

# t/test.t
use Test2::V0;
use Test2::Plugin::SubtestFilter;

subtest 'foo' => sub {
    subtest 'nested foo1' => sub { ok 1 };
    subtest 'nested foo2' => sub { ok 1 };
};

subtest 'baz' => sub {
    ok 1;
};

done_testing;
# Run tests matching `foo1`
❯ SUBTEST_FILTER=foo1 prove -lvr t/test.t
t/test.t ..
# Seeded srand with seed '20251020' from local date.
ok 1 - foo {
    ok 1 - nested foo1 {
        ok 1
        1..1
    }
    ok 2 - nested foo2 # skip
    1..2
}
ok 2 - baz # skip
1..2
ok
All tests successful.
Files=1, Tests=2,  0 wallclock secs ( 0.00 usr  0.00 sys +  0.04 cusr  0.00 csys =  0.04 CPU)
Result: PASS
AHOCORASICK - Reduce heap allocation overhead

A `U8*` structure is used to track character positions during Aho-Corasick
string searching. This used to always be allocated from the heap, and
wrapped in a mortal SV to avoid leakage. However, that incurs overhead.

Following this commit:
* A stack buffer is used if `maxlen` is small enough.
* Otherwise, the heap allocation is saved directly to the savestack
for freeing during stack unwinding.
* Since a mortal SV is no longer used, there is no need to `SAVETMPS`
and `FREETMPS` at scope entry/exit.

https://binary.golf/6:

Binary Golf Grand Prix is an annual small file format competition, currently in it's sixth year. The goal is to make the smallest possible file that fits the criteria of the challenge.

This year's BGGP challenge was to output or display 6. I always wanted to work with actual machine code, so I decided to submit a DOS COM executable. Why? Because the COM format has no headers or other metadata; you can just put some x86 instructions in a file and run it directly.

Having no experience with DOS, I started by looking up a "hello world" example and found https://github.com/susam/hello:

MOV AH, 9
MOV DX, 108
INT 21
RET
DB 'hello, world', D, A, '$'

This loads 9 into the AH register (the upper byte of AX) and executes interrupt 0x21, which triggers the DOS "display string" routine. The address of the string is given directly in DX; $ is used as an in-band string terminator because DOS is weird.

Adapting this snippet to output 6 instead is trivial, but I discovered something better: Function 2 of interrupt 0x21 outputs a character (code given in DL) directly. That gives us:

MOV AH, 2
MOV DL, '6'
INT 21
RET

Or in binary:

b4 02 b2 36 cd 21 c3

If you write these 7 bytes to a .COM file, the result is already a fully functional DOS executable. And since the RET command terminates the program, we can append whatever bytes we want, for example to create a palindrome:

b4 02 b2 36 cd 21 c3 21 cd 36 b2 02 b4

However, I've always liked polyglots. It turns out that the byte sequence 23 de corresponds to the x86 instruction AND BX, SI (which modifies BX, but is harmless otherwise). And byte 23 happens to be character # in ASCII, which means anything that follows will be ignored as a comment when the binary file is read by an interpreter that understands # as a comment marker (which includes Perl, Python, Ruby, PHP, make, and the shell). This leads to the following x86/Perl polyglot:

#<DE><B4><02><B2>6<CD>!<C3>
print 6;

And with a few modifications, we get a palindrome again:

#<DE><B4><02><B2>6<CD>!<C3>#
print 6#6 tnirp
#<C3>!<CD>6<B2><02><B4><DE>#

This is also a valid shell script, but it tries to run a program called print with arguments '6#6' and 'tnirp'. We can make the shell recognize # as a comment marker by putting a space in front, but there is no print command, so how do we make the shell use echo while retaining print for perl? Fortunately we don't need to if we're willing to use 6 as a "format string" and switch to printf:

#<DE><B4><02><B2>6<CD>!<C3>#
printf 6 # 6 ftnirp
#<C3>!<CD>6<B2><02><B4><DE>#

We can do one better and add make to the mix. We just need some form of dummy target and an empty list of prerequisites; the rest will be the shell command we already have. Normally that would look like this (with a literal tab before printf):

foo:
        printf 6

However, at least GNU make lets you write it all in one line without using tabs:

foo: ; printf 6

This form happens to be valid Perl already: foo: ; is just a label attached to a null statement. But the shell would try to run a program called foo: and we don't want that. A creative choice of label name and spacing takes care of this problem as well:

true :;printf 6

To make this means: The target true can be created/updated (with no prerequisites) by running printf 6. Since it is the first target in our "makefile", true automatically becomes the default target.

To perl this means: A label (named true) is attached to a null statement, followed by printf(6) (the final semicolon being optional because we're at the end of the file). 6 is implicitly converted to the format string "6", which simply outputs 6.

To the shell this means: Run the true command (with an argument of ':'), then run the printf command (with an argument of 6).

In final 55-byte palindrome + x86 machine code form:

#<DE><B4><02><B2>6<CD>!<C3>#
true :;printf 6 # 6 ftnirp;: eurt
#<C3>!<CD>6<B2><02><B4><DE>#

That's it!

I can't get DBD-mysql to install on my Windows 11 machine.
Activestate Perl 5.40.2
DBD-mysql version 5.013 (Auto)
Mysql server 8.4

Mysql server is running.
Database 'test' has been created.
ContainerAdministrator with password 's3kr1t' has been granted all privileges for all databases.

state install dbd-mysql responds with:

• Searching for packages in the ActiveState Catalog ✔ Found
• Resolving Dependencies x Failed
x Could not plan build. Platform responded with:
Failed to create build plan due to solve errors
dbd-mysql is unavailable.
let.sources.solve
Because root depends on every version of Feature|language/perl|dbd-mysql which doesn't match any versions, version solving failed.

I have also tried doing the build on the Activestate site. I would post the results, but it is 502 lines of messages. But I did see this, which may be relevant:

perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:

LC_ALL = (unset), LC_CTYPE = (unset),
LC_NUMERIC = (unset),
LC_COLLATE = (unset),
LC_TIME = (unset),
LC_MONETARY = (unset),
LANG = "en_US.UTF-8"

are supported and installed on your system.

perl: warning: Falling back to the system default locale ("English_United States.1252").

'mysql_config' is not recognized as an internal or external command, operable program or batch file.

Failed to determine directory of mysql.h

Problem compiling Net::Pcap on macos

r/perl

Hello.

% sw_vers ProductName:macOS ProductVersion:26.0.1 BuildVersion:25A362 % perl -v This is perl 5, version 40, subversion 2 (v5.40.2) built for darwin-thread-multi-2level 

I'm trying to install Net::DHCP via CPAN, and Net::Pcap is a dependency. Net::Pcap's build succeeds, but the tests fail.

I've downloaded the Net::Pcap source, and get the exact same errors as when using CPAN. I've tried two different versions of libpcap, firstly the native macos version:

% perl Makefile.PL looking for -lpcap... yes checking for pcap_lib_version() in -lpcap... yes detecting available functions... ok detecting libpcap version... Running [cc -fno-common -DPERL_DARWIN -mmacosx-version-min=15.2 -DNO_THREAD_SAFE_QUERYLOCALE -DNO_POSIX_2008_LOCALE -fno-strict-aliasing -pipe -fstack-protector-strong -Wall -Wwrite-strings 'pcap_version.c' -lpcap -o pcap_version.exe] ok (libpcap version 1.10.1) Setting -DPERL_PCAP_VERSION=1010001 Checking if your kit is complete... Looks good Generating a Unix-style Makefile Writing Makefile for Net::Pcap Writing MYMETA.yml and MYMETA.json 

and then the slightly newer version available via Homebrew:

% perl Makefile.PL INC=-I/opt/homebrew/opt/libpcap/include LIBS=-L/opt/homebrew/opt/libpcap/lib looking for -lpcap... yes checking for pcap_lib_version() in -lpcap... yes detecting available functions... ok detecting libpcap version... Running [cc -fno-common -DPERL_DARWIN -mmacosx-version-min=15.2 -DNO_THREAD_SAFE_QUERYLOCALE -DNO_POSIX_2008_LOCALE -fno-strict-aliasing -pipe -fstack-protector-strong -Wall -Wwrite-strings 'pcap_version.c' -L/opt/homebrew/opt/libpcap/lib -lpcap -o pcap_version.exe] ld: warning: building for macOS-15.2, but linking with dylib '/opt/homebrew/opt/libpcap/lib/libpcap.A.dylib' which was built for newer version 26.0 ok (libpcap version 1.10.5) Setting -DPERL_PCAP_VERSION=1010005 Checking if your kit is complete... Looks good Generating a Unix-style Makefile Writing Makefile for Net::Pcap Writing MYMETA.yml and MYMETA.json 

In both cases the build succeeds (350 warnings generated, but no errors), but in both cases the tests fail with the exact same errors:

% sudo make test "/opt/homebrew/Cellar/perl/5.40.2/bin/perl" -MExtUtils::Command::MM -e 'cp_nonempty' -- Pcap.bs blib/arch/auto/Net/P> PERL_DL_NONLAZY=1 "/opt/homebrew/Cellar/perl/5.40.2/bin/perl" "-MExtUtils::Command::MM" "-MTest::Harness" "-e" "unde> # Testing Net::Pcap 0.21 (libpcap version 1.10.1) under Perl 5.040002 t/00-load.t ................ ok t/01-api.t ................. ok t/02-lookup.t .............. ok t/03-openlive.t ............ ok t/04-loop.t ................ ok t/05-dump.t ................ ok t/06-offline.t ............. ok t/07-stats.t ............... ok t/08-filter.t .............. ok t/09-error.t ............... ok t/10-fileno.t .............. ok t/11-snapshot.t ............ ok t/12-next.t ................ skipped: pcap_next() behaves too strangely for being tested on random machines t/13-dispatch.t ............ ok t/14-datalink.t ............ ok # This platform has been detected as a little endian architecture t/15-is_swapped.t .......... ok t/16-setnonblock.t ......... ok # libpcap version 1.10.1 t/17-lib_version.t ......... ok t/18-open_dead.t ........... ok t/19-breakloop.t ........... ok t/20-constants.t ........... ok t/21-next_ex.t ............. skipped: slowness and random failures... testing pcap_next_ex() is a PITA # Failed test 'pcap_open()' # at t/22-open.t line 90. # got: 'Your vendor has not defined pcap macro OPENFLAG_PROMISCUOUS, used at t/22-open.t line 89. # ' # expected: '' # Failed test ' - returned a defined value' # at t/22-open.t line 92. # Failed test '' - $pcap' isa 'SCALAR'' # at t/22-open.t line 93. # ' - $pcap' isn't defined # Failed test '' - $pcap' isa 'pcap_tPtr'' # at t/22-open.t line 94. # ' - $pcap' isn't defined # Failed test 'setbuff()' # at t/22-open.t line 98. # got: 'p is not of type pcap_tPtr at t/22-open.t line 97. # ' # expected: '' # Failed test ' - return 0 for true' # at t/22-open.t line 99. # got: undef # expected: '0' # Failed test 'setuserbuffer()' # at t/22-open.t line 103. # got: 'p is not of type pcap_tPtr at t/22-open.t line 102. # ' # expected: '' # Failed test ' - return 0 for true' # at t/22-open.t line 104. # got: undef # expected: '0' # Failed test 'setmode()' # at t/22-open.t line 108. # got: 'Your vendor has not defined pcap macro MODE_CAPT, used at t/22-open.t line 107. # ' # expected: '' # Failed test ' - return 0 for true' # at t/22-open.t line 109. # got: undef # expected: '0' # Failed test 'setmintocopy()' # at t/22-open.t line 113. # got: 'p is not of type pcap_tPtr at t/22-open.t line 112. # ' # expected: '' # Failed test ' - return 0 for true' # at t/22-open.t line 114. # got: undef # expected: '0' p is not of type pcap_tPtr at t/22-open.t line 116. # Looks like your test exited with 29 just after 24. t/22-open.t ................ Dubious, test returned 29 (wstat 7424, 0x1d00) Failed 12/24 subtests (less 11 skipped subtests: 1 okay) # Failed test 'createsrcstr() ' # at t/23-srcstr.t line 70. # got: 'Undefined subroutine &main::createsrcstr called at t/23-srcstr.t line 69. # ' # expected: '' # Failed test ' - should return zero: ' # at t/23-srcstr.t line 71. # got: undef # expected: '0' # Failed test ' - checking created source string' # at t/23-srcstr.t line 72. # got: '' # expected: 'rpcap://fangorn:12345/eth0' # Failed test 'parsesrcstr() ' # at t/23-srcstr.t line 76. # got: 'Undefined subroutine &main::parsesrcstr called at t/23-srcstr.t line 75. # ' # expected: '' # Failed test ' - should return zero: ' # at t/23-srcstr.t line 77. # got: undef # expected: '0' # Failed test ' - checking parsed type' # at t/23-srcstr.t line 78. # got: '' # expected: 'rpcap' # Failed test ' - checking parsed host' # at t/23-srcstr.t line 79. # got: '' # expected: 'fangorn' # Failed test ' - checking parsed port' # at t/23-srcstr.t line 80. # got: '' # expected: '12345' # Failed test ' - checking parsed name' # at t/23-srcstr.t line 81. # got: '' # expected: 'eth0' # Looks like you failed 9 tests of 18. t/23-srcstr.t .............. Dubious, test returned 9 (wstat 2304, 0x900) Failed 9/18 subtests (less 9 skipped subtests: 0 okay) t/24-offline_filter.t ...... ok t/50-anyevent-pcap.t ....... skipped: AnyEvent is not available t/50-net-pcap-easy.t ....... skipped: Net::Pcap::Easy is not available t/50-poe-component-pcap.t .. skipped: POE is not available t/distchk.t ................ skipped: Test::Distribution required for checking distribution t/pod.t .................... skipped: Test::Pod 1.14 required for testing POD t/podcover.t ............... skipped: Currently not working for Net::Pcap t/podspell.t ............... skipped: Pod spelling: for maintainer only t/portfs.t ................. skipped: Only for the module maintainer Test Summary Report ------------------- t/06-offline.t (Wstat: 0 Tests: 403 Failed: 0) TODO passed: 398 t/10-fileno.t (Wstat: 0 Tests: 21 Failed: 0) TODO passed: 19 t/22-open.t (Wstat: 7424 (exited 29) Tests: 24 Failed: 12) Failed tests: 12, 14-24 Non-zero exit status: 29 t/23-srcstr.t (Wstat: 2304 (exited 9) Tests: 18 Failed: 9) Failed tests: 10-18 Non-zero exit status: 9 Files=33, Tests=1557, 3 wallclock secs ( 0.10 usr 0.04 sys + 1.02 cusr 0.23 csys = 1.39 CPU) Result: FAIL Failed 2/33 test programs. 21/1557 subtests failed. make: *** [Makefile:1079: test_dynamic] Error 255 

Any clues?

Thank you.

submitted by /u/Flashy_Boot
[link] [comments]
t/class/accessor.t - add additional :reader and :writer tests

The :reader tests are not really needed at present, as `pp_leavesub` makes
the necessary copies, but exploring some fast accessor ideas which bypass
that logic has shown the usefulness of having these tests present.

A test for :writer on an array field already exists in a separate file.
A comment pointing to that file has been added, and a test for the hash
case added next to the pre-existing test.

I know how to get all of the values of a hash given a set of keys:

use strict;
use warnings;

use DDP;

my %h = (
  a => 1,
  b => 2
);

my @v = @h{a,b};

p @v; # ( 1, 2 )

However I'm not sure how to get all of the values from a hash, excluding a key, without putting all of the keys, or using a for loop:

use strict;
use warnings;

use DDP;

my %h = (
  a => 1,
  b => 2,
  c => 3,
  d => 4,
  e => 5
);

my @v;
for (keys %h) {
  if ($_ eq 'd') {
    next;
  }
  push @values, $h{$_};
}

p @v;

What I'd like to do is something like:

my @v = @h{!d};

Is there a more elegant way of doing this instead of just using a for-loop, and without knowing all of the keys before hand, just knowing the key we want to exclude?

Perl 🐪 Weekly #743 - Writing Perl with LLMs

dev.to #perl

Originally published at Perl Weekly 743

Hi there,

Last week I went to a small conference on "Teaching Computer Science in the era of AI/LLMs" hosted at The Academic College of Tel Aviv-Yaffo. It was very interesting to hear how at various institutions, for example at the Technion, at the Tel Aviv University and at Ben Gurion University the lecturers feel the need to change things as LLMs can now implement basically everything at the level of a CS student in a BSc program. How do you teach them to actually learn the syntax? How much should you let them use LLMs for the assignments and during the exams?

I personally teach a course at the Weizmann Institute of Science to Master's and Phd students of Biology and Life Sciences. I think 15 years ago there was a course in Perl, but now this is in Python. It is very different from teaching CS students. My students use the language a lot more to write one-time programs to analyze some data and most of them don't see themselves as 'programmers'. So they are way less interested in syntax, or 'good code'. They mostly want to get nice graphs.

It is quite clear that giving them well written assignments is rather pointless. They can just put the assignment in an LLM and get the result. So what can be done?

As the semester starts this coming Sunday I really need to figure out how and what to teach them. Should I basically teach them prompt engineering?

Related to this I'd love to hear from you - either in an email or better yet in a blog post - on how do you use LLMs to write code? Especially Perl code. Which LLMs do you use? How do you make your prompts better? Anything special to Perl code?

What would you recommend to fellow Perl developers, how to best use LLMs while writing Perl code?

I know we are all still trying to figure this out, but learning from each other could make this process easier.

Enjoy your week!

--
Your editor: Gabor Szabo.

Articles

The joy of rediscovering Perl

Wait a minute, Mr POSTman

Debugging POST request headers in under 40 screen rows. Doesn't actually use Postman.

Clipadd Cookbook

clipadd, a CLI included in the App::ClipboardUtils distribution, lets you add items to the desktop clipboard. It is clipboard-manager-agnostic, supporting backends like xclip and Klipper. It also has some convenient features which will be demonstrated in this document.

Porting from Perl to Go: Simplifying for Platform Engineering

Rewriting a script for the Homebrew package manager taught me how Go’s design choices align with platform-ready tools.

Discussion

Full stack javascript developer looking to find a perl job

Compute the Reverse Polish Notation using Perl

Would you like to add comments to this post with your suggestions on how to improve the code?

Managing memory in the GPU with Perl

Task-MemManager-Device

perl XS modules for CUBIN files patching

Would love a good web hosting company to run Perl in a fcgi or plack or whatever

There are a number of companies where one can have a full VPS for 4-5 USD / month with root access. You can run your own code there directly on the VPS or you can run a Docker container on it and as the load on the system growth you can upgrade the VPS to something bigger for slightly more money. I have been using Linode and Digital Ocean for many years and I am very satisfied.

Perl

This week in PSC (204) | 2025-10-13

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 Lance Wicks.

The Weekly Challenge - 344

Welcome to a new week with a couple of fun tasks "Array Form Compute" and "Array Formation". 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 - 343

Enjoy a quick recap of last week's contributions by Team PWC dealing with the "Zero Friend" and "Champion Team" tasks in Perl and Raku. You will find plenty of solutions to keep you busy.

TWC343

Clear, effective, and showcasing strong Perl programming abilities. While Task 2 efficiently counts wins in a match matrix with clear logic and sensible hash usage, Task 1 demonstrates succinct numeric handling with strong absolute value comparisons. All things considered, the answers are clear, accurate, and elegantly demonstrate how to solve real-world problems using Perl.

Team Zero

For both inexperienced and seasoned Raku developers, this is a great resource. It explores the nuances of the language and offers insightful lessons in efficient coding techniques in addition to offering workable answers to typical issues.

Friendly Champions

These solutions demonstrate superior algorithmic thinking and Perl programming at the expert level. This submission is incredibly impressive because it combines mathematical insight, language proficiency, and innovative problem-solving.

Perl Weekly Challenge 343

The solutions demonstrate the useful application of PDL for array and matrix operations and are technically sound and reliable. They are great illustrations of algorithmic problem-solving in Perl since they are not only accurate but also optimized for readability and maintainability.

The Zero Champion

In addition to overcoming the obstacles, Matthias did so in a way that inspired and educated others. Programming challenges are valuable because of contributions like this one.

Ze-ro the CHAMPIONS!

In addition to accurately resolving the difficulties, Packy has produced a superb analysis of how to apply the same algorithm across various programming paradigms while honoring the idioms and advantages of each language.

No friends among the champions

Both solutions demonstrate a thorough grasp of Perl's capabilities and are executed with clarity and efficiency. They make sure the code is both functional and maintainable by following best practices for coding.

The Weekly Challenge #343

Robbie's solutions show a thorough comprehension of the problem requirements and the ability to apply efficient algorithms. They are both elegant and mathematically sound. His creative method of breaking the tie in Task 2 gives the solution an additional level of complexity.

Zero to Champion

Roger's solutions exhibit a thorough comprehension of the problem requirements and are well-structured. The code is efficient and simple to understand because it makes use of Perl's built-in functions and simple logic. These implementations are great illustrations of how to precisely and clearly approach algorithmic problems.

It’s hard to make friends when you’re a zero

The two challenges are thoughtfully and technically insightfully explored in Ryan's post. His elegant and effective solutions demonstrate a thorough comprehension of Perl's capabilities.

Absolute Champion

Simon's solution shows a thorough grasp of Python's capabilities and is both simple and effective. He makes sure the solution is reliable and capable of handling a variety of numerical inputs by utilizing the decimal module.

Weekly collections

NICEPERL's lists

Great CPAN modules released last week;
MetaCPAN weekly report.

Events

Paris.pm monthly meeting

November 12, 2025

Toronto.pm - online - How SUSE is using Perl

December 6, 2025

Paris.pm monthly meeting

December 10, 2025

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.

Show that you can't goto into optimized-away construct

Incorporating suggestions from Lukas Mai.  Use less generic names for
labels in test files.

Fixes: GH #23810
As you know, The Weekly Challenge, primarily focus on Perl and Raku. During the Week #018, we received solutions to The Weekly Challenge - 018 by Orestis Zekai in Python. It was pleasant surprise to receive solutions in something other than Perl and Raku. Ever since regular team members also started contributing in other languages like Ada, APL, Awk, BASIC, Bash, Bc, Befunge-93, Bourne Shell, BQN, Brainfuck, C3, C, CESIL, Chef, COBOL, Coconut, C Shell, C++, Clojure, Crystal, CUDA, D, Dart, Dc, Elixir, Elm, Emacs Lisp, Erlang, Excel VBA, F#, Factor, Fennel, Fish, Forth, Fortran, Gembase, Gleam, GNAT, Go, GP, Groovy, Haskell, Haxe, HTML, Hy, Idris, IO, J, Janet, Java, JavaScript, Julia, K, Kap, Korn Shell, Kotlin, Lisp, Logo, Lua, M4, Maxima, Miranda, Modula 3, MMIX, Mumps, Myrddin, Nelua, Nim, Nix, Node.js, Nuweb, Oberon, Octave, OCaml, Odin, Ook, Pascal, PHP, PicoLisp, Python, PostgreSQL, Postscript, PowerShell, Prolog, R, Racket, Rexx, Ring, Roc, Ruby, Rust, Scala, Scheme, Sed, Smalltalk, SQL, Standard ML, SVG, Swift, Tcl, TypeScript, Typst, Uiua, V, Visual BASIC, WebAssembly, Wolfram, XSLT, YaBasic and Zig.
Welcome to the Week #344 of The Weekly Challenge.
Thank you Team PWC for your continuous support and encouragement.
toke.c: Fix inconsistency under 'use utf8'

This code can't work properly:

    if (UTF ? isIDFIRST_utf8((U8*)s+1) : isWORDCHAR_A(s[1]))

Suppose you have a string composed entirely of ASCII characters
beginning with a digit.  If the string isn't encoded in UTF-8, the
condition is true, but it is false if the string happens to have the
UTF-8 flag set for whatever reason.  One of those reasons simply is that
the Perl program is being compiled under 'use utf8'.

The UTF-8 flag should not change the behavior of ASCII strings.

The code was introduced in 9d58dbc453a86c9cbb3a131adcd1559fe0445a08 in
2015, to fix [perl #123963] "@<fullwidth digit>".  The line it replaced
was

    if (isWORDCHAR_lazy_if(s+1,UTF))

(The code was modified in 2016 by
fac0f7a38edc4e50a7250b738699165079b852d8 as part of a global
substitution to use isIDFIRST_utf8_safe() so as to have no possibility
of going off the end of the buffer), but that did not affect the logic.

The problem the original commit was trying to solve was that fullwidth
digits (U+FF10 etc) were accepted when they shouldn't be, whereas [0-9]
should remain as being accepted.  The defect is that [0-9] stopped being
accepted when the UTF-8 flag is on.  The solution is to change it to
instead be

    if (isDIGIT_A(s[1]) || isIDFIRST_lazy_if_safe(s+1, send, UTF))

This causes [0-9] to remain accepted regardless of the UTF-8 flag.
So when it is on, the only difference between before this commit and
after is that [0-9] are accepted.

In the ASCII range, the only difference between \w and IDFirst is that
the former includes the digits 0-9, so when the UTF-8 flag is off this
evaluates to isWORD_CHAR_A, as before.

(Changing to isIDFIRST from isWORDCHAR in the original commit did solve
a bunch of other cases where a \w is not supposed to be the first
character in a name.  There are about 4K such characters currently in
Unicode.)

Full stack javascript developer looking to find a perl job

r/perl

Hello everyone, now this seems kinda of cringe ig, but here is the thing i am a full stack developer with javascript and although it is okay, i am getting tired of web dev, and i am looking to switch to more "advanced" software fields and i made a search on "software developer jobs no one wants" and i saw a comment about perl, so my inquiry is, if any of you are in need or looking for an intern(or if you know where i can find such jobs), i am willing to work for 1 month free and in the process also learn the language. Now this is a huge shot and even if the mods delete this then no biggie.

Thank you in advance

submitted by /u/No-Sprinkles-1754
[link] [comments]

(dlxx) 12 great CPAN modules released last week

r/perl
Updates for great CPAN modules released last week. A module is considered great if its favorites count is greater or equal than 12.

  1. App::DBBrowser - Browse SQLite/MySQL/PostgreSQL databases and their tables interactively.
    • Version: 2.435 on 2025-10-14, with 18 votes
    • Previous CPAN version: 2.434 was 9 days before
    • Author: KUERBIS
  2. App::rdapper - a command-line RDAP client.
    • Version: 1.20 on 2025-10-14, with 21 votes
    • Previous CPAN version: 1.19 was 1 month, 13 days before
    • Author: GBROWN
  3. CPANSA::DB - the CPAN Security Advisory data as a Perl data structure, mostly for CPAN::Audit
    • Version: 20251018.003 on 2025-10-19, with 25 votes
    • Previous CPAN version: 20251017.001 was 1 day before
    • Author: BRIANDFOY
  4. Cucumber::TagExpressions - A library for parsing and evaluating cucumber tag expressions (filters)
    • Version: 8.0.0 on 2025-10-14, with 16 votes
    • Previous CPAN version: 6.2.0 was 4 months, 20 days before
    • Author: CUKEBOT
  5. Data::ObjectDriver - Simple, transparent data interface, with caching
    • Version: 0.26 on 2025-10-17, with 16 votes
    • Previous CPAN version: 0.25 was 6 months, 1 day before
    • Author: SIXAPART
  6. Finance::Quote - Get stock and mutual fund quotes from various exchanges
    • Version: 1.67 on 2025-10-18, with 143 votes
    • Previous CPAN version: 1.66_02 was 1 month, 17 days before
    • Author: BPSCHUCK
  7. Google::Ads::GoogleAds::Client - Google Ads API Client Library for Perl
    • Version: v29.0.1 on 2025-10-16, with 20 votes
    • Previous CPAN version: v29.0.0 was 1 day before
    • Author: CHEVALIER
  8. JSON::Schema::Modern - Validate data against a schema using a JSON Schema
    • Version: 0.620 on 2025-10-15, with 12 votes
    • Previous CPAN version: 0.619 was 17 days before
    • Author: ETHER
  9. SPVM - The SPVM Language
    • Version: 0.990105 on 2025-10-16, with 36 votes
    • Previous CPAN version: 0.990104 was 1 day before
    • Author: KIMOTO
  10. Term::Choose - Choose items from a list interactively.
    • Version: 1.777 on 2025-10-15, with 15 votes
    • Previous CPAN version: 1.776 was 18 days before
    • Author: KUERBIS
  11. Time::Piece - Object Oriented time objects
    • Version: 1.38 on 2025-10-18, with 64 votes
    • Previous CPAN version: 1.3701 was 1 month, 23 days before
    • Author: ESAYM
  12. Type::Tiny - tiny, yet Moo(se)-compatible type constraint
    • Version: 2.008004 on 2025-10-17, with 145 votes
    • Previous CPAN version: 2.008003 was 1 month, 15 days before
    • Author: TOBYINK

(dcxvii) metacpan weekly report - DBD::DuckDB

Niceperl

This is the weekly favourites list of CPAN distributions. Votes count: 105

Week's winner: DBD::DuckDB (+3)

Build date: 2025/10/19 06:44:20 GMT


Clicked for first time:


Increasing its reputation:

Thank you Team PWC for your continuous support and encouragement.
Thank you Team PWC for your continuous support and encouragement.

Compute the Reverse Polish Notation using Perl

dev.to #perl
use feature "switch";  
    # Construct operator priority data  
    %priority_of_operation=(  
    '+'=>1,  
    '-'=>1,  
    '*'=>2,  
    '/'=>2,  
    '('=>0,  
    ')'=>0  
    );  
    # Expression to be calculated  
    $prepare_exec="5.98+((1+2211.12)*4)-3";  
    $prepare_endexpres=$prepare_exec;  
    # Convert to Reverse Polish Notation  
    # Create operator stack for storing operators  
    @operationstack=qw();  
    $exeexpress=$prepare_endexpres;  
    $result="";  
    print "String to scan: $exeexpress\n";    
    while (length($exeexpress)>0)  
    {  
    # Scan expression sequentially, if current character is a number, output it directly  
    if ($exeexpress =~ /^(\d+)/ | $exeexpress =~/^((\d+)\.(\d*))/){  
       print "Number: $1\n";      
       $result.=($1." ");     
       print "Expression: $result\n";  
       $exeexpress=substr($exeexpress,length($1),length($exeexpress)-length($1));  
       print "String to scan: $exeexpress\n";     
    }  
    # If current operator is '(', push directly to stack     
    elsif($exeexpress =~ /^\(+?/){     
       print "Operator: (\n";      
       print "Operator ( pushed to stack\n";    
       push @operationstack,"(";    
       print "Current operator stack: @operationstack\n";  
       print "Expression: $result\n";     
       $exeexpress=substr($exeexpress,1,length($exeexpress)-1);     
       print "String to scan: $exeexpress\n";        
    }     
    # If it's ')', pop and output operators sequentially until encountering first '(', first '(' is popped but not output     
    elsif($exeexpress =~ /^\)+?/){     
        print "Operator: )\n";           
        $temp=pop @operationstack;  
        while ($temp ne "(") {     
           $result.=($temp." ");     
           print "Operator $temp popped, expression: $result\n";  
           $temp=pop @operationstack;    
           print "Current operator stack: @operationstack\n";   
        }           
        $exeexpress=substr($exeexpress,1,length($exeexpress)-1);         
        print "String to scan: $exeexpress\n";          
    }   
    # If it's arithmetic operator, compare priority of top stack element with current element. If priority of top stack element operator >= priority of current element, pop and output operators sequentially until priority of top stack element < priority of current element, then push current element to stack;   
    elsif($exeexpress =~/^(\+|\-|\*|\/)?(.*)$/)  
    {  
        print "Operator: $1\n";   
        print "Current operator stack: @operationstack\n";   
        $curoperation=$1    ;   
        $topoperation=pop @operationstack;  
        push @operationstack,$topoperation;       
        if ($topoperation){  
            if ($priority_of_operation{$curoperation}>$priority_of_operation{$topoperation})  
            {  
                # If this character's priority is higher than the operator at top of operator stack, push this operator to stack  
                push @operationstack,$curoperation;       
                print "Operator $curoperation pushed to stack\n";  
                $exeexpress=substr($exeexpress,1,length($exeexpress)-1);      
                print "String to scan: $exeexpress\n";               

            }  
            else{  
    # If priority of top stack element operator >= priority of current element, pop and output operators sequentially until priority of top stack element < priority of current element, then push current element to stack;   
                while ($priority_of_operation{$curoperation}<=$priority_of_operation{$topoperation})  
                {  
                    $topoperation=pop @operationstack;    
                    if (not $topoperation) {  
                        last;  
                    }  
                    $result.=($topoperation." ");  
                    print "Operator $topoperation popped, expression: $result\n";  
                }  
                push @operationstack,$curoperation;  
                $exeexpress=substr($exeexpress,1,length($exeexpress)-1);      
                print "String to scan: $exeexpress\n";               
            }  
        }  
        else{  
                push @operationstack,$curoperation;  
                print "Expression: $result\n";   
                $exeexpress=substr($exeexpress,1,length($exeexpress)-1);      
                print "String to scan: $exeexpress\n";               
        }  
        print "Current operator stack: @operationstack\n";  
    }  

    }  
    while ((scalar @operationstack)>0){  
        $result.=pop @operationstack;  
    }  
    print "Expression: $result\n";   
    $mycode=$result;  
    @waitingexe=split (" ",$mycode);  
    $mylength= scalar  @waitingexe;  
    print "Reverse Polish Notation to calculate: $mycode\n";  
    print "Starting Reverse Polish Notation calculation\n";  
    @exestack=qw();  
    # Output current stack contents  
    print "Current stack contents: @exestack\n";  
    for ($i=0;$i<$mylength;$i++){  
    # Start Reverse Polish Notation calculation  
     $myvar=$waitingexe[$i];  
     print "Element read: $myvar   =>";  
     given ($myvar){  
        when ("+") { $num2=pop @exestack;$num1=pop @exestack; $result=$num1+$num2;push @exestack,$result;print "$num1+$num2=$result, $num2 and $num1 popped, $result pushed\n";}  
        when ("-") { $num2=pop @exestack;$num1=pop @exestack; $result=$num1-$num2;push @exestack,$result;print "$num1-$num2=$result, $num2 and $num1 popped, $result pushed\n";}  
        when ("*") { $num2=pop @exestack;$num1=pop @exestack; $result=$num1*$num2;push @exestack,$result;print "$num1*$num2=$result, $num2 and $num1 popped, $result pushed\n";}  
        when ("/") { $num2=pop @exestack;$num1=pop @exestack; $result=$num1/$num2;push @exestack,$result;print "$num1/$num2=$result, $num2 and $num1 popped, $result pushed\n";}  
        # Numbers pushed directly to stack  
        default    {push @exestack,$myvar;print "$myvar pushed to stack\n";}  
    }  
        # Output current stack contents  
        print "Current stack contents: @exestack=>";  
    }  
    $endresult=pop @exestack;  
    print "Result: $endresult\n";  

    sub trim  
    {  
            my $string = shift;  
            $string =~ s/^\s+//;  
            $string =~ s/\s+$//;  
            return $string;  
    }

The above code evaluates a mathematical expression.
We will use the following example to illustrate the computation process.
The infix expression "5 + ((1 + 2) * 4) - 3" is written in Reverse Polish Notation as:

5 1 2 + 4 * + 3 -

The table below illustrates the step-by-step evaluation of this RPN expression from left to right.

Input Action Stack Comment
5 Push 5
1 Push 5, 1
2 Push 5, 1, 2
+ Addition 5, 3 Pop (1, 2); push result (3)
4 Push 5, 3, 4
* Multiplication 5, 12 Pop (3, 4); push result (12)
+ Addition 17 Pop (5, 12); push result (17)
3 Push 17, 3
- Subtraction 14 Pop (17, 3); push result (14)

When the calculation is complete, only one operand remains in the stack, which is the final result of the expression: 14.

Managing memory in the GPU with Perl

r/perl

Wrote an infrastructural module to control memory traffic and manage memory on the GPU using Perl and OpenMP.

https://github.com/chrisarg/Task-MemManager-Device

Examples show how one can get memory in and out of the device and update memory regions (e.g. PDL ndarrays as shown in one of the examples).

I decided to split this functionality out of my bitset/bitvector library Bit (https://github.com/chrisarg/Bit) and it's Perl interface Bit::Set (https://github.com/chrisarg/Bit-Set) when I realized that the stuff I was doing to accelerate these codes could be of general use.

The module is part of the https://github.com/chrisarg/Task-MemManager ecosystem which (hopefully) will allow tighter integration of Perl with low level languages especially assembly for systems programming.

submitted by /u/ReplacementSlight413
[link] [comments]

Rewriting a script for the Homebrew package manager taught me how the Go programming language’s design choices align with platform-ready…

When using the perl library Storable to store and retrieve XML::LibXML document objects, I get a segmentation fault. Specifically, once I use the LibXML routine findnodes. My question is: why, and is there a way around it?

For example consider the following code:

use strict;
use warnings;
use XML::LibXML;
use Storable;
use Devel::Size qw/size/;
use Data::Dumper qw/Dumper/;

my $file = "test.xml";
my $store_file = "./dom.xml.store";
my $dom;
if(-e $store_file ){
        $dom = retrieve $store_file;
        print "dom retrieved from storable $store_file\n";
}else{
        $dom = XML::LibXML->load_xml(location => $file);
        print "dom retrieved from xml $file\n";
        store $dom, $store_file;
}
print Dumper $dom;
print "Dom ref(". ref($dom) . "), size(" . size($dom) . ")\n";

#test 
foreach my $title ($dom->findnodes('//title')){
        print $title->to_literal() . "\n";
}

Running this code twice yields the following result:

$~/perl:perl libxml.pl 
dom retrieved from xml test.xml
$VAR1 = bless( do{\(my $o = '93874673292592')}, 'XML::LibXML::Document' );
Dom ref(XML::LibXML::Document), size(72)
Apollo 13
Solaris
Ender's Game
Interstellar
The Martian
$~/perl:perl libxml.pl 
dom retrieved from storable ./dom.xml.store
$VAR1 = bless( do{\(my $o = '93874673292592')}, 'XML::LibXML::Document' );
Dom ref(XML::LibXML::Document), size(72)
Segmentation fault
$~/perl:

The xml file which I named test.xml file is the same file retrieved from the tutorial here. Similar tests were run on my system with unblessed and blessed perl objects, neither of which caused the segmentation fault.

Usual disclaimers: perl version: v5.26.3 Storable version 3.11 LibXML version 2.0132

I have a minimal working example of two nested while loops on the same array @g:

#!/usr/bin/env perl

use 5.042;
no source::encoding;
use warnings FATAL => 'all';
use feature 'say';
use autodie ':default';

my @g = qw(Matthew Mark Luke John);
while (my ($i, $gos) = each @g) {
    say "$i => $gos";
    while (my ($j, $g2) = each @g) {
        say "\t$j=>$g2";
    }
}

This particular loop gets stuck on the first iteration $i, and never moves on to the 2nd.

The infinity is easily fixed by foreach my $gos (@gos) { instead of the first while.

Is this a bug or an intended feature?

Shouldn't foreach my $gos (@g) in the first line be the exact equivalent of while (my ($i, $gos) = each @g) {?

Wait a minute, Mr POSTman

dev.to #perl

Debugging POST request headers in under 40 screen rows. Doesn't actually use Postman

While developing a Perl client for the Harvard Astrophysics Data System API, I was getting errors from my POST requests. Not usually a problem to fix, but I connect to the host machine via a dodgy terminal session that hangs up when the screensaver kicks in or when the wind blows from the East. To keep from having to restart the 6+ windows that I have open in my dev env every time I go for a walk, I start a tmux session running on the host which ignores the SIGHUP and is just where I left it when I reconnect, no .swp files to clean up.

The problem is that I can't scroll up in tmux like I do in a regular terminal1 which leaves me with 40 rows to read the error returned from the POST request. This is nowhere near enough. Hmmm...

💡 Remember that you're using LWP::UserAgent::Mockable (or its Mojo cousin) to record the tests to avoid using the network during the test pipeline. Realise that all the traffic from those network calls are stored as plain text files and you don't have to mess around with tcpdump just to inspect the HTTP headers anymore.

The raw file itself is a bit messy to look at (maybe I should write a quick tool that deserializes it for STDOUT), but it shows me that the Authorization header just isn't there. But it's in my code, I made sure. See right after the call to post ...

Ahh, after some reflection I realize that the post method makes the HTTP request as soon as it's invoked, which is why I was using build_tx in the GET request to add my Dev Key, like so

my $tx = $self->ua->build_tx( GET => $url );
$tx->req->headers->authorization( 'Bearer ' . $self->token );
...
try { $tx = $self->ua->start($tx) }
catch ($error) { ...
}

Now, the response has changed from UNAUTHORIZED to INTERNAL SERVER ERROR. I try the curl command suggested by the docs and that works fine. Look back in the mock file and see ... no payload.

Quietly add the json attribute to the transaction constructor

my $tx = $self->ua->build_tx( POST => $url, json => $hash );

because sending a JSON payload is so damn easy in Mojo.

All done - and Robert is your mother's brother.

  1. why yes, you can scroll up and down in a tmux session when you remember to enter Copy mode with Prefix [ so you get the arrow keys and the Page Up/Down buttons to play with. Enter to exit. ↩

Clipadd Cookbook

Perlancar

(A version of this article also appears as App::ClipboardUtils::Manual::HowTo::ClipaddCookbook on CPAN.)

Introduction

clipadd, a CLI included in the App::ClipboardUtils distribution, lets you add items to the desktop clipboard. It is clipboard-manager-agnostic, supporting backends like xclip and Klipper. It also has some convenient features which will be demonstrated in this document.

Adding multiple entries from a text file

% clipadd --split-by '\n---\n' < FILENAME

For example, after adding FAQ entries from this text file:

Question 1
Answer 1
---
Question 2
Answer 2
---
Question 3
Answer 3

then you can setup keyboard shortcuts like Ctrl-Alt-Up and Ctrl-Alt-Down in Klipper to quickly access those FAQ entries to provide quick answers in chats.

Adding ouput from command-line, one line at a time

% clipadd -c COMMAND
% clipadd -c COMMAND -t ; # to see the output on stdout as well

This is convenient because it lets you feed input to a command interactively and immediately get the output as clipboard content.

For example, using with safer:

% clipadd -c safer -t
Foo Bar, Inc.
foo-bar-inc
_

I can paste company names and get a slug name to use in file manager to create directory, etc.

Adding text fragments from a text file as clipboard contents

% clipadd --fragments < FILENAME

This is a variation of the previous recipe. Using fragments allows you to only add certain entries and in custom order. Example content of text file:

FAQS
====

Question 1
# BEGIN clipadd id=2
Answer 1
# END clipadd id=2

Question 2
# BEGIN clipadd id=1
Answer 2
# END clipadd id=1

Question 3
# BEGIN clipadd id=3
Answer 3
# END clipadd id=3

Question 4
Answer 4

See Also

clipadd (a.k.a. add-clipboard-content, a.k.a. ca).

Rewriting a script for the Homebrew package manager taught me how Go’s design choices align with platform-ready tools.

The problem with brew upgrade

By default, the brew upgrade command updates every formula (terminal utility or library). It also updates every cask (GUI application) it manages. All are upgraded to the latest version — major, minor, and patch. That’s convenient when you want the newest features, but disruptive when you only want quiet patch-level fixes.

Last week I solved this in Perl with brew-patch-upgrade.pl, a script that parsed brew upgrade’s JSON output, compared semantic versions, and upgraded only when the patch number changed. It worked, but it also reminded me how much Perl leans on implicit structures and runtime flexibility.

This week I ported the script to Go, the lingua franca of DevOps. The goal wasn’t feature parity — it was to see how Go’s design choices map onto platform engineering concerns.

Why port to Go?

  • Portfolio practice : I’m building a body of work that demonstrates platform engineering skills.
  • Operational focus : Go is widely used for tooling in infrastructure and cloud environments.
  • Learning by contrast : Rewriting a working Perl script in Go forces me to confront differences in error handling, type safety, and distribution.

The journey

Error handling philosophy

Perl gave me try/catch (experimental in the Perl v5.34.1 that ships with macOS, but since accepted into the language in v5.40). Go, famously, does not. Instead, every function returns an error explicitly.

use v5.34;
use warnings;
use experimental qw(try);
use Carp;
use autodie;



try {
  system brew, upgrade, $name;
  $result = upgraded;
}
catch ($e) {
  $result = failed;
  carp $e;
}
package main

import (
  os/exec
  log
)



cmd := exec.Command(brew, upgrade, name)
if output, err := cmd.CombinedOutput(); err != nil {
  log.Printf(failed to upgrade %s: %v\n%s,
    name,
    err,
    output)
}

The Go version is noisier, but it forces explicit decisions. That’s a feature in production tooling: no silent failures.

Dependency management

  • Perl : cpanfile + CPAN modules. Distribution means “install Perl (if it’s not already), install modules, run script.” Tools like carton and the cpan or cpanm commands help automate this. Additionally, one can use further tooling like fatpack and pp to build more self-contained packages. But those are neither common nor (except for cpan) distributed with Perl.
  • Go : go.mod + go build. Distribution is a single (platform-specific) binary.

For operational tools, that’s a massive simplification. No runtime interpreter, no dependency dance.

Type safety

Perl let me parse JSON into hashrefs and trust the keys exist. Go required a struct:

type Formula struct {
  Name string `json:"name"`
  CurrentVersion string `json:"current_version"`
  InstalledVersions []string `json:"installed_versions"`
}

The compiler enforces assumptions that Perl left implicit. That friction is valuable — it surfaces errors early.

Binary distribution

This is where Go shines. Instead of telling colleagues “install Perl v5.34 and CPAN modules,” I can hand them a binary. No need to worry about scripting runtime environments — just grab the right file for your system.

Available on the release page. Download, run, done.

Semantic versioning logic

In Perl, I manually compared arrays of version numbers. In Go, I imported golang.org/x/mod/semver:

import (
  golang.org/x/mod/semver
)



if semver.MajorMinor(toSemver(formula.InstalledVersions[0])) !=
  semver.MajorMinor(toSemver(formula.CurrentVersion)) {
  log.Printf(%s is not a patch upgrade, formula.Name)
  results.skipped++
  continue
}

Cleaner, more legible, and less error-prone. The library encodes the convention, so I don’t have to.

Deliberate simplification

I didn’t port every feature. Logging adapters, signal handlers, and edge-case diagnostics remained in Perl. The Go version focuses on the core logic: parse JSON, compare versions, run upgrades. That restraint was intentional — I wanted to learn Go’s idioms, not replicate every Perl flourish.

Platform engineering insights

Three lessons stood out:

  1. Binary distribution matters. Operational tools should be installable with a single copy step. Go makes that trivial.
  2. Semantic versioning is an operational practice. It’s not just a convention for library authors — it’s a contract that tooling can enforce.
  3. Go’s design aligns with platform needs. Explicit errors, type safety, and static binaries all reduce surprises in production.

Bringing it home

This isn’t a “Perl vs. Go” story. It’s a story about deliberate simplification, taking a working Perl script and recasting it in Go. The aim is to see how the language’s choices shape a solution to the same problem.

The result is homebrew-semver-guard v0.1.0, a small but sturdy tool. It’s not feature-finished, but it’s production-ready in the ways that matter.

Next up: I’m considering more Go tools, maybe even Kubernetes for services on my home server. This port was a practice, an artifact demonstrating platform engineering in action.

Links

This week in PSC (204) | 2025-10-13

blogs.perl.org

All three of us attended.

  • We touched on the recent discussion about the classification of modules included with the perl distribution. We agreed that the PSC and p5p need to do a better job of tracking which modules have active maintainers and who they are. Something like a dashboard would be useful for this, and we identified as a starting point that it would be good to have an up-to-date overview of dists, their maintainers, and whether they are active. Maintainers.pl probably already covers the list of dists but it would be useful to split the data out into a separate file so it could be used in other scripts, which could also populate records with additional data, outside of what Maintainers.pl needs, for their own purposes. For example a tool for querying PAUSE for the maintainers of dual-life dists and flagging deviations could check a custom field where known deviations (along with a comment) would be recorded, so as to not flag them.
  • We had a somewhat surface-level discussion about the future of stdio in Perl. Leon has been thinking about this and intends to write a separate mail to p5p about it.

[P5P posting of this summary]

How can I substitute every character that is:

  • not WORD (a-z, A-Z, 0-9) and also

  • not minus (-) and also

  • not hash (#)

to underline (_)

e.g.

"aB-09 !#"       =>      "aB-09__#"
"=a_-#(0"        =>      "_a_-#_0"

I started with:

s/\W/_/g

for the not-word, but I'm not sure about the not - and not #.

Updates for great CPAN modules released last week. A module is considered great if its favorites count is greater or equal than 12.

  1. App::Cmd - write command line apps with less suffering
    • Version: 0.338 on 2025-10-03, with 49 votes
    • Previous CPAN version: 0.337 was 9 months, 3 days before
    • Author: RJBS
  2. App::DBBrowser - Browse SQLite/MySQL/PostgreSQL databases and their tables interactively.
    • Version: 2.434 on 2025-10-05, with 17 votes
    • Previous CPAN version: 2.433 was 6 days before
    • Author: KUERBIS
  3. App::MHFS - A Media HTTP File Server. Stream your own music and video library via your browser and standard media players.
    • Version: v0.7.0 on 2025-10-07, with 14 votes
    • Previous CPAN version: v0.6.0 was 11 months, 16 days before
    • Author: GAHAYES
  4. App::Netdisco - An open source web-based network management tool.
    • Version: 2.091001 on 2025-10-09, with 762 votes
    • Previous CPAN version: 2.091000 was 8 days before
    • Author: OLIVER
  5. App::Sqitch - Sensible database change management
    • Version: v1.6.0 on 2025-10-06, with 3041 votes
    • Previous CPAN version: v1.5.2 was 5 months, 6 days before
    • Author: DWHEELER
  6. CGI - Handle Common Gateway Interface requests and responses
    • Version: 4.71 on 2025-10-01, with 46 votes
    • Previous CPAN version: 4.70 was 2 months, 24 days before
    • Author: LEEJO
  7. CPANSA::DB - the CPAN Security Advisory data as a Perl data structure, mostly for CPAN::Audit
    • Version: 20251003.001 on 2025-10-03, with 25 votes
    • Previous CPAN version: 20250807.001 was 1 month, 27 days before
    • Author: BRIANDFOY
  8. DateTime::Format::Natural - Parse informal natural language date/time strings
    • Version: 1.22 on 2025-10-01, with 18 votes
    • Previous CPAN version: 1.21_01 was 1 month, 12 days before
    • Author: SCHUBIGER
  9. Imager - Perl extension for Generating 24 bit Images
    • Version: 1.029 on 2025-10-06, with 68 votes
    • Previous CPAN version: 1.028 was 3 months, 19 days before
    • Author: TONYC
  10. Mojolicious - Real-time web framework
    • Version: 9.42 on 2025-10-01, with 507 votes
    • Previous CPAN version: 9.41 was 2 months, 29 days before
    • Author: SRI
  11. Selenium::Remote::Driver - Perl Client for Selenium Remote Driver
    • Version: 1.50 on 2025-10-06, with 49 votes
    • Previous CPAN version: 1.49 was 2 years, 6 months before
    • Author: TEODESIAN
  12. Specio - Type constraints and coercions for Perl
    • Version: 0.53 on 2025-10-04, with 12 votes
    • Previous CPAN version: 0.52 was 1 month, 26 days before
    • Author: DROLSKY
  13. SPVM - The SPVM Language
    • Version: 0.990102 on 2025-10-10, with 36 votes
    • Previous CPAN version: 0.990101 was before
    • Author: KIMOTO
  14. WWW::Mechanize::Chrome - automate the Chrome browser
    • Version: 0.74 on 2025-10-06, with 22 votes
    • Previous CPAN version: 0.73 was 1 year, 6 months, 7 days before
    • Author: CORION
  15. YAML::Syck - Fast, lightweight YAML loader and dumper
    • Version: 1.36 on 2025-10-10, with 18 votes
    • Previous CPAN version: 1.35 was before
    • Author: TODDR

(dcxvi) metacpan weekly report - perl

Niceperl

This is the weekly favourites list of CPAN distributions. Votes count: 67

Week's winner: perl (+3)

Build date: 2025/10/12 06:49:03 GMT


Clicked for first time:


Increasing its reputation:

Episode 6 - CPAN Testers

The Underbar
During the Perl Toolchain Summit 2025 in Leipzig, we interviewed the CPAN Testers team. They told us about the history of the project, its recent problems, and how a new team formed around its (then) lone maintainer. They also told us about their plans for the future.

bless vs Class::Mite

blogs.perl.org


Class::Mite is getting better relatively as numbers shown in the post below: https://theweeklychallenge.org/blog/bless-vs-class-mite


Dave writes:

Last month I completed rewriting and modernising perlxs.pod, Perl's reference manual for XS. It's now sitting as PR #23795, and will hopefully get merged before too long. (I actually completed the work two days into October, so next month's report will show a few hours.)

From the PR's description:

This branch completely rewrites and modernises the XS reference manual, perlxs.pod.

The new file is about twice the size of the old one.

This branch:

  • deletes some obsolete sections;
  • reorders the existing sections into a more logical order;
  • adds a large new introductory/overview part, which explains all the background needed to understand what XSUBs do, including SVs, the stack, reference counts, magic etc.
  • includes a BNF syntax section
  • modernises: e.g. it uses ANSI parameter syntax throughout
  • has a fully-worked example using T_PTROBJ

SUMMARY:

  • 23:19 modernise perlxs.pod
  • 3:50 rationalise XS typemap file search order

    Total:

    • 27:09 (HH::MM)

Tony writes: ``` [Hours] [Activity] 2025/09/01 Monday 0.48 #23641 testing, comment 0.77 #23659 review and comment 0.20 #23659 review updates and comment 0.83 reply DB.pm discussion 0.25 #23648 try to reproduce and comment

0.47 #23659 review and approve

3.00

2025/09/02 Tuesday 0.60 #23627 follow-up, review and comment 0.53 #23616 research and comment 0.23 #23669 review, look for a similar test, comment 0.43 #23641 testing, follow-up 0.32 #23648 follow-up 0.32 #23667 review, comment 0.57 #23668 review, testing, comment and approve

0.85 #13307/#23661 testing, comment on both

3.85

2025/09/03 Wednesday 0.12 #23668 review updates and reapprove 0.57 #23661 review discussion and consider, request changes 0.18 #23616 review and approve 0.33 #23627 review updates and approve

0.62 #23669 testing and comments

1.82

2025/09/04 Thursday 0.15 #23661 review changes, test results and comment 1.07 #23573 testing, approve with comment 0.13 #23669 review updates and approve 0.52 #23641 review, research and comment 0.15 #23638 check latest changes and approve 0.37 #23680 review and approve 0.17 #23680 review updates and still approved 0.18 #23682 review, check test failures and comment

0.33 #23679 review

3.07

2025/09/05 Friday

0.42 #23680 follow-up

0.42

2025/09/08 Monday 1.07 #23641 review, research, comments 0.08 #23670 review and approve 0.55 #23671 review, research and approve with comment 0.08 #23672 review and approve 0.18 #23678 review and comment 0.22 #23683 review and comment

2.62 test_pl.pod - make a start

4.80

2025/09/09 Tuesday 0.82 #23679 review and comment 1.48 test_pl.pod - more

1.78 test_pl.pod - more

4.08

2025/09/10 Wednesday 1.30 #23690 review, testing and comment 0.15 #23691 review and approve 0.70 #23694 review and approve

1.42 test_pl.pod - mostly done, needs review

3.57

2025/09/11 Thursday 0.48 #23695 review and comment 0.12 #23696 review and approve 0.10 #23697 review and approve 1.48 #23698 review and comments

1.38 test_pl.pod, add and test example, make PR 23700

3.56

2025/09/15 Monday 0.25 #23700 follow-up 1.57 #23690 review updates, testing, research and comment 0.32 #23695 review updates and approve 1.00 #23641 testing, research and comment

0.92 test_pl.pod - some missing stuff, don’t talk about TAP

4.06

2025/09/16 Tuesday 0.42 #23698 review updated PR and approve 0.73 test_pl.pod - minor fixes, add to podcheck 0.28 #23678 review and comment 0.80 #23702 review

0.92 #23702 try to understand line-breaking

3.15

2025/09/17 Wednesday 0.17 #23702 comment 1.50 #23678 try to understand the threads watchdog 1.00 #23678 debugging, testing and comment

0.65 #23715 review and comments

3.32

2025/09/18 Thursday 0.12 #23717 review and approve 0.27 #23718 review and approve 0.42 #23719 review and approve 0.32 #23720 review, research and comment 0.17 #23721 review and approve 0.52 #23714 review, research and comment 0.42 #23720 research and follow-up

1.48 test.pl/test-dist-modules.pl clean up, testing, fixes

3.72

2025/09/22 Monday 0.42 #23720 review updates and approve 0.22 #23714 comment 0.63 #23747 review and comment 0.58 #23731 review and comment 0.52 #23730 review, research and comment 0.72 #23728 review, research and comments 0.12 #23734 review and approve 0.13 #23735 review and approve 0.15 #23736 review and approve with comment 0.33 #23742 review and approve 0.42 #23743 review and approve 0.13 #23749 review and approve

0.12 #23746 review and approve

4.49

2025/09/23 Tuesday 0.70 #23731 review updates and approve 0.25 #23752 review and comment 0.32 #23754 review and approve 0.22 #23747 review update and approve 0.17 #23725 review and briefly comment

0.27 #23752 review update and comment

1.93

2025/09/24 Wednesday 0.82 #23710 review and approve 0.25 #23076 review discussion and change and approve 0.83 #23761 review, research and approve 1.22 #23753 look into CI failure, testing, fixes start a full test 0.28 #23753 clean up and push to PR for CI

0.37 #23757 review and approve

3.77

2025/09/25 Thursday 0.60 github notifications 0.23 #23753 comment and approve 1.05 #23759 review, check cpan, testing, approve with comment 0.17 #23763 review and comment 0.08 #23765 review and approve 0.08 #23766 review and approve 0.08 #23767 review and comment 0.82 #21877 write tests, work on re-work

0.45 #21877 more re-work

3.56

2025/09/29 Monday

3.27 #23641 review, testing

3.27

2025/09/30 Tuesday 1.28 #23782 review and comments 0.35 #23779 review and approve

0.63 #23641 more testing and comment

2.26

Which I calculate is 61.7 hours.

Approximately 63 tickets were reviewed or worked on. ```


Paul writes:

This month I spent mostly tidying up various bits of fallout from last month's OP_MULTIPARAM work towards signatures, and also got the named parameters branch (just-about) ready for review and merge, along with a few other bugfixes.

  • 1 = BBC ticket meta
    • https://github.com/Perl/perl5/issues/23675
  • 2 = Bugfix parse_subsignature() on empty parens
    • https://github.com/Perl/perl5/issues/17689
  • 2 = BBC ticket XS-Parse-Sublike
    • https://github.com/Perl/perl5/issues/23674
  • 5.5 = Improvements to B::Deparse of on signatures
    • https://github.com/Perl/perl5/issues/23699
    • https://github.com/Perl/perl5/pull/23710
  • 1 = BBC ticket Syntax-Keyword-MultiSub
    • https://github.com/Perl/perl5/issues/23712
  • 1 = BBC ticket Future-AsyncAwait
    • https://github.com/Perl/perl5/issues/23711
  • 1 = Continue work on signature-named-params branch
    • https://github.com/leonerd/perl5/tree/signature-named-parameters
  • 4 = COP warnings API additions
    • https://github.com/Perl/perl5/pull/23731
  • 1 = BBC ticket Syntax-Keyword-Try
    • https://github.com/Perl/perl5/issues/23609
  • 1 = Bugfix module ends in ADJUST block
    • https://github.com/Perl/perl5/issues/23758
  • 2 = Modernize attributes.pm to use v5.40
    • https://github.com/Perl/perl5/pull/23769
  • 2 = Bugfix fieldinfo during thread cloning
    • https://github.com/Perl/perl5/issues/23771
  • 1 = Other github code reviews

Total: 24.5 hours

Available from the Wiki Haven.
I have still not had time to update CPAN::MetaCustodian, so it does not yet work correctly with the latest version of Perl.Wiki.html.

• Analyze existing Perl scripts and understand their functionality
• Convert Perl scripts to Python while maintaining performance and functionality
• Debug and optimize converted Python scripts
• Collaborate with team members to meet project requirements
• Document the conversion process and provide knowledge transfer
________________________________________
Technical Skills:
Perl (5+ years):
• Strong understanding of Perl syntax, modules, and libraries
• Experience with regular expressions, file handling, and data manipulation
• Familiarity with legacy Perl scripts and debugging
Python (2+ years):
• Strong understanding of Python syntax, libraries, and frameworks
• Experience with Python’s re module, file handling, and data manipulation
• Knowledge of Python best practices (PEP 8 standards)
Code Conversion:
• Hands-on experience converting Perl scripts to Python
• Ability to map Perl constructs (scalars, arrays, hashes) to Python equivalents (variables, lists, dictionaries)
• Understanding of differences in error handling, string manipulation, and OOP between Perl and Python
________________________________________
Soft Skills:
• Analytical Thinking: Ability to understand complex Perl scripts before conversion
• Attention to Detail: Ensure converted Python scripts maintain original functionality
• Communication: Document and explain conversion process to stakeholders
• Collaboration: Work closely with developers, testers, and stakeholders

For quite some I wanted to write a small static image gallery so I can share my pictures with friends and family. Of course there are a gazillion tools like this, but, well, sometimes I just want to roll my own.

I took the opportunity during our stay in Schwaz to take a few hours and hack together snig, the (small | static | simple | stupid | ...) image gallery. Here you can see the example gallery (showing some of the pictures I took in Schwaz).

I used the old, well tested technique I call brain coding0, where you start with an empty vim buffer and type some code (Perl, HTML, CSS) until you're happy with the result. It helps to think a bit (aka use your brain) during this process.

According to my timetracker I spend 8h 15min (probably half of it spend fiddling with CSS...).

Installation

I used the new Perl class feature, so you'll need at least Perl 5.40 which was released last year and is included in current Debian.

I prefer cpm to install CPAN modules:

cpm install -g Snig

I haven't provided a Containerfile yet, but if somebody is interested, drop me a line.

You can get the raw source code from Source Hut (as I don't want to support the big LLM vacuum machines formerly known as Git(Hub|Lab)).

Example usage

You need a folder filled with images (eg some-pictures) and some place where you can host a folder and a bunch of HTML files.

ls -la some-pictures/
7156 -rw------- 1 domm domm 7322112 Oct  6 09:14 P1370198.JPG
7188 -rw------- 1 domm domm 7354880 Oct  6 09:14 P1370208.JPG
7204 -rw------- 1 domm domm 7369728 Oct  6 09:14 P1370257.JPG

Then you do

snig.pl --input some-pictures --output /var/web/my-server-net/gallery/2025-10-some-pictures --name "Some nice pictures"

This will:

  • find all jpgs in the folder some-pictures
  • copy them into the output folder
  • generate a thumbnail (for use in the list)
  • generate a preview (for use in the detail page)
  • generate a HTML overview page
  • generate a HTML detail page for each image, linked to the next/prev image
  • generate a zip archive of the images
  • EXIF rotation hints are used to properly orient the previews
  • images are sorted by the EXIF timestamp (per default, you could also use mtime)
ls -la /var/web/my-server-net/gallery/2025-10-some-pictures
-rw-rw-r-- 1 domm domm      1370 Sep 26 21:15 20250914_p1370198.html
-rw-rw-r-- 1 domm domm      1370 Sep 26 21:15 20250914_p1370208.html
-rw-rw-r-- 1 domm domm      1370 Sep 26 21:15 20250915_p1370253.html
-rw-rw-r-- 1 domm domm 171738640 Sep 26 21:12 2025-10-some-pictures.zip
-rw-rw-r-- 1 domm domm      3977 Sep 26 21:15 index.html
-rw-rw-r-- 1 domm domm   7322112 Sep 26 21:12 orig_20250914_P1370198.JPG
-rw-rw-r-- 1 domm domm   7354880 Sep 26 21:12 orig_20250914_P1370208.JPG
-rw-rw-r-- 1 domm domm   7686656 Sep 26 21:12 orig_20250915_P1370253.JPG
-rw-rw-r-- 1 domm domm    106382 Sep 26 21:12 preview_20250914_P1370198.JPG
-rw-rw-r-- 1 domm domm    170346 Sep 26 21:12 preview_20250914_P1370208.JPG
-rw-rw-r-- 1 domm domm    133342 Sep 26 21:12 preview_20250915_P1370253.JPG
-rw-rw-r-- 1 domm domm      1434 Sep 26 21:15 snig.css
-rw-rw-r-- 1 domm domm      5128 Sep 26 21:12 thumbnail_20250914_P1370198.JPG
-rw-rw-r-- 1 domm domm      9495 Sep 26 21:12 thumbnail_20250914_P1370208.JPG
-rw-rw-r-- 1 domm domm      6408 Sep 26 21:12 thumbnail_20250915_P1370253.JPG

You can then take the output folder and rsync it onto a static file server. Or install snig on your web server and do the conversion there..

Again, take a look at the example gallery.

Limitations

For now, snig is very simple and stupid, so quite a few things are still hard coded (like copyright and license). If somebody else wants to use it, I will add some more command line flags and/or a config file. But for now this is not needed, so I did not add it.

Have fun

I had quite some fun hacking snig and also used it as an opportunity to learn the new class syntax (and fresh up on subroutine / method signatures). So not only did I use my brain, I actually learned something new!

Please feel free to give snig a try. Or just use one of the countless other static image gallery generators. Or just write your own!

lobste.rs, hacker news, reddit

Footnotes

0 As opposed to vibe coding.

Updates for great CPAN modules released last week. A module is considered great if its favorites count is greater or equal than 12.

  1. App::DBBrowser - Browse SQLite/MySQL/PostgreSQL databases and their tables interactively.
    • Version: 2.433 on 2025-09-29, with 17 votes
    • Previous CPAN version: 2.432 was 2 days before
    • Author: KUERBIS
  2. App::Netdisco - An open source web-based network management tool.
    • Version: 2.091000 on 2025-09-30, with 760 votes
    • Previous CPAN version: 2.090002 was 5 days before
    • Author: OLIVER
  3. Dist::Zilla::Plugin::Git - Update your git repository after release
    • Version: 2.052 on 2025-09-28, with 44 votes
    • Previous CPAN version: 2.051 was 1 year, 3 months, 12 days before
    • Author: ETHER
  4. JSON::Schema::Modern - Validate data against a schema using a JSON Schema
    • Version: 0.619 on 2025-09-28, with 12 votes
    • Previous CPAN version: 0.618 was 21 days before
    • Author: ETHER
  5. Mojo::Pg - Mojolicious ♥ PostgreSQL
    • Version: 4.28 on 2025-09-29, with 98 votes
    • Previous CPAN version: 4.27 was 3 years, 6 months, 14 days before
    • Author: SRI
  6. SPVM - The SPVM Language
    • Version: 0.990093 on 2025-09-30, with 36 votes
    • Previous CPAN version: 0.990092 was 4 days before
    • Author: KIMOTO

The Experiment

At the beginning of the year, we ran a small experiment at work. We hired four annotators and let them rate 900 sentences (the details are not important). To decide whether the inter-annotator agreement was significant, we calculated (among others) Krippendorff’s alpha coefficient.

I’d used Perl for everything else in the project, so I reached for Perl to calculate the alpha, as well. I hadn’t found any module for it on CPAN, so I wrote one: I read the Wikipedia page and implemented the formulas.

The Real Data

The experiment was promising, so we got additional funding. We hired 3 more annotators, and a few months later, another nine. This increased the number of raters to 16. So far, they’ve rated about 200K sentences. Each sentence has been annotated by at least two annotators (usually three).

One day, I decided to calculate the inter-annotator agreement for the new data. To my surprise, the calculation took more than 6 hours.

Profiling

I ran the NYTProf profiler on a smaller dataset to discover the problematic areas of the code. Browsing the results I clearly identified the culprit: the method responsible for building the coincidence matrix.

sub _build_coincidence($self) {
    my %coinc;
    my @s = $self->vals;
    for my $v (@s) {
        for my $v_ (@s) {
            $coinc{$v}{$v_} = sum(map {
                my $unit = $_;
                my @k = keys %$unit;
                sum(0,
                    map {
                        my $i = $_;
                        scalar grep $unit->{$_} eq $v_,
                        grep $i ne $_, @k
                    } grep $unit->{$_} eq $v, @k
                ) / (@k - 1)
            } @{ $self->units });
        }
    }
    return \%coinc
}

You can see the four nested loops here (two for’s and two map’s). I apologise, that’s how I’d understood the formula.

The Solution Suggested by AI

“This is the right time to experiment with AI!” thought I to myself. I started a session with GPT-4o, showed it the whole module and asked for a more efficient version. The LLM correctly identified the four nested loops, suggested an improvement, and generated code to implement it.

Unfortunately, the code didn’t even compile. I asked the AI several times to fix the problems, until I got a runnable code, but its running time wasn’t different to the original implementation. So I asked it to try harder, and after a third iteration of the whole ordeal, we had the following code that ran for 20 seconds instead of 22:

 sub _build_coincidence($self) {
    my %coinc;
    my @vals = $self->vals;

    # Initialize coincidence counts
    for my $v (@vals) {
        for my $v_ (@vals) {
            $coinc{$v}{$v_} = 0;
        }
    }

    # Iterate over each unit
    for my $unit (@{ $self->units }) {
        my @keys = keys %$unit;
        my $unit_count = @keys;

        # Count occurrences of each value in the current unit
        my %value_count;
        $value_count{ $unit->{$_} }++ for @keys;

        # Calculate coincidences based on the value counts
        for my $v (@vals) {
            for my $v_ (@vals) {
                if (exists $value_count{$v} && exists $value_count{$v_}) {
                    my $coinc_count = 0;

                    # Count pairs of keys that match the values
                    for my $key1 (@keys) {
                        for my $key2 (@keys) {
                            next if $key1 eq $key2;  # Skip same keys
                            if ($unit->{$key1} eq $v && $unit->{$key2} eq $v_) {
                                $coinc_count++;
                            }
                        }
                    }

                    # Update the coincidence count
                    $coinc{$v}{$v_} += $coinc_count / ($unit_count - 1) if $unit_count > 1;
                }
            }
        }
    }
    return \%coinc
}

That’s many lines. We’ve got comments, which should be nice, but… For the large data, I’d still have had to wait for several hours.

The Optimisation

I decided to bite the bullet and tried to optimise the code myself. I went over both the original code and the suggested improvement to realise the LLM wanted to cache the number of values in %value_count but never really used the value: it only checked for existence.

What was really needed was not to iterate over all the values every time skipping the irrelevant ones, but to precompute the relevant values and reduce the breadth of the loops. Usually, there were only two or three values per unit (each sentence was annotated by at least two annotators), so there was no reason to check all sixteen possible.

This is the code I wrote and released as the new version of the module. For the large dataset, the running time dropped from six hours to four seconds.

 sub _build_coincidence($self) {
    my @vals = $self->vals;
    my %coinc;
    @{ $coinc{$_} }{@vals} = (0) x @vals for @vals;

    for my $unit (@{ $self->units }) {
        my %is_value;
        @is_value{ values %$unit } = ();
        my @values = keys %is_value;
        my @keys   = keys %$unit;

        for my $v (@values) {
            for my $v_ (@values) {
                my $coinc_count = 0;
                for my $key1 (@keys) {
                    for my $key2 (@keys) {
                        next if $key1 eq $key2;

                        ++$coinc_count
                            if $unit->{$key1} eq $v
                            && $unit->{$key2} eq $v_;
                    }
                }
                $coinc{$v}{$v_} += $coinc_count / (@keys - 1);
            }
        }
    }
    return \%coinc
}

I showed my final code to the LLM. It congratulated me on the brilliant solution, emphasising some of the tricks I’d used to make it faster (e.g. filling the matrix by zeros using the repetition operator).

Did it help me? I’m not sure. It motivated me to study the code and find the solution, but it didn’t really contribute to it. It took me about two hours including finding the solution myself.

What’s your experience with AI?

For the second consecutive year, The Perl and Raku Foundation (TPRF) is overjoyed to announce a donation of USD 25,000 from DuckDuckGo.

DuckDuckGo has demonstrated how Perl and its ecosystem can deliver power and scale to drive the DuckDuckGo core systems, plug-in framework and Instant Answers. The Foundation is grateful that DuckDuckGo recognises the importance of Perl, and for their generous funding support for a second year through their charitable donations programme.

– Stuart J Mackintosh, President of The Perl and Raku Foundation

Last year’s donation of USD 25,000 from DuckDuckGo was instrumental in helping to fund the foundation’s Core Perl Maintenance Fund and this year’s donation will help to fund more of the same crucial work that keeps the Perl language moving forward.

Fireworks celebration

Paul “LeoNerd” Evans is one of the developers who gets regular funding from the Perl Core Maintenance Fund. Here is a short list of just some of the many contributions which Paul has made to core Perl as part of the maintenance fund work:


  • The builtin module (5.36), making available many new useful language-level utilities that were previously loaded from modules like Scalar::Util
  • The complete feature ‘class’ system (5.38), adding proper object-orientation syntax and abilities
  • Lexical method support (5.42), adding my method and the $obj->&method invocation syntax for better object encapsulation
  • Stabilising some of the recent experiments - signatures (5.36), try/catch (5.40), foreach on multiple vars (5.40)
  • Ability to use the //= and ||= operators in signatures (5.38), performance improvements and named parameters (upcoming in next release)
  • The new any and all keywords (5.42)

We look forward to many more innovative contributions from Paul over the coming year.

While TPRF never takes continued support for granted, when it does arrive, it allows the foundation to plan for the future with much greater confidence. Multi-year partnerships with our sponsors allow us to continue to prioritize important work, knowing that we will have the runway that we need to fund the work which helps to sustain the Perl Language and its associated communities.

For more information on how to become a sponsor, please contact: olaf@perlfoundation.org


"Fireworks" by colink. is licensed under CC BY-SA 2.0.

Episode 5 - Test::Smoke

The Underbar
During the Perl Toolchain Summit 2025 in Leipzig, we interviewed Merijn Brand, Test::Smoke's founder, and Thibault Duponchelle, its new maintainer following the passing of Abe Timmerman, about the history of the project, and how to contribute to it.

Easy SEO for lazy programmers

Perl Hacks

A few of my recent projects—like Cooking Vinyl Compilations and ReadABooker—aim to earn a little money via affiliate links. That only works if people actually find the pages, share them, and get decent previews in social apps. In other words: the boring, fragile glue of SEO and social meta tags matters.

As I lined up a couple more sites in the same vein, I noticed I was writing very similar code again and again: take an object with title, url, image, description, and spray out the right <meta> tags for Google, Twitter, Facebook, iMessage, Slack, and so on. It’s fiddly, easy to get 80% right, and annoying to maintain across projects. So I pulled it into a small Moo role—MooX::Role::SEOTags—that any page-ish class can consume and just emit the right tags.

What are these tags and why should you care?

When someone shares your page, platforms read a handful of standardised tags to decide what to show in the preview:

  • Open Graph (og:*) — The de-facto standard for title, description, URL, image, and type. Used by Facebook, WhatsApp, Slack, iMessage and others.
  • Twitter Cards (twitter:*) — Similar idea for Twitter/X; the common pattern is twitter:card=summary_large_image plus title/description/image.
  • Classic SEO tags<title>, <meta name="description">, and a canonical URL tell search engines what the page is about and which URL is the “official” one.

MooX::Role::SEOTags gives you one method that renders all of that, consistently, from your object’s attributes.

For more information about OpenGraph, see ogp.me.

What it does

MooX::Role::SEOTags adds a handful of attributes and helper methods so any Moo (or Moose) class can declare the bits of information that power social previews and search snippets, then render them as HTML.

  • Open Graph tags (og:title, og:type, og:url, og:image, etc.)
  • Twitter Card tags (twitter:card, twitter:title, twitter:description, twitter:image)
  • Standard SEO: <title>, meta description, canonical <link rel="canonical">
  • A single method to render the whole block with one call
  • But also individual methods to give you more control over tag placement

That’s the whole job: define attributes → get valid tags out.

Quick start

Install the role using your favourite CPAN module installation tool.

cpanm MooX::Role::SEOTags

Then, in your code, you will need to add some attributes or methods that define the pieces of information the role needs. The role requires four pieces of information – og_title, og_description, og_url and og_type – and og_image is optional (but highly recommended).

So a simple class might look like this:

package MyPage;
use Moo;
with 'MooX::Role::SEOTags';

# minimal OG fields
has og_title      => (is => 'ro', required => 1);
has og_type       => (is => 'ro', required => 1);   # e.g. 'article'
has og_url         => (is => 'ro', required => 1);
has og_description => (is => 'ro');

# optional niceties
has og_image        => (is => 'ro');           # absolute URL
has twitter_card    => (is => 'ro', default => sub { 'summary_large_image' });

1;

And then you create the object:

my $page = MyPage->new(
  og_title       => 'How to Title a Title',
  og_type        => 'article',
  og_url         => 'https://example.com/post/title',
  og_image       => 'https://example.com/img/hero.jpg',
  og_description => 'A short, human description of the page.',
);

Then you can call the various *_tag and *_tags methods to get the correct HTML for the various tags.

The easiest option is to just produce all of the tags in one go:

say $page->tags;

But, for more control, you can call individual methods:

say $page->title_tag;
say $page->canonical_tag;
say $page->og_tags;
# etc...

Depending on which combination of method calls you use, the output will look something like this:

<title>How to Title a Title</title>
<meta name="description" content="A short, human description of the page.">
<link rel="canonical" href="https://example.com/post/title">

<meta property="og:title" content="How to Title a Title">
<meta property="og:type"  content="article">
<meta property="og:url"   content="https://example.com/post/title">
<meta property="og:image" content="https://example.com/img/hero.jpg">

<meta name="twitter:card"        content="summary_large_image">
<meta name="twitter:title"       content="How to Title a Title">
<meta name="twitter:description" content="A short, human description of the page.">
<meta name="twitter:image"       content="https://example.com/img/hero.jpg">

In many cases, you’ll be pulling the data from a database and displaying the output using a templating system like the Template Toolkit.

my $tt = Template->new;

my $object = $resultset->find({ slug => $some_slug });

$tt->process('page.tt', { object => $object }, "$some_slug/index.html");

In this case, you’d just add a single call to the <head> of your page template.

<head>

  <!-- lots of other HTML -->

[% object.tags %]

</head>

A note if you used my earlier Open Graph role

If you spotted MooX::Role::OpenGraph arrive on MetaCPAN recently: SEOTags is the “grown-up” superset. It does Open Graph and Twitter and standard tags, so you only need one role. The old module is scheduled for deletion from MetaCPAN.

SEO tags and JSON-LD

These tags are only one item in the SEO toolkit that you’d use to increase the visibility of your website. Another useful tool is  JSON-LD – which allows you to add a machine-readable description of the information that your page contains. Google loves JSON-LD. And it just happens that I have another Moo role called MooX::Role::JSON_LD which makes it easy to add that to your page too. I wrote a blog post about using that earlier this year.

In conclusion

If you’ve got even one page that deserves to look smarter in search and social previews, now’s the moment. Pick a page, add a title, description, canonical URL and a decent image, and let MooX::Role::SEOTags spit out the right tags every time (and, if you fancy richer results, pair it with MooX::Role::JSON_LD). Share the link in Slack/WhatsApp/Twitter to preview it, fix anything that looks off, and ship. It’s a 20-minute tidy-up that can lift click-throughs for years—so go on, give one of your pages a quick SEO spruce-up today.

The post Easy SEO for lazy programmers first appeared on Perl Hacks.

TL;DR

I built a dynamic workspace loader for i3wm that lets me switch between multiple context-aware workspace groups (like “Client”, “Company”, and “Personal”) without losing state. It uses Perl, AnyEvent::I3, YAML config, and tick events to load layouts and launch apps on demand.

Workspaces on Demand

I have been using i3 as a Window Manager for years now. And while I love working with i3, there is one thing I found annoying. And that is that it lacks context awareness. What do I mean by this? I use it in several contexts: “Client”, “Company”, and “Personal”. Meaning, I’m in either client mode, company mode or personal mode. And ‘client mode’ can mean working with one or multiple clients at once. I pitched my initial idea on i3’s discussion pages and someone else asked about something similar as KDE’s “Activities”.