note
tye
<p>
[ Aside: Yes, I discussed that briefly in my node. That is why I said
that I didn't get your point (in the context of the discussion). Also, I
assumed that you must have been getting at something else since your reply
included code with exactly the same problem (as did your own code earlier
in the thread). My node explicitly mentions not being able to use
imported subs via barewords, so using that specific case as an example of
where I was wrong doesn't make much sense. :) And you are wrong about me
not having been burned. I've had quite a bit of fun battling conditional
module use (and conditional constant definition) with compile-time magic.
Now back to the technical discussion. ]
</p><p>
You can't use a module's compile-time magic (like prototypes) when you
conditionally use the module, unless you conditionally compile all of the
code that uses that magic. Like I said, there are ways to do this, but
none of them are good.
</p><p>
There is only one way that I've actually used that survived very long. You
can, at compile time, conditionally compile a sub that makes use of the
compile-time magic. For example:
</p><code>
BEGIN {
my $code= '...some work-around for not having GD...';
if( eval { require GD } ) {
$code= '...code that uses GD...';
}
eval "sub mySub { $code }; 1 "
or die "$@";
}
</code><p>
But doing this in order to use bareword constants is rarely worth it (though
it does give you compile-time checking for half of your typos).
</p><p>
So you are often better to just not use the compile-time magic. For example,
consider this pretend module that makes other uses of prototypes:
</p><code>
package Pretend;
use base qw(Exporter);
our @EXPORT_OK= qw( sorter hasher );
sub sorter(&@);
sub hahser(\%);
#[...]
</code><p>
You might try to work-around a possible lack of this module via:
</p><code>
my $got_Pretend;
BEGIN {
if( $got_Pretend= eval { require Pretend } ) {
Pretend->import( qw( sorter hasher ) );
} else {
require WorsePretendWorkAround;
WorsePretendWorkAround->import(
qw( comparer worse_hasher ) );
}
}
#[...]
if( $got_Pretend ) {
@list= sorter { $_[0] <=> $_[1] } hasher %hash;
} else {
@list= sort \&comparer worse_hasher(\%hash);
}
</code><p>
And if you never bother to test your code when the Pretend module isn't
installed, then you might think you've got a pretty good fix. However,
this code will just fail to compile if there is no Pretend module (well,
it certainly will if you <code>use strict</code>, but you always do
that). If that were an okay failure mode, then you might as well just
write <code>use Pretend</code>!
</p><p>
But you can work-around this possible lack of compile-time magic by
just avoiding your use of the magic:
</p><code>
if( $got_Pretend ) {
@list= sorter( sub { $_[0] <=> $_[1] }, &hasher(\%hash) );
} else {
@list= sort \&comparer worse_hasher(\%hash);
}
</code><p>
Unfortunately, this solution won't tell you when the interface to
<code>hasher</code> changes. But there are enough problems with
using current Perl prototypes for this kind of checking that you
probably won't ever run into that.
</p><p>
Another solution that sounds very nice but that I've never actually
used, is to put the module-dependant code into a separate file and
require that file (at compile time) if the module is present.
</p><p>
Anyway, the one simple thing that everyone should remember about this
problem is:
<ul><li><b>
If you conditionally use a module, then you need to test your code both
with and without the module!
</b></ul><p>
(And if you conditionally use N modules, then you need to test your code
in the 2^N cases of module availability!)
</p>
-
<a href="/index.pl?node=tye&lastnode_id=1072">tye</a>
(but my friends call me "Tye")
27443
27510