Build a Lexically Scoped Perl Pragma with %^H
You will build a small user pragma that switches a class's overloaded addition between ordinary floating-point arithmetic and integer arithmetic. The switch will follow Perl's lexical scope, so no myint restores the surrounding behaviour automatically. Allow about 20 minutes for the example and its checks. The commands use Perl 5.38.2 and perl-doc 5.38.2-3.2ubuntu0.6 as installed on this machine.
The route
Jump straight to the step you need, or tick off Done means at the end.
No elevated privileges are needed. Work in a scratch directory and keep the three example files together. A user pragma is executable code loaded by Perl, so do not run source you have not inspected.
1. Check the Perl version
The installed manual describes user pragmata as modules that affect compile-time or run-time behaviour within a lexical scope. Confirm the interpreter and documentation package before relying on the examples:
$ perl -v
This is perl 5, version 38, subversion 2 ...
$ dpkg-query -W -f='${Package} ${Version}\n' perl-base perl-doc
perl-base 5.38.2-3.2ubuntu0.6
perl-doc 5.38.2-3.2ubuntu0.6
The full perl -v output includes build details that are not relevant here. The version matters because the manual page documents the syntax and implementation supplied with this Perl release.
2. Write the pragma module
Create a directory such as ~/perl-pragma-check, then save this as myint.pm inside it:
package myint;
use v5.36;
sub import {
$^H{"myint/in_effect"} = 1;
}
sub unimport {
$^H{"myint/in_effect"} = 0;
}
sub in_effect {
my $level = shift // 0;
my $hinthash = (caller($level))[10];
return $hinthash->{"myint/in_effect"};
}
1;
use myint; calls import at compile time. no myint; calls unimport at compile time. Both methods write a module-specific key into the special %^H hash. The slash in myint/in_effect is deliberate: pragma keys conventionally begin with the main package name followed by /, which avoids collisions with other modules.
The in_effect helper reads the hint hash associated with the caller's compiled code. Its optional level lets the caller skip one frame, which is needed when the helper is called from an overloaded operator.
3. Keep module loading separate from activation
Write this as MyMaths.pm in the same directory:
package MyMaths;
use v5.36;
use myint();
use overload '+' => sub {
my ($l, $r) = @_;
if (myint::in_effect(1)) {
return int($$l) + int($$r);
}
return $$l + $$r;
};
sub new {
my ($class, $value) = @_;
bless \$value, $class;
}
1;
The empty parentheses in use myint(); are an important detail. They load the module without calling its import method, so merely loading MyMaths does not activate integer arithmetic in the user's file. The overloaded operator checks the lexical hint belonging to its caller instead.
4. Exercise the lexical boundaries
Save this as check.pl. The use lib path must point to the directory containing the two modules:
use lib '/home/USER/perl-pragma-check';
use MyMaths;
my $l = MyMaths->new(1.2);
my $r = MyMaths->new(3.4);
print "A: ", $l + $r, "\n";
use myint;
print "B: ", $l + $r, "\n";
{
no myint;
print "C: ", $l + $r, "\n";
}
print "D: ", $l + $r, "\n";
no myint;
print "E: ", $l + $r, "\n";
Replace /home/USER/perl-pragma-check with the real directory. Run it without sudo:
$ perl check.pl
A: 4.6
B: 4
C: 4.6
D: 4
E: 4.6
Checkpoint: the output must alternate exactly as shown. The first addition uses the default behaviour. The later use myint affects code compiled after it, the nested no restores the outer state, and the final no disables the pragma for the rest of the file.
5. Understand the compile-time boundary
Pragmata are not ordinary run-time switches. Perl effectively expands use myint; to a compile-time require myint followed by myint->import(), and expands no myint; to the corresponding unimport call. That is why the lexical state is attached to the optree, the compiled representation of the program, rather than kept in one global variable.
Do not replace this with a package global when the feature must be lexical. A global flag would make one scope's choice visible to unrelated code and would need manual cleanup on every path. The %^H approach gives nested blocks the normal Perl scope rules.
6. Store only safe hint values
The hint state is stored in the optree and can outlive the interpreter thread that created it. The documented representation supports integers, strings and undef. References and floating-point values are stringified. If you need several values or a complex structure, serialise it, for example with pack, and decode it when reading.
Do not hide a reference inside an integer and reconstruct it later. That creates unsynchronised access to Perl data and is not thread-safe. If a setting can be absent, remember that deleting a key and storing undef are different states; use exists when that distinction matters.
7. Diagnose the common mistakes
- If Perl says it cannot locate
myint.pm, checkperl -I/path/to/dir check.plor correct theuse libpath. The file name must match the package file expected by Perl. - If integer mode is active from the start, inspect every module load.
use myint;activates the pragma;use myint();only loads it. - If a nested block does not restore its parent state, verify that
no myintis inside the block and that the operator callsin_effect(1). The level accounts for the helper call frame. - If the result is wrong after changing the module, rerun the script in a fresh Perl process. Module code is compiled when it is loaded, and a long-running process may retain an already-loaded module.
This example changes only files in your scratch directory. To undo it, stop running the script and remove that directory after checking that it contains nothing else you need. Do not delete a shared Perl library directory as a shortcut.
Done means
myint.pmdefinesimport,unimportand a caller-based state reader.MyMaths.pmloads the pragma with an empty import list.perl check.plprints4.6,4,4.6,4,4.6for A through E.- The hint key is namespaced and stores simple, thread-safe values rather than references.