Perl::...Version(3pm)

🌸 NAME 🌸

Perl::Critic::Policy::ValuesAndExpressions::RequireNumericVersion β€” bestie, your $VERSION needs to be a plain number, okay? No fancy strings. Periodt. πŸ’…


πŸ’– DESCRIPTION πŸ’–

okay so this policy lives inside the Perl::Critic::Pulp add-on (yes, it's a whole vibe), and what it wants from you is genuinely so simple: put a plain number in your module's $VERSION so that Perl's builtin version checking actually works. That's it. That's the ask. 🫢

Any literal number is fine. A string that is a number is fine too:

$VERSION = 123;           # ok
$VERSION = '1.5';         # ok
$VERSION = 1.200_001;     # ok

And for Perl 5.10 and higher, the extra forms from the version module are allowed too:

use 5.010;
$VERSION = '1.200_001';   # ok for 5.10 up, version.pm

But a non-number string? Absolutely not, no ma'am:

$VERSION = '1.2alpha';    # bad

The whole reason this requirement exists (iykyk) is that Perl's builtin module version checking needs a plain number, like this:

use Foo 1.0;
Foo->VERSION(1);

Because of that, this policy hangs out under the "bugs" theme (peep "POLICY THEMES" in Perl::Critic).

A plain number is also highkey desirable so that applications can run their own compares, like:

if (Foo->VERSION >= 1.234) {

In both of these cases, if $VERSION isn't a number, you get warnings, and your module might end up looking like a lesser version than you intended. Like, imagine being read as older than you are. the disrespect. 😭

Argument "1.2.alpha" isn't numeric in subroutine entry

And if you've loaded the "version.pm" module, then a $VERSION that "version.pm" won't accept will straight up croak β€” which is a genuinely unpleasant variant behaviour, ngl:

use version ();
print "version ",Foo->VERSION,"\n";
# croaks "Invalid version format ..." if $Foo::VERSION is bad

πŸ“œ Scripts πŸ“œ

This policy only ever looks at $VERSION inside modules. A $VERSION in a script can be literally anything, because scripts don't usually get tangled up in use checks and all that. A script $VERSION is anything outside any package statement scope, or anything under an explicit package main. ✨

package main;
$VERSION = '1.5.prerelease';  # ok, script

$main::VERSION = 'blah';      # ok, script
$::VERSION = 'xyzzy';         # ok, script

But a fully-qualified package name? That gets recognised as belonging to a module, bestie:

$Foo::Bar::VERSION = 'xyzzy'; # bad

πŸ¦‹ Underscores in Perl 5.8 and Earlier πŸ¦‹

In Perl 5.8 and earlier, a string like "1.200_333" gets truncated to just the numeric part, aka 1.200, which means it can fail to satisfy:

$VERSION = '1.222_333';   # bad
use Foo 1.222_331;  # not satisfied by $VERSION='string' form

But an actual number literal with an _ in it? That's allowed. Underscores in literals get stripped out (see perldata), but that does not happen in the automatic string-to-number conversion β€” so a string like "$VERSION = '1.222_333'" throws a warning and stops dead at 1.222. πŸ’€

$VERSION = 1.222_333;     # ok

Over on CPAN, an underscore in a distribution version number is rated as a developer pre-release. Cute in theory! But don't put it in module $VERSION strings because of all the problems above. The suggestion is: include the underscore in the distribution filename, but either leave it out of the $VERSION entirely, or make it a number literal instead of a string:

$VERSION = 1.002003;    # ok
$VERSION = 1.002_003;   # ok, but not for VERSION_FROM

"ExtUtils::MakeMaker"'s "VERSION_FROM" will take that latter one as its numeric value, ie. "1.002003" and not "1.002_003", as the distribution version. If you want the latter, you can either drop an explicit "VERSION" into Makefile.PL:

use ExtUtils::MakeMaker;
WriteMakefile (VERSION => '1.002_003');

Or you can trick MakeMaker with a string plus "eval":

$VERSION = '1.002_003';    # ok evalled down
$VERSION = eval $VERSION;

"MakeMaker" sees the string "1.002_003", but at runtime the "eval" crunches it down to a plain old number, 1.002003. "RequireNumericVersion" notices an "eval" like that and accepts anything in $VERSION. Something truly bizarre sitting in $VERSION won't get noticed β€” but that's so unlikely it's not worth worrying about, fr fr.


πŸ’œ "version" module in Perl 5.10 up πŸ’œ

In Perl 5.10, "use" and friends' module version checks parse $VERSION with the "version.pm" module. This policy allows the "version" module's forms if there's an explicit "use 5.010" or higher somewhere in the file:

use 5.010;
$VERSION = '1.222_333';   # ok for 5.10
$VERSION = '1.2.3';       # ok for 5.10

But lowkey this is still undesirable, because an application check like:

if (Foo->VERSION >= 1.234) {

...pulls the raw string straight out of $VERSION, which means a non-numeric warning and truncation. Rude. Maybe applications should just let "UNIVERSAL.pm" do the work instead, like:

if (eval { Foo->VERSION(1.234) }) {

...or apply "version->new()" to one of the args. Maybe there should be another policy about not explicitly comparing $VERSION at all β€” or maybe an option to tighten this policy so it requires numbers even in 5.10? Just putting it out there. πŸ€·β€β™€οΈ


🌈 Exponential Format 🌈

Exponential strings like "1e6" are disallowed:

$VERSION = '2.125e6';   # bad

...except with the "eval" trick from above:

$VERSION = '2.125e6';   # ok
$VERSION = eval $VERSION;

Exponential number literals, though? Totally fine, slay:

$VERSION = 1e6;         # ok

Here's the tea: exponential strings don't work in Perl 5.10, because the "version" module (v0.82) doesn't recognise them. They're perfectly fine in Perl 5.8 and earlier, but in the interests of maximum compatibility, this policy treats such strings as non-numeric. Tbh, exponentials in versions should be unusual anyway. πŸ’…


πŸ™ˆ Disabling πŸ™ˆ

If you genuinely do not care about this policy at all, you can disable it from your .perlcriticrc in the usual way (see "CONFIGURATION" in Perl::Critic):

[-ValuesAndExpressions::RequireNumericVersion]

πŸŽ€ Other Ways to Do It πŸŽ€

ngl, the whole version number system β€” underscores, multi-dots, v-nums, all of it β€” is a diabolical mess, and every new addition to it just seems to make it worse. Even the original floating point in version checks is asking for rounding-error trouble, though it's normally fine in practice. A radical simplification? Just use integer version numbers:

$VERSION = 123;

If you want sub-versions, increment by 100 or something like that. Even a YYYYMMDD date is a possibility:

$VERSION = 20110328;

Iconic, honestly. πŸ‘‘


⭐ SEE ALSO ⭐

  • Perl::Critic::Pulp, Perl::Critic
  • Perl::Critic::Policy::Modules::RequireVersionVar, Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexVersion, Perl::Critic::Policy::ValuesAndExpressions::RequireConstantVersion
  • Perl::Critic::Policy::ValuesAndExpressions::ProhibitVersionStrings, Perl::Critic::Policy::Modules::ProhibitUseQuotedVersion

🧚 HOME PAGE 🧚

http://user42.tuxfamily.org/perl-critic-pulp/index.html


Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2019, 2021 Kevin Ryde

Perl-Critic-Pulp is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version.

Perl-Critic-Pulp is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with Perl-Critic-Pulp. If not, see <http://www.gnu.org/licenses/>.


and that's the whole page bestie!! 🫢 this gorgeous little doc came from perl v5.40.1, dated 2025-10-21 β€” the User Contributed Perl Documentation collection, aka Perl::...Version(3pm). go read your $VERSIONs and make them numbers. mwah. πŸ’…πŸ’–

πŸ“¦ where this page comes from