Front page | perl.perl5.porters |
Postings from November 2022
Re: undeclared named subs/subrefs under strict
Thread Previous
|
Thread Next
From:
demerphq
Date:
November 22, 2022 09:34
Subject:
Re: undeclared named subs/subrefs under strict
Message ID:
CANgJU+UwypfqVSQTrzWN6LzNdcv6iq47R_-sf7BnA4Y+iG4peQ@mail.gmail.com
On Mon, 21 Nov 2022 at 19:33, breno <oainikusama@gmail.com> wrote:
>
> Dear porters,
>
> when sub bar is not declared at all, 'sub foo { bar() }' compiles under strict/warnings without any issues. Same goes for 'my $x = \&bar'.
>
> Is this by design? I understand why glob refs would not warn or croak but Is there a reason why explicitly named subroutines that don't exist at compile time would not yield even a warning until they are actually called?
[Resent on-list, with minor extensions and corrections from the
off-list version]
Yes it is by design. There are many reasons why one would want to late
bind function calls. A simple reason is that it means perl doesnt
*require* forward declarations, which means that a bunch of double
entry is avoided, and developers can order the subs in their code
however you choose.
sub foo_a { foo_b($_[0]-1) if $_[0] }
sub foo_b { foo_a($_[0]-1) if $_[0] }
But you might argue "well at the end of a compilation unit you could
go back and validate all the sub calls", and indeed there are modules
that will do this for you.
But there are many other reasons as well. Remember perl is a
hyper-dynamic language. This means the concept of "binding" is bit
more subtle than in other languages necessarily. But it allows us to
provide the ability to relatively seamlessly do "late loading" and
"plug in architectures". An extreme example is recursive closures via
a localized sub:
sub do_something_recursive_with_a_closure {
my ($self, @args)= @_;
local *recursive= sub { shift @args; recursive() if @args };
recursive();
}
In this case the recursive() sub doesn't exist except at run time. Or
even if it did, it wouldnt be the recursive() sub being called by this
code. Early binding would make that troublesome or impossible.
Another example is late loading of an optional capability
package Thing;
sub whatever {
require Thing::Heavy(); # injects heavy_thing() into our namespace
heavy_thing();
}
Another example is AUTOLOAD as LeoNerd mentioned.
There are many good reasons to treat functions like methods, and
generally perl doesn't treat them differently. Methods are always late
bound, and subs are late bound if a definition can't be found at
compile time. A great deal of code depends on it.
> On a somewhat related note, how would one detect if a named subroutine/subref was declared or not (it's ok if it was declared but is empty) at runtime? Right now I'm doing:
One way is to treat the package as a class, and ask if it "can" do
something. Assuming its not also a class and does not have an established
@ISA then it should be fine:
if (UNIVERSAL::can("Package", "name")) { ... }
I have to wonder tho, *why* do you want to do this? Usually the
correct way to see if a function is defined /is to call it/. :-) Why
do you want to know in advance? It feels like you are asking an XY
question, eg, "how do i find out if a subroutine exists", instead of
"I want to do X, I think Y (finding out if a subroutine exists) should
work, but its not working out as I expect, how do I do X?". There
are patterns where "does this class expose this method" is more
tractible than others. If we knew X we might be able to guide you
better (although this is really a perlmonks question not a p5p
question). FWIW, my general experience of programming perl is its
better to reorder your thinking around not ever having to /ask/ if
something is loaded (let perl do that), but instead to approach the
problems where that might an issue a different way. For instance
simply putting a version number in a package allows one to ask an
alternate but equivalent question
if ($Some::Package::VERSION >= 5) { # assume they have the frobnitz() function
Some::Package->import("frobnitz");
}
sub use_frobnitz {
frobnitz(...);
}
Yves
ps: sorry for the weird wrapping here
--
perl -Mre=debug -e "/just|another|perl|hacker/"
Thread Previous
|
Thread Next