<!-- category: hidden-gems -->
The diagnostics Pragma
Perl tells you something is wrong. Then it tells you absolutely nothing useful about what.use warnings; my @x = (1, 2, 3); my $y = @x[1];
Okay. But why is it better? What is actually happening? What doesScalar value @x[1] better written as $x[1] at script.pl line 3.
@x[1] do differently than $x[1]? Perl knows the answer. It just
does not feel like sharing.
Until you ask nicely.
use diagnostics; use warnings; my @x = (1, 2, 3); my $y = @x[1];
Now we're talking. A full paragraph of explanation, pulled straight from Perl's internal documentation. The terse one-liner becomes a mini-tutorial. This is diagnostics.Scalar value @x[1] better written as $x[1] at script.pl line 4 (#1) (W syntax) You've used an array slice (indicated by @) to select a single element of an array. Generally it's better to ask for a scalar value (indicated by $). The difference is that $foo[&bar] always behaves like a scalar, both when assigning to it and when evaluating its argument, while @foo[&bar] behaves like a list when you assign to it, and provides a list context to its subscript, which can do weird things if you're expecting only one subscript.
Part 1: THE BASIC USAGE
One line. That is all it takes:Drop it at the top of your script, right afteruse diagnostics;
use warnings. Every
warning and error message that Perl emits gets expanded into a
multi-paragraph explanation sourced from the perldiag man page.
Without diagnostics, you get nothing (autovivification is silent here). But try something that does warn:#!/usr/bin/env perl use strict; use warnings; use diagnostics; my %h; my $val = $h{nonexistent}{deep}{key};
#!/usr/bin/env perl use strict; use warnings; use diagnostics; print "hello" . undef;
Instead of just "uninitialized value," you get a complete explanation of what Perl did with that undef and what you probably should have done instead.Use of uninitialized value in concatenation (.) or string at script.pl line 6 (#1) (W uninitialized) An undefined value was used as if it were already defined. It was interpreted as a "" or a 0, but maybe it was a mistake. To suppress this warning assign a defined value to your variables. ...
Part 2: HOW IT WORKS UNDER THE HOOD
Thediagnostics pragma is not magic. It is a module that ships with
Perl core, and it does exactly two things:
- At compile time, it loads and parses the entire
perldiagman page
- It installs a
$SIG{__WARN__}handler and a$SIG{__DIE__}handler
That is why it works on every warning and every fatal error. It is sitting between Perl's messaging system and your terminal, enriching every message that passes through.Perl emits warning | v $SIG{__WARN__} fires | v diagnostics looks up the message pattern in perldiag | v Prints original message PLUS the multi-paragraph explanation
The lookup is pattern-based. Perl's diagnostic messages contain
placeholders like %s and %d. The module matches your actual
error text against these patterns to find the right explanation.
Part 3: BEFORE AND AFTER
Here is a side-by-side gallery. On the left, what Perl normally says. On the right, the gist of what diagnostics adds.The verbose versions are not guesses. They are the official Perl documentation for that exact diagnostic. Written by the Perl porters. Reviewed over decades. Usually accurate and helpful.TERSE: "my" variable $x masks earlier declaration VERBOSE: You declared a variable with the same name in the same scope. The inner declaration hides the outer one. This is almost always a mistake. TERSE: Useless use of a constant in void context VERBOSE: You used a value (like a string or number) as a statement but didn't do anything with the result. Perl computed it and threw it away. You probably meant to assign it. TERSE: Bareword "foo" not allowed while "strict subs" in use VERBOSE: You used an unquoted string where Perl expected an operator or expression. Under strict subs, barewords aren't allowed as a safety measure. Quote it or declare a subroutine. TERSE: Can't use string ("3") as an ARRAY ref VERBOSE: You tried to dereference a plain string as if it were a reference. Something upstream gave you a string instead of the reference you expected.
Part 4: THE SPLAIN COMMAND
Do not want to adduse diagnostics to your code? Use splain
from the command line instead. It is a filter that reads Perl
error output and adds the verbose explanations:
Same lookup table, same explanations. But it processes output after the fact instead of hooking into the runtime. Feed it any Perl error messages and it will expand them.$ perl script.pl 2>&1 | splain
You can also use it on saved log files:
The$ perl buggy_script.pl 2> errors.log $ splain < errors.log
splain tool ships with Perl. No installation needed. It is
literally the same perldiag lookup engine, packaged as a command-line
filter.
One difference: splain only processes text you feed it. It does not
hook into your running program. If your script swallows warnings
internally, splain will never see them.
Part 5: RUNTIME OVERHEAD
Here is the part nobody talks about. The diagnostics pragma is not free.At compile time, it reads and parses the entire perldiag man page. That is a big document. On Perl 5.40, it is over 5,000 lines of diagnostic descriptions. Parsing that adds noticeable startup time.
About 80ms of extra startup on a modern machine. Trivial for a one-off debugging session. Not trivial if you are running the script 10,000 times in a loop, or if it is a CGI script that starts fresh on every request.# benchmark: startup time with and without diagnostics # (rough numbers, vary by system) # Without: # $ time perl -e 'use strict; use warnings; 1' # real 0m0.011s # With: # $ time perl -e 'use strict; use warnings; use diagnostics; 1' # real 0m0.089s
The runtime overhead per warning is also nonzero. Every warning triggers a hash lookup and string concatenation. If your code emits thousands of warnings (maybe you should fix those), the diagnostics handler will slow things down.
Part 6: DEVELOPMENT ONLY
The diagnostics pragma is a development tool. Not a production tool. Put it in when debugging, take it out before deploying.A clean pattern:
Or use an environment variable to toggle it:#!/usr/bin/env perl use strict; use warnings; # Uncomment for verbose error explanations during development # use diagnostics; use feature 'say';
Now you can flip verbose errors on and off without editing the script:#!/usr/bin/env perl use strict; use warnings; BEGIN { if ($ENV{PERL_DIAGNOSTICS}) { require diagnostics; diagnostics->import(); } }
This is especially useful for shared codebases. Everyone gets terse messages by default. When someone hits a confusing error, they set the env var and get the full story.$ PERL_DIAGNOSTICS=1 perl my_script.pl # verbose $ perl my_script.pl # normal
Part 7: COMBINING WITH WARNINGS
The diagnostics pragma plays nicely with warnings categories. You can enable specific warning categories and get verbose output for just those:Warnings you have explicitly disabled stay disabled. Diagnostics only enhances warnings that are active. So you can silence the noisy ones you already understand and get verbose help on the ones that surprise you.use warnings; use diagnostics; no warnings 'uninitialized'; # suppress these even with diagnostics
You can also use the -W flag for maximum coverage:
The$ perl -W -Mdiagnostics script.pl
-W flag enables all warnings regardless of what the script says.
Combined with diagnostics, this is the nuclear option for debugging
someone else's code. Every possible warning, fully explained.
Part 8: THE PERLDIAG DOCUMENTATION
Everything diagnostics knows comes fromperldoc perldiag. You can
read the whole thing yourself:
It is organized alphabetically by message text. Each entry includes:$ perldoc perldiag
If you are the kind of person who reads man pages for fun (and you are reading perl.gg, so you probably are), perldiag is one of the best. It is basically a catalog of every mistake you can make in Perl, with explanations written by the people who implemented the error checking.- The message pattern with placeholders - The severity category (W = warning, F = fatal, S = severe) - The specific warning category (syntax, numeric, etc.) - A paragraph or more of explanation - Sometimes example code showing the fix
The diagnostics module just makes perldiag available at runtime, automatically, at the exact moment you need it.
Part 9: PRACTICAL DEBUGGING WORKFLOW
Here is how to use diagnostics effectively in a real debugging session. You have a script. It is misbehaving. You are not sure why.Step 1: Add diagnostics and run:
Step 2: Read the verbose output. The explanation usually tells you not just what is wrong, but what to do about it.#!/usr/bin/env perl use strict; use warnings; use diagnostics; use feature 'say'; # ... your buggy code ...
Step 3: Fix the issue. Remove diagnostics. Run again.
For one-liners, skip the file editing entirely:#!/usr/bin/env perl use strict; use warnings; use feature 'say'; # ... your fixed code ...
The$ perl -Mdiagnostics -e 'my @x; print $x[0] + 1'
-Mdiagnostics flag loads the module from the command line.
Perfect for quick experiments where you want to understand what
Perl is complaining about.
Part 10: WHY IT MATTERS
Perl has a reputation for cryptic error messages. The diagnostics pragma demolishes that reputation. Every cryptic message has a paragraph of explanation sitting right there in the distribution, waiting to be read.The information was always there. In perldiag. In the man pages. In
perldoc. The diagnostics pragma just brings it to you at the
moment you need it, without making you go look it up.
Most programmers learn about.--. |o_o | "I know what the error means. |:_/ | I just wanted Perl to say it // \ \ out loud." (| | ) /'\_ _/`\ \___)=(___/
use strict and use warnings in their
first week of Perl. Most never learn about use diagnostics. That
is a shame, because diagnostics is the one that actually teaches
you things. Strict and warnings tell you something is wrong.
Diagnostics tells you why, and what to do about it.
It is a development tool, not a production tool. It has startup overhead. It makes your error output verbose. But when you are staring at a message you do not understand, those are features, not drawbacks.
Add it when confused. Remove it when enlightened. Repeat for 30 years of Perl programming.
perl.gg