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

Monks,

I ran across a strange bug in the code of one of my modules, and I'm puzzled.

I use the $haveModule = eval { require Module; 1; } idiom fairly frequently, but it broke down for the Capture::Tiny module. Basically, it boils down to Capture::Tiny seemingly requiring module load via a BEGIN block in order to function without error. But I don't understand why, and it chafes me.

Here's code using Capture::Tiny that works:

use strict; use warnings; BEGIN { require Capture::Tiny; } my $out = Capture::Tiny::capture { print "STDOUT output to capture"; } +; print "Captured STDOUT: [" . ( defined $out ? $out : 'undef' ) ."]\n";
Output:
Captured STDOUT: [STDOUT output to capture]

By just removing the BEGIN block around require Capture::Tiny, the code now fails:

use strict; use warnings; require Capture::Tiny; my $out = Capture::Tiny::capture { print "STDOUT output to capture"; } +; print "Captured STDOUT: [" . ( defined $out ? $out : 'undef' ) ."]\n";
Output:
Can't call method "Capture::Tiny::capture" without a package or object + reference at C:\@local\@projects\@tests\test-Capture-Tiny.pl line 5.

The source for Capture::Tiny (@ http://cpan.uwinnipeg.ca/htdocs/Capture-Tiny/Capture/Tiny.pm.html) seems reasonably straight-forward, but I must be missing something.

Can someone offer an explanation for this behavior?

Thanks, in advance, for the illumination.

Replies are listed 'Best First'.
Re: Why does Capture::Tiny require loading via BEGIN to successfully function?
by ikegami (Patriarch) on Nov 10, 2010 at 00:52 UTC
    Capture::Tiny::capture { print "STDOUT output to capture"; };

    isn't a normal sub call. Something must define this syntax, and that something must define the syntax before the statement is compiled. (That something is capture's prototype.)

Re: Why does Capture::Tiny require loading via BEGIN to successfully function?
by BrowserUk (Patriarch) on Nov 10, 2010 at 00:56 UTC
    I use the $haveModule = eval { require Module; 1; } idiom fairly frequently,

    I think anonymonk has explained the problem, but I just wanted to ask you what advantages you see in detecting module absence at runtime via require, over compile-time as would happen with use?


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Why does Capture::Tiny require loading via BEGIN to successfully function?
by Anonymous Monk on Nov 10, 2010 at 00:44 UTC
    I disagree that this module's code is straightforward. I couldn't actually find the capture sub in that module at a glance -- the author redefines *_debug at run time, for instance, which hints at the possibility for obfuscation-like complexity).

    However, what appears to be happening is that it's doing something to change parsing rules at compile time. This can be done with a function prototype, like sub foo (&) {}, which is how you create functions that act like builtins such as map and grep.

    What this means is that when you require it at run time, that compile-time code isn't executed, so the magic of capture BLOCK isn't applied. Instead, you need to use capture( CODEREF ).

    Look at this:

    # perl -lwE "use Capture::Tiny (); say 'captured: ', Capture::Tiny::ca +pture { say 'foo' }" captured: foo
    # perl -lwE "require Capture::Tiny; say 'captured: ', Capture::Tiny::c +apture { say 'foo' }" foo Can't call method "Capture::Tiny::capture" without a package or object + reference at -e line 1.
    # perl -lwE "require Capture::Tiny; say 'captured: ', Capture::Tiny::c +apture(sub { say 'foo' })" captured: foo

    Upshot: without the compile-time-added syntax sugar, you need to call this sub like capture($coderef);.