Send Parameter to Multiple Subroutines in Perl Module - perl

I'm creating a user module to extract user information and currently I have:
sub new
{
my $class = shift;
my ( $id ) = #_;
my $self = getUserInfo ($id);
bless $self, $class;
return $self;
}
sub getUserInfo
{
...
}
However, I would like to achieve something to the effect of:
my $self = (getFirstName($id), getLastName($id), getEmpNum($id));
...
sub getFirstName{ return { firstname => $firstname }; }
sub getLastName{ return { lastname => $lastname }; }
sub getEmpNum{ return { empnum => $empnum }; }
How do I go about distributing a parameter to multiple subroutines?

I think your general code architecture has a few problems, but the snippets so far don't offer enough context to suggest an alternative solution – consider posting your complete code on Code Review for a more complete criticism.
Regarding your immediate problem: You could write a function to combine the hash references:
use Carp ();
sub combine_hashrefs {
my %combined;
for my $hashref (#_) {
if (my #conflicts = grep { exists $combined{$_} } keys %$hashref) {
Carp::confess "The keys [#conflicts] are conflicting";
}
#combined{keys %$hashref} = values %$hashref;
}
return \%combined;
}
...
my $self = combine_hashrefs($hashref_a, $hashref_b, $hashref_c, ...);

Do I understand correctly that you want to avoid the repetition of $id in the following line?
my $self = (getFirstName($id), getLastName($id), getEmpNum($id));
$self is a scalar, so you should rather use the anonymous array [...]. To specify $id only once, you can use
my $self = [ map $_->($id), \&getFirstName, \&getLastName, \&getEmpNum ];

Related

In Perl, can you subclass and hook all parent-class functions without `AUTOLOAD`?

I'm writing a subclass that encapsulates multiple objects of the parent class so I can call functions sort-of like a vector, something like this:
package OriginalClass;
sub new { return bless {bar => 123}, 'OriginalClass' }
sub foo { return shift->{bar}; }
1;
package NewClass;
use parent OriginalClass;
# Return a blessed arrayref of "OriginalClass" objects.
# new() would be called NewClass->new(OriginalClass->new(), ...)
sub new {
my $class = shift;
return bless \#_, 'NewClass';
}
# Vectorized foo(), returns a list of SUPER::foo() results:
sub foo
{
my $self = shift;
my #ret;
push #ret, $_->SUPER::foo() foreach #$self;
return #ret;
}
1;
I don't want to write a new vectorized function in NewClass for each function in OriginalClass, particularly for when OriginalClass adds new functions to be maintained (vectorized) in NewClass.
Question:
As I understand AUTOLOAD is slow, so is there a way to vectorize calls OriginalClass via something like NewClass without AUTOLOAD?
As I understand AUTOLOAD is slow
If AUTOLOAD generates the missing sub, then only the first call is "slow" since subsequent calls of the same method don't result in AUTOLOAD being called at all.
package NewClass;
use strict;
use warnings;
sub new {
my $class = shift;
return bless( \#_, $class );
}
sub AUTOLOAD {
my $method_name = our $AUTOLOAD =~ s/^.*:://sr;
my $method = sub {
my $self = shift;
return map { $_->$method_name( #_ ) } #$self;
};
{
no strict 'refs';
*$method_name = $method;
}
goto &$method;
}
1
Note that I didn't use parent and SUPER::. This isn't an inheritance relationship. And it would prevent AUTOLOAD from getting called since AUTOLOAD is only called when a method doesn't exist.
You can use Sub::Name to "name the sub" for better diagnostics.
use Sub::Name qw( subname );
my $method = subname $method_name => sub { ... };
But yes, AUTOLOAD can be avoided here, as long as you can get a list of the method names in advance.
package NewClass;
use strict;
use warnings;
sub new {
my $class = shift;
return bless( \#_, $class );
}
for my $method_name (qw( foo ... )) {
my $method = sub {
my $self = shift;
return map { $_->$method_name( #_ ) } #$self;
};
no strict 'refs';
*$method_name = $method;
}
1
The above uses a hardcoded list, but more dynamic solutions are possible. For example, the list could be obtained from inspecting the contents of the OriginalClass namespace for subs (filtering out new and anything else inappropriate such as names starting with _).
Module https://metacpan.org/pod/Array::Delegate could be helpful : it delegates method calls to an array of objects.

Implementing a dispatch table

I'm trying to implement a dispatch table which calls functions inside a Perl module. I know how to implement dispatch tables generally, but I can't seem to get it right when referencing an object method from within $self. Maybe I haven't Googled enough, but so far, the right syntax is elusive.
I have traced the parameters though the calls, and I know what is happening -- the function references are not receiving a reference to $self as their first parameter. This is what I currently have inside $self. I believe I copied this over properly; if I made a mistake and it doesn't run, I apologize.
package MyRefHashTest;
use strict;
use warnings;
sub new {
my $class = shift;
my $self = {
DISPATCH => {
ONE => \&funcOne,
TWO => \&funcTwo,
THREE => \&funcThree,
FOUR => \&funcFour
}
};
bless $self, $class;
return $self;
}
sub funcOne {
my ($self, $param) = #_;
print "func1 $param \n";
}
sub funcTwo {
my ($self, $param) = #_;
print "func2 $param \n";
}
sub funcThree {
my ($self, $param) = #_;
print "func3 $param \n";
}
sub funcFour {
my ($self, $param) = #_;
print "func4 $param \n";
}
sub runTesting {
my ($self, $type) = #_;
($self->{DISPATCH}{$type} || sub {})->("string");
}
1;
# To Test:
$test = MyRefHashTest->new;
$test->runTesting("ONE");
$test->runTesting("TWO");
$test->runTesting("THREE");
$test->runTesting("FOUR");
The actual output I get is that $param is undefined in the function calls from the dispatch table, when it should not be. This is how I know that the references to $self are not where they should be. The functions think that $type is $self.
I have tried editing the hash table references so they look like \$self->functionName, but that only results in a compilation error for $self not being properly defined on that line.
Can anyone guide me to the right syntax for this, please?
Thanks!
EDIT: After much more work, I finally found a solution. It's some very interesting syntax, a lot more complicated than I thought. Essentially, I'm building the hash from the inside out:
my $self = {
DISPATCH => undef
};
$self->{DISPATCH} = {
ONE => sub { $self->funcOne(#_); },
TWO => sub { $self->funcTwo(#_); },
THREE => sub { $self->funcThree(#_); },
FOUR => sub { $self->funcFour(#_); }
};
It works, but it seems like a lot of hassle for what it is. If anyone knows of an easier way to do this, I would still be very interested in it. If there isn't an easier way, on the other hand, I hope this can help somebody.
What follows are four approaches for implementing a method-based dispatch table. The differences explained afterwards.
my %DISPATCH = (
ONE => \&funcOne,
TWO => \&funcTwo,
THREE => \&funcThree,
FOUR => \&funcFour,
);
sub runTesting {
my ($self, $type) = #_;
my $method = $DISPATCH{$type};
return $self->$method("string");
}
or
my %DISPATCH = (
ONE => __PACKAGE__->can('funcOne'),
TWO => __PACKAGE__->can('funcTwo'),
THREE => __PACKAGE__->can('funcThree'),
FOUR => __PACKAGE__->can('funcFour'),
);
sub runTesting {
my ($self, $type) = #_;
my $method = $DISPATCH{$type};
return $self->$method("string");
}
or
my %DISPATCH = (
ONE => 'funcOne',
TWO => 'funcTwo',
THREE => 'funcThree',
FOUR => 'funcFour',
);
sub runTesting {
my ($self, $type) = #_;
my $method_name = $DISPATCH{$type};
return $self->$method_name("string");
}
or
my %DISPATCH = (
ONE => sub { shift->funcOne(#_) },
TWO => sub { shift->funcTwo(#_) },
THREE => sub { shift->funcThree(#_) },
FOUR => sub { shift->funcFour(#_) },
);
sub runTesting {
my ($self, $type) = #_;
my $cb = $DISPATCH{$type};
return $cb->($self, "string");
}
All four approaches allow the methods to be defined in the same class.
The last three approaches allow the methods to be defined in a superclass as well.
The last two approaches allow a subclass to provide or override the method as well. These are your best options.
How about passing in $self in the dynamic dispatch method:
sub runTesting {
my ($self, $type) = #_;
($self->{DISPATCH}{$type} || sub {})->($self,"string");
^^^^^
}
I believe the problem is that you are invoking the methods as plain functions and not object methods.

How can I do function partial application in Perl?

Is there any way to achieve partial application in Perl?
Suppose, I want to do something like:
sub each_file($arr, $op) {
$op->($_) for #{$arr};
...
}
sub each_line($op, $file) {
...
}
each_file($arr, each_line($op));
I want to partially apply each_line() to only $op, so it'll become a new function can be passed to $each_file, how do I express this in idiomatic Perl?
You can do this in Perl with two approaches combined:
A function which returns a function reference
Closures
Example:
sub each_file {
my ($arr, $line_fn) = #_;
$line_fn->($_) for #{$arr};
...
}
sub each_line {
my ($op, $file) = #_;
...
}
sub make_line_processor {
my ( $op ) = #_;
# This is closed over $op, which effectively becomes
# a constant for the returned function
my $fn = sub {
return each_line( $op, #_ );
};
return $fn;
}
# To call it:
each_file( $arr, make_line_processor($op) );
This can be an even more useful technique in cases where you don't want $op directly, but some expensive-to-fetch derivation of it. In which case you would calculate the derived value just once (in the make_line_processor function) and close over that instead.
# given some $op as implied by your code snippet
each_file($arr, sub { each_line($op, shift) });
# shift op will be applied when anonymous sub { … } is called
(Your code snippet doesn't make it entirely clear what you intend $op to be when you make the call to each_line. It's usually better to present small working programs.)
You can roll this functionality up into a class. Then you can overload the subroutine dereference operator to make it look like your class is really a code reference.
package Partial;
use overload '&{}' => \&call;
sub new {
my $class = shift;
my $code = shift;
bless {code => $code, args => \#_}, $class;
}
sub call {
my ($self) = #_;
return sub{ $self->{code}->(#{$self->{args}}, #_) }
}
You can then use it like this:
sub printArgs {
print join ", ", #_;
print "\n";
}
my $partial = Partial->new(\&printArgs, 'foo', 'bar');
$partial->('baz', 'bat');
# prints foo, bar, baz, bat

How can I call methods on a tied variable?

I've just started to learn about tie. I have a class named Link which I would like to do the following thing:
if fetched, return the link's address
if stored, store the new address
be able to call methods on it
So far, my code is :
package Link;
sub FETCH {
my $this = shift;
return $this->{"site"};
}
sub STORE {
my ($self,$site) = #_;
$self->{"site"} = $site;
}
sub print_method {
my $self = shift;
print $self->{"site"};
}
sub TIESCALAR {
my $class = shift;
my $link = shift;
my $this = {};
bless($this,$class);
$this->{"site"} = $link;
return $this;
}
1;
And the code I'm using to check the functionality is:
use Link;
tie my $var,"Link","http://somesite.com";
$var->print_method;
When ran, the script will terminate with the following error:
Can't call method "print_method" without a package or object reference at tietest.pl line 4..
If I understand its message correctly, $var->print_method resolves to some string upon which the method print_method is called. How could I benefit from tie, but also use the variable as an object?
EDIT: after experimenting a bit,I found out that if I return $self on fetch , I can call the methods , however , fetch won't return the address .
EDIT 2:the perl monks supplied me the solution : tied . tied will return a reference to the object VARIABLE .
By combining tied with my methods , I can accomplish everything I wanted .
Tie is the wrong tool for this job. You use ties when you want the same interface as normal data types but want to customize how the operations do their work. Since you want to access and store a string just like a scalar already does, tie doesn't do anything for you.
It looks like you want the URI module, or a subclass of it, and perhaps some overloading.
If you really need to do this, you need to use the right variable. The tie hooks up the variable you specify to the class you specify, but it's still a normal scalar (and not a reference). You have to use the object it returns if you want to call methods:
my $secret_object = tie my($normal_scalar), 'Tie::Class', #args;
$secret_object->print_method;
You can also get the secret object if you only have the tied scalar:
my $secret_object = tied $normal_scalar;
I have an entire chapter on tie in Mastering Perl.
I suggest making a normal Perl object and then overloading stringification. You lose the ability to store a value through assignment, but retain the ability to get the value out by printing the object. Once you start wanting to call methods directly, an object is probably what you want.
package Link;
use strict;
use Carp;
use overload
(
'""' => sub { shift->site },
fallback => 1,
);
sub new
{
my $class = shift;
my $self = bless {}, $class;
if(#_)
{
if(#_ == 1)
{
$self->{'site'} = shift;
}
else { croak "$class->new() expects a single URL argument" }
}
return $self;
}
sub site
{
my $self = shift;
$self->{'site'} = shift if(#_);
return $self->{'site'};
}
sub print_method
{
my $self = shift;
print $self->site, "\n";
}
1;
Example usage:
use Link;
my $link = Link->new('http://somesite.com');
print $link, "\n"; # http://somesite.com
$link->print_method; # http://somesite.com
If you really, really want assignment to work too, you can combine a normal object with overloaded stringification (Link, above) with tie:
package LinkTie;
use strict;
use Link;
sub FETCH
{
my $this = shift;
return $this->{'link'};
}
sub STORE
{
my($self, $site) = #_;
$self->{'link'}->site($site);
return $site;
}
# XXX: You could generalize this delegation with Class::Delegation or similar
sub print_method
{
my $self = shift;
print $self->{'link'}->print_method;
}
sub TIESCALAR
{
my $class = shift;
my $self = bless {}, $class;
$self->{'link'} = Link->new(#_);
return $self;
}
1;
Example usage:
tie my $link,'LinkTie','http://somesite.com';
print $link, "\n"; # http://somesite.com
$link->print_method; # http://somesite.com
$link = 'http://othersite.com';
print $link, "\n"; # http://othersite.com
$link->print_method; # http://othersite.com
This is all quite hideous and a long way to go just to get the dubious ability to assign to something that you can also call methods on and also print as-is. A standard URI object with stringification is probably a better bet.

Deferring code on scope change in Perl

I often find it useful to be able to schedule code to be executed upon leaving the current scope. In my previous life in TCL, a friend created a function we called defer.
It enabled code like:
set fp [open "x"]
defer("close $fp");
which was invoked when the current scope exited. The main benefit is that it's always invoked no matter how/where I leave scope.
So I implemented something similar in Perl but it seems there'd be an easier way. Comments critiques welcome.
The way I did it in Perl:
create a global, tied variable which holds an array of subs to be executed.
whenever I want to schedule a fn to be invoked on exit, I use local to change the array.
when I leave the current scope, Perl changes the global to the previous value
because the global is tied, I know when this value change happens and can invoke the subs in the list.
The actual code is below.
Is there a better way to do this? Seems this would be a commonly needed capability.
use strict;
package tiescalar;
sub TIESCALAR {
my $class = shift;
my $self = {};
bless $self, $class;
return $self;
}
sub FETCH {
my $self = shift;
return $self->{VAL};
}
sub STORE {
my $self = shift;
my $value = shift;
if (defined($self->{VAL}) && defined($value)) {
foreach my $s (#{$self->{VAL}}) { &$s; }
}
$self->{VAL} = $value;
}
1;
package main;
our $h;
tie($h, 'tiescalar');
$h = [];
printf "1\n";
printf "2\n";
sub main {
printf "3\n";
local $h = [sub{printf "9\n"}];
push(#$h, sub {printf "10\n";});
printf "4\n";
{
local $h = [sub {printf "8\n"; }];
mysub();
printf "7\n";
return;
}
}
sub mysub {
local $h = [sub {printf "6\n"; }];
print "5\n";
}
main();
printf "11\n";
Well, your specific case is already handled if you use lexical filehandles (as opposed to the old style bareword filehandles). For other cases, you could always use the DESTROY method of an object guaranteed to go to zero references when it goes out of scope:
#!/usr/bin/perl
use strict;
use warnings;
for my $i (1 .. 5) {
my $defer = Defer::Sub->new(sub { print "end\n" });
print "start\n$i\n";
}
package Defer::Sub;
use Carp;
sub new {
my $class = shift;
croak "$class requires a function to call\n" unless #_;
my $self = {
func => shift,
};
return bless $self, $class;
}
sub DESTROY {
my $self = shift;
$self->{func}();
}
ETA: I like brian's name better, Scope::OnExit is a much more descriptive name.
Instead of using tie for this, I think I'd just create an object. You can also avoid the local that way too.
{
my $defer = Scope::OnExit->new( #subs );
$defer->push( $other_sub ); # and pop, shift, etc
...
}
When the variable goes out of scope, you have a chance to do things in the DESTROY method.
Also, in the example you posted, you need to check that the values you store are code references, and it's probably a good idea to check that the VAL value is an array reference:
sub TIESCALAR { bless { VAL => [] }, $_[0] }
sub STORE {
my( $self, $value ) = #_;
carp "Can only store array references!" unless ref $value eq ref [];
foreach { #$value } {
carp "There should only be code refs in the array"
unless ref $_ eq ref sub {}
}
foreach ( #{ $self->{VAL}} ) { $_->() }
$self->{VAL} = $value;
}
You may want to try out B::Hooks::EndOfScope
I Believe this works:
use B::Hooks::EndOfScope;
sub foo {
on_scope_end {
$codehere;
};
$morecode
return 1; # scope end code executes.
}
foo();
I think you want something like Scope::Guard, but it can't be pushed. Hmmm.
Thanks.
Trivially,
sub OnLeavingScope::DESTROY { ${$_[0]}->() }
used like:
{
...
my $onleavingscope = bless \sub { ... }, 'OnLeavingScope';
my $onleavingscope2 = bless \\&whatever, 'OnLeavingScope';
...
}
(The extra level of having a reference to a reference to a sub is necessary only to work around an optimization (that's arguably a bug) when using a non-closure anonymous sub.)