Perl has a notion of an undefined function. A function that's declared but not defined.
sub foo;
foo(); # Undefined subroutine error
bar(); # Undefined subroutine error
This function foo exists now in the symbol table and it can be used to resolve a method call. But why does this "feature" even exist? In C, it's because functions are type-checked and sometimes you want to have a call before you define (such as to resolve a circular dependency). But Perl has no such feature and all function symbols are resolved in runtime not compile time.
If it's for prototypes, then why should an undefined function exist if it does not have a prototype?
If it's not for prototypes, why does it exist at all?
And why is that undefined subroutines are used in method resolution? Why not ignore them entirely -- you can't call them and they're internal implementation details as far as I can see (at best)? That is why if function isn't defined can't we continue method resolution as if it did not exist (it seems like it would be less confusing).
CodePudding user response:
It is about prototypes, and the declaration (or not) of a function does have an effect at compile time. Consider
-
print foo 42;In isolation, this is equivalent to
print('foo' 42);—foois a "bareword". If you havestrict 'subs'enabled, it will instead give you a compilation error saying that barewords are forbidden. -
sub foo; print foo 42;This is equivalent to
print(foo(42)); the compiler knows thatfoois a sub and it has no prototype, so it consumes everything after it in "list op" fashion, and what follows it is the term42. -
sub foo(); print foo 42;This is equivalent to
print(foo() 42); the compiler knows thatfoohas a prototype and that it takes no arguments, so none will be looked for, andfooand42will be the operands of theoperator. -
sub foo($); print foo 42;Like case 2 this is equivalent to
print(foo(42)). I think there's probably a test I could have used to distinguish them.
Point being, whether a sub is known or not does have effects at compile-time, and Perl gives you the option to declare that fact before you define the body of the sub, rarely needed as it may be.
As for why it has an impact on method resolution order — most likely it's a side-effect, but it's not wrong. Forward-declaring a sub is supposed to mean that you intend to intend to provide the definition before compilation is done. If you don't, then you will get a runtime error when you try to call it. It seems fair enough to me that if such a declaration is in a package in the MRO, then that means "there should be a method here, but I forgot", and you get an error when the MRO reaches that package.
CodePudding user response:
It's the same as with other types, I'd say; just the artifact of how it is parsed.
my $hr = { a => 1 }; # $hr name introduced at compile time, assigned or not
So the same goes with
sub name { ... }; # "name" "declared" at compile time
and saying just sub name; is about the same as saying my $hr; -- and then having a symbol with no definition attached to it.
I don't know how the parser works but I'd guess that it has to take sub name first and "bind" the definition later, so by happenstance we can then also say just sub name; and have that name.
I mean to say that this is the "reason", per the question "But why does this "feature" even exist?"
But then once it is known to the compiler ahead of time that there is a sub with that name then there may be various uses of that fact.
CodePudding user response:
I can think of these reasons:
Allows placing the definition of a sub after a call to it if they have a prototype or attributes.
Notable specifics:
- Allows mutually recursive subs to exist if they have a prototype or attributes.
Allows AUTOLOADed and similar subs to be declared.
Notable specifics:
- Allows ones to check if AUTOLOADed subs exist.
- Allows AUTOLOADed and similar subs to be exported.
Can be used as an abstract method.
CodePudding user response:
This has saved me a lot of debugging:
sub AUTOLOAD
{
my (undef,$filename,$lineno) = caller;
my ($fn) = basename($filename);
logmsg('E',"Undefined reference ($fn/$lineno): ref=$AUTOLOAD, refer=$ENV{HTTP_REFERER}");
}
