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

Monks, why does the following code
#! /usr/local/bin/perl -l package P; sub new { use Time::HiRes 'time'; return bless { t => time }, 'P' } package main; print P->new->{t};

produce 1145538655.40264, and yet the following code (note the string eval)

#! /usr/local/bin/perl -l package P; sub new { eval "use Time::HiRes 'time'"; die $@ if $@; return bless { t => time }, 'P' } package main; print P->new->{t};

produce 1145538655 (that is, no fractional accuracy)? %INC does contain a Time::HiRes afterwards...

I have tried requireing and importing but that doesn't work either, and I'm at a bit of a loss. Basically, I'm just trying to attempt to load Time::HiRes on demand, and if it available, then I get improved time accuracy, and if it doesn't, well there's no harm done.

But as it stands, I never get improved accuracy, and I don't know what I'm missing.

Thanks for the clues (and I'll punch anyone who mumbles use strict :)

• another intruder with the mooring in the heart of the Perl

Replies are listed 'Best First'.
Re: Importing Time::HiRes::time at run-time (and failing)
by salva (Canon) on Apr 20, 2006 at 13:44 UTC
    because time is not a function but a built-in operator.

    When perl compiles your function, it checks if time has been redefined in the current package and if so, it emits opcodes to call a subroutine called "time", otherwise, it emits the time opcode (pp_time). As you are defining time after compilation, the time call is being compiled as the built-in and so the function from Time::HiRes never gets called.

    You can work around it as follows:

    sub new { require Time::HiRes; return bless { t => Time::HiRes::time() }, 'P' }

      Aha! Thanks for the cogent explanation. Along with some advice from bart on the chatterbox, I came up with the following proof-of-concept solution.

      #! /usr/local/bin/perl -l package P; sub new { my $class = shift; my $mod = shift; eval "use $mod ()"; my $self = {}; if ($@) { $self->{_time_func} = sub { time }; } else { $self->{_time_func} = sub { Time::HiRes::time() }; } return bless $self, 'P' } package main; my $p = P->new( shift || 'Time::HiRes' ); print $p->{_time_func}->(); sleep 2; print $p->{_time_func}->();

      You can run this, and if T::HR is installed, you get fractional times. Pass it junk on the command line, and then T::HR is not loaded (which simulates what happens on an installation that lacks it, or fails to load) and integer seconds come out.

      In the real world, the _time_func business will be hidden from the user, they just get back fractional seconds, or not.

      • another intruder with the mooring in the heart of the Perl

        you can simplify it a bit replacing the eval "..." by a eval {...} and returning a reference to Time::HiRes::time instead of it wrapped inside an additional sub:
        package P; sub new { my $class = shift; my $self = {}; $self->{_time_func} = eval { require Time::HiRes } ? \&Time::HiRes::time : sub { time() }; bless $self, $class }
Re: Importing Time::HiRes::time at run-time (and failing)
by blazar (Canon) on Apr 20, 2006 at 13:37 UTC

    Taking into account e.g. perldoc -q require, the right thing to do should be to require, but if it "doesn't work either", then you should define "doesn't work", which you fail to, it seems to me.

    Also, Benchmark.pm can conditionally use Time::HiRes if available, so you may try looking into it to see how it accomplishes to do so...

    Update: well, I just gave a peek into the source of Benchmark.pm myself and I can see this:

    # --- ':hireswallclock' special handling my $hirestime; sub mytime () { time } init(); sub BEGIN { if (eval 'require Time::HiRes') { import Time::HiRes qw(time); $hirestime = \&Time::HiRes::time; } }
Re: Importing Time::HiRes::time at run-time (and failing)
by Hue-Bond (Priest) on Apr 20, 2006 at 13:53 UTC

    I think it has something to do with scoping rules:

    $ perl -w -Mstrict use Storable 'freeze'; my $c = freeze \4; print "$c\n"; __END__ 12�4 $ perl -w -Mstrict eval "use Storable 'freeze'"; my $c = freeze \4; Backslash found where operator expected at - line 2, near "freeze \" (Do you need to predeclare freeze?) print "$c\n"; __END__ syntax error at - line 2, near "freeze \" Execution of - aborted due to compilation errors.

    --
    David Serrano