Beefy Boxes and Bandwidth Generously Provided by pair Networks
Welcome to the Monastery

Comment on

( #3333=superdoc: print w/replies, xml ) Need Help??


Those of us who learned pm stuff when Perl 5 came out remember that supplying a numeric argument to the import list will assert that the module is that required version. The docs used to state that this had hidden potential, of being able to sense and react to the caller's requested version, in case updates are not exactly compatible.

Times Change

However, this whole mechanism has changed. As of Perl 5.6.1, there is support for "v-strings" of the form v5.6.1. This raises immediate confusion because comparing v5.6.1 against '5.6.0' will not work, as the values are in totally different and incompatible formats. Since the single-argument for of require and use to demand a minimum Perl version will take either way, it might come as a surprise that your own code and module versions do not.

So, when assigning a value to $VERSION, do you use the old way or the new way? If there is a choice, the user must know the proper way to formulate numbers when refering to your module's version.

The Reality

The way module version checking works has changed. With Perl 5.6.0, use and require can take a version number as the "indirect object". In 5.6.0 this had to be a numeric literal; in 5.6.1 it can be a v-string.

indirect-object VERSION syntax

Instead of including the version number as an argument to import, now you are to write:
# use VersionTest qw/1.2.3 foo bar/; # the above is no longer Kosher use VersionTest v1.2.3 qw/foo bar/; # do it this way (no comma).
The thing between the module name and the list must be a v-string or numeric literal. Anything else is rejected by the parser, unlike the normal indirect-object syntax that can be overridden with braces even if it doesn't smell like an indirect object.

For example, you cannot write:

use VersionTest '1.2.3' qw/foo bar/; #string my $needed= v2.3.4; use VersionTest $needed qw/foo bar/; #not a literal

indirect-object VERSION check semantics

If you use this syntax, Perl calls the module's VERSION method with that value as the argument. If you write such a sub, it can do anything you want, presumably do more elaborate version range checks.

If you don't write a sub VERSION, then your module inherits one from UNIVERSAL::, and that does a simple ge test.

So, if you expect to use the indirect-object form of version checking, your $VERSION variable must be a v-string or a floating point value using the three digit convension, explained below. If you use a literal such as $VERSION= '2.30.4', the old way, then the UNIVERSAL::VERSION function croaks with an error that '2.30.4' is not numeric, pointing to your caller's use statement. Not very useful, since the value it mentions isn't found anywhere where the error is reported!

Perl 5.6.0 used numbers for versions with this feature, with floating-point numbers encoding the version with three decimal digits per part. The idea was that version X.Y.Z would be represented as X+Y/1000 + Z/1000000. So 2.3.4 is the number 2.003004. That's why you had Perl version 5.005.

This is important because UNIVERSAL::VERSION will allow mixing of v-strings with floats by converting the v-string to a float using this convention.

So, if the module defines $VERSION=1.2 and you use Module v1.2 it will compare 1.2 against 1.002, which is not what you meant. If you reverse that and declare $VERSION=1.2.1 (v is implied if there is more than one dot in a literal) and then use Module 1.1, you will learn that 1.2.1 is less than 1.1, since the former is converted to 1.002001. The module will refuse to load since it is not a high enough version!

The lesson here is that if using numbers, not v-strings, always remember the 3-digit convension. It is improper to make a module version 1.2 (a floating point literal), unless you really did mean v1.200. Some modules do this wrong, and will cause this kind of surprise.

version import number

So what of the "old way", of listing a number in the import list? E.g.
use VersionTest qw/1.2.3 foo bar/;
If you are using the normal Exporter::import function, this will call your module's require_version method. If you don't define one, you inherit one from Exporter. That implementation is surprising:

It will do a numeric comparison between your request and $VERSION. What's '1.2.3' going to do with a numeric comparison? It will parse the floating-point value 1.2 and ignore the rest. Basically, it only looks at the first two parts, and silently ignores everything else! This is different from the indirect-object version check, which gives a "not numeric" error instead.

Worse yet, it will see a v-string as a numeric zero. So, if you have a v-string in $VERSION and you use the import-number form, your version check will always fail but no testing had been done!

Hmm, an old program uses this technique, and the old module had $VERSION='1.2'. The new module has $VERSION=v2.0.1, and you run your old program. It will report that you asked for 1.2 but the file contained version 0.0, and it won't load.

Where are we now?

Using the supplied functions as inherited from Exporter and UNIVERSAL, the old and new forms of module version checking are fundimentally incompatible.

If you change from old to new when updating a module, and intend it to be backward compatible, it won't be if a user checks the version! New code, though, is expected to use the new form, isn't it?

With two incompatible forms, the user would have to know which to use, on a per-module basis. It's easy to figure out because doing it the wrong way results in a run-time error during the version checking code.

An easy solution would be to supply a require_version method that also uses the v-string in $VERSION but expects a old-style string as an argument. Do this with something similar to $requested= eval "v$requested"; VERSION($requested);

Survey defines $VERSION="1.72". Being a string, the indirect-object syntax will be rejected. For the import-list syntax, it will be taken as a floating point number, and the user needs to understand that it's a single float, not a dotted pair, when comparing them. The quotes seem like a mistake knowing that the importer compares numbers not dotted strings, but look at what it does: it prevents the new syntax from being used. That can be a way to keep backward compatibility with old modules. says $VERSION = 1.02, which does not follow the 3-digit convension. This will give improper comparisons when using the indirect-object syntax.


In reply to Everything you wanted to know about Module Version Numbers and Checking by John M. Dlugosz

Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":

  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.
  • Log In?

    What's my password?
    Create A New User
    [stevieb]: choroba just gleaned your post about Module::Starter. I use it too, pretty much for every dist I write
    [LanX]: I remeber M::S (it was dialog driven?) to be buggy
    [stevieb]: As far as Dist::Zilla goes, I don't like installing that other than on systems my test platorm runs on. I find it too heavy. I prefer being able to glean a Makefile.PL
    [LanX]: what's frustrating me is that a distribution has lots of dupplicated info
    [stevieb]: LanX I don't know if it's dialog driven; I just use it in the simplest of terms (just run module-starter at the CLI, and the very last couple of lines are how I use it.
    [stevieb]: which dist are you speaking of regarding dups, LanX?
    [LanX]: readme version number and so on ...

    How do I use this? | Other CB clients
    Other Users?
    Others meditating upon the Monastery: (5)
    As of 2017-08-18 20:52 GMT
    Find Nodes?
      Voting Booth?
      Who is your favorite scientist and why?

      Results (310 votes). Check out past polls.