Bod has asked for the wisdom of the Perl Monks concerning the following question:
I'm looking for the 'right' way to call a function from within the module when that module is usually blessed. I've been putting an empty string as the first parameter passed to the function but I feel sure there is a nicer way to do this.
package Some::Module;
use strict;
sub new {
return bless {};
}
sub do_something {
my ($self, $param) = @_;
# ...do something...
return $param;
}
sub do_more {
my $self = shift;
return do_something('example');
}
If we create an instance of this then call the function
my $instance = Some::Module->new;
$instance->do_something('testing');
a reference to $instance is passed to $self in the function (I think) and 'testing' is passed to $param.
But when the same function is called from within the module by do_more();, there isn't a reference to anything to be passed to do_something as the first argument so the argument list is out. Do we just add a dummy argument to the call within do_more like this
sub do_more {
my $self = shift;
return do_something('', 'example');
}
or is there a more elegant solution?
Re: Calling module function from within a blessed module
by 1nickt (Canon) on Jan 02, 2021 at 15:39 UTC
|
sub do_more {
my $self = shift;
return $self->do_something('example');
}
Hope this helps!
The way forward always starts with a minimal test.
| [reply] [d/l] |
|
| [reply] |
Re: Calling module function from within a blessed module
by haukex (Archbishop) on Jan 02, 2021 at 15:40 UTC
|
Since both do_something and do_more appear to be methods, since they both take $self as their first argument, then you probably should always call them as such.
sub do_more {
my $self = shift;
return $self->do_something('example');
}
| [reply] [d/l] [select] |
|
| [reply] |
Re: Calling module function from within a blessed module
by stevieb (Canon) on Jan 02, 2021 at 17:01 UTC
|
Another way if you want to keep it a function, which will prevent it from being accessible outside of your class, or if you want to export it as a function if your module also provides non-OO functionality:
package Foo;
use warnings;
use strict;
use Exporter qw(import);
our @EXPORT_OK = qw(function_or_method);
our %EXPORT_TAGS = (all => [@EXPORT_OK]);
sub new {
return bless {}, shift;
}
sub function_or_method {
# Throw away the first param if:
# A) it's defined, and
# B) it's either the class, or an object of the class
if (defined $_[0]) {
shift if $_[0] eq __PACKAGE__ || ref $_[0] eq __PACKAGE__;
}
# Now we grab the remaining params (if expecting any)
my ($call_num, $call_type) = @_;
print "Num: $call_num called as $call_type\n";
}
The calling script:
use warnings;
use strict;
use lib '.';
use Foo qw(:all);
# Call as function
function_or_method('1', 'function');
# Call as method
my $obj = Foo->new;
$obj->function_or_method('2', 'method');
Note that if you shift off the object to use the symbol as a function only, you obviously can't retrieve any of the object's attributes or call its other methods unless 1) you have stored a saved object globally (ie. at the class scope) or 2) you pass in an object through explicit parameter passing.
Sometimes I provide my users the option of an OO interface or functional interface for an entire class. Again, if used in non-OO context, you can't call things on $self within the module. My entire WiringPi::API (source) is one such distribution that is 100% OO capable, and 100% functional capable. | [reply] [d/l] [select] |
|
This (nice post++ btw) combined with butthole-politics brings me to think about verification of an object. Is that subject an object of our class?
Hmm... is this really an Irishman (or insert $country instead) and not some offspring of Letonian, given that there was a wave of immigration from there to Ireland? and he claims to be irish because offsprings of his ancestors moved to Ireland?
Depending on that, what methods are available? etc. Maybe I'll be coding something like Acme::Object::Racist or such :-D
perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
| [reply] |
Re: Calling module function from within a blessed module
by shmem (Chancellor) on Jan 02, 2021 at 15:53 UTC
|
This looks like you want do_soemthing() to act both as a method and a function.
If so, you need to check the first argument passed in. Inside that sub you would decide what to do either way.
sub do_something {
my ($self, $param);
$self = shift if ref $_[0] eq __PACKAGE__; # first argument is obj
+ect
$param = shift;
# ...do something...
if ($self) { # method
...
} else { # function
...
}
return $param;
}
If it is a mere function internal to the class, it is good practice to prepend the function name with a dash. That's an informal convention to mark functions private to the module or class.
If it is strictly a method, then the answers of my fellow monks apply: $self->method(@args)
perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
| [reply] [d/l] [select] |
|
use warnings;
use strict;
{ package Foo;
use Data::Dump;
use Scalar::Util qw/blessed/;
sub new { return bless {}, shift }
sub foo {
my $self = shift if ref $_[0] eq __PACKAGE__;
dd 'foo', $self, \@_;
}
sub bar {
my $self = shift if defined blessed($_[0])
&& $_[0]->isa(__PACKAGE__);
dd 'bar', $self, \@_;
}
}
{ package Bar;
use parent -norequire, 'Foo';
}
Foo::foo('a'); # ("foo", undef, ["a"])
Foo::bar('b'); # ("bar", undef, ["b"])
my $f = Foo->new();
$f->foo('c'); # ("foo", bless({}, "Foo"), ["c"])
$f->bar('d'); # ("bar", bless({}, "Foo"), ["d"])
my $g = Bar->new();
$g->foo('e'); # ("foo", undef, [bless({}, "Bar"), "e"]) - oops!
$g->bar('f'); # ("bar", bless({}, "Bar"), ["f"])
Update: Added arguments to calls to make @_ more clear. Update 2: I guess in case the package name is "0", one could be doing defined blessed instead of just plain blessed, so I've updated the above, but since that's pretty unlikely, I'd say it's optional. | [reply] [d/l] [select] |
|
Note that this unfortunately breaks for subclasses.
This is true. Thanks for pointing that out, so I don't have to do that ;)
The OP was really about calling syntax, and I sneaked in dual subs (method/function) and the __PACKAGE__ keyword, just to have that mentioned. Now you bring on subclassing, which is great - and a big field to tackle. Yes, the check for __PACKAGE__ is suitable for classes which aren't meant to be subclassed, won't be, or "I don't care about" modules, and more so: which strictly forbid subclassing.
Your post is right, mine wasn't wrong, and from here we can go on exploring the whole perl OO fractal landscape... ;)
perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
| [reply] |
|
| [reply] |
|
|
|
|
|
Re: Calling module function from within a blessed module
by tobyink (Canon) on Jan 03, 2021 at 11:48 UTC
|
sub new {
return bless {};
}
Never, ever, ever do this. The one argument version of bless really should issue a warning.
Do this:
sub new {
my $class = shift;
return bless( {}, $class );
}
| [reply] [d/l] [select] |
|
Never, ever, ever do this. The one argument version of bless really should issue a warning.
Actually, in real code I do not generally use the one argument version. I created that as a minimal example to support the question.
Having said that, I don't know why I avoid the single argument version. It is because I have copied other code that works and looks like the author knows what they are doing which is not exactly the most rigorous methodology for learning...
| [reply] |
|
The reason why you generally shouldn't use the one-argument version of bless is because it makes subclassing more difficult.
If you have two classes, Parent and Child, and Parent uses bless {}, $_[0];, then Child can simply inherit Parent's new (or other constructor) and Child->new() will Just WorkTM.
If Parent uses one-argument bless, then it will always create a Parent object, and Child will have to override the constructor (either writing a completely new constructor, or calling Parent's constructor and re-blessing the result before returning it).
| [reply] [d/l] [select] |
|
|