bishop729 has asked for the wisdom of the Perl Monks concerning the following question:

I have written a script that uses the isnum, isint, and isfloat functions from Scalar::Util::Numeric. I now want to share this script with others, so I have created a Git repo containing this and other scripts. However, Scalar::Util::Numeric isn't part of the Perl core, and I don't want to require users to install anything - just git clone and it should work.

Option 1: I put the module source code in my repo itself. I tried putting the Numeric.pm file by itself first, and then the full Scalar/ directory, and then tried to execute its contents using do. Didn't work.
$ find Scalar/ Scalar/ Scalar/Util Scalar/Util/Numeric.pm $ perl -e 'do "./Scalar/Util/Numeric.pm" or die;' Can't locate loadable object for module Scalar::Util::Numeric in @INC +(@INC contains: /etc/perl /usr/local/lib/x86_64-linux-gnu/perl/5.26.1 + /usr/local/share/perl/5.26.1 /usr/lib/x86_64-linux-gnu/perl5/5.26 /u +sr/share/perl5 /usr/lib/x86_64-linux-gnu/perl/5.26 /usr/share/perl/5. +26 /usr/local/lib/site_perl /usr/lib/x86_64-linux-gnu/perl-base) at - +e line 1. ...propagated at -e line 1.
Option 2: implement isnum, isint, and isfloat myself.

Option 2 seems to be the easy way out; I've seen some solutions online that use regex. This can work, and I will be calling it only a few times, so performance isn't a concern at all.
My objection is that I imagine I will want to use other non-core modules in future scripts, and I would like to be able to simply copy their sources into my repo. So my question is: is Option 1 possible, and if so, how should I do it? If not, should I implement Option 2, or are their other, better solutions?

Replies are listed 'Best First'.
Re: Portable way of checking if a scalar is a number / integer / float
by haukex (Archbishop) on May 13, 2023 at 20:55 UTC
    Can't locate loadable object for module Scalar::Util::Numeric

    Scalar::Util::Numeric is an XS module, which means it must always be compiled against whatever version of Perl is on the user's machine. So your requirement

    I don't want to require users to install anything - just git clone and it should work.

    is the first thing I would question. Yes, even you can use CPAN, and so can your users.

    Yes, there is Scalar::Util::Numeric::PP and you could use this for your Option 1. But then the problem becomes maintenance: You'd have to always make sure to update your module if there are changes or fixes to the upstream module - I guess one could call this a variation of DRY. Similar issue with your Option 2: Sure, you can reinvent the wheel, but then remember to write enough tests to cover all edge and corner cases etc.

    Though this is just a variation of the above, another option might be to take the regexes produced by Regexp::Common::number and integrate those into your code. This has the same problem as above, but at least it's a bit less code to copy over...

    See also the FAQ How do I determine whether a scalar is a number/whole/integer/float?

    By the way, in regards to the title of the question, there is an important distinction sometimes missed by people that the question should be "does this string look like a integer/float", and not "does this Perl scalar hold a number or a string", because oftentimes Perl scalars can be both a number and a string depending on what context they're used in, and therfore, in Perl, this is not the right question to ask. (The only exception might be when writing a data serialization tool, and even those often run into trouble in making that distinction.)

      Thank you for pointing me to Scalar::Util::Numeric::PP. I installed it using CPAN and copied over the source into my repo and it works.

      use lib dirname(__FILE__); do "Scalar/Util/Numeric/PP.pm" or die;

      To your point that:

      Yes, even you can use CPAN, and so can your users.

      For now my quick-and-dirty solution is sufficient. If I find myself adding more dependencies I'll create an install script that installs cpan and the necessary libraries.

      Thanks for the tip about scalars. It had slipped my mind that strings and numbers have distinct internal representation in Perl

      Edit: solution was incorrect; I fixed it.
Re: Portable way of checking if a scalar is a number / integer / float
by kcott (Archbishop) on May 13, 2023 at 22:02 UTC

    G'day bishop729,

    Welcome to the Monastery.

    If a module is installed, you can simply put

    use Some::Module;

    in your code (see use for some variations on that syntax).

    If you don't want your users to have to install anything, you can put the module source in your repo. Something like this:

    bin/ script.pl lib/ Some/ Module.pm

    You can use FindBin so your code can find the module.

    Be aware that you'll need to keep copies of Some::Module in all repos; all fixes, enhancements, extensions, etc. will need to be kept in sync across all repos. This tends towards a maintenance nightmare and I certainly don't recommend it; however, if it's a requirement that users don't install modules, this is probably what you'll need to do.

    I don't know how familiar you are with writing modules. I'd recommend using Exporter to avoid namespace pollution. Your code might look something like this:

    package Some::Module; use strict; use warnings; our $VERSION = '0.001'; use Exporter 'import'; our @EXPORT_OK = qw{isnum isint isfloat}; our %EXPORT_TAGS = (all => [@EXPORT_OK]); sub isnum { ... } sub isint { ... } sub isfloat { ... } 1;

    And don't forget to add your POD after that.

    See also:

    — Ken

      Thanks for the welcome. I'm pleased at how quickly I got a solution to my problem. For now I am copying the source Scalar::Util::Numeric:PP into my repo. I realize it's not sustainable as my dependencies increase in number and complexity, and I will revisit your suggestions when the time comes.
Re: Portable way of checking if a scalar is a number / integer / float
by ikegami (Patriarch) on May 14, 2023 at 17:53 UTC

    I tried putting the Numeric.pm file by itself first, and then the full Scalar/ directory, and then tried to execute its contents using do. Didn't work.

    You didn't include the DLL. These are the files installed by the distro:

    • Scalar/Util/Numeric.pm (in the site lib dir)
    • auto/Scalar/Util/Numeric/Numeric.so (or .dll, in the site lib arch dir)
    • Scalar::Util::Numeric.3 (in the man3 dir)

    You can find these files in the blib dir created during the installation process.


    I don't want to require users to install anything

    As a module with a compiled component, this would only be possible by providing binaries that are suitably compiled for your users. Keep in mind that modules compiled with one version of Perl are only compatible with that version of Perl. This would a large headache.