Coderefs in objects: tricky questions

P

peterkayatwork

I have an object (using standard AUTOLOAD from perltoot) that I want to
pass a coderef to and be able to run the coderef from.

If I do this:
new {
.... # standard object creation from perltoot
$self->{Sub}=sub { return $self->Real(@_) };
return $self;
};

sub Real
{
my $self=shift;
my $arg=shift;
return "[$arg]\n";
}


then I can call it via:
print &{$object->Sub}("Hi");

and get
[Hi]

Few questions:
1. Is this a Bad Way To Do It?
2.
This doesn't work:
$self->{Sub}=\&{$self->Real};

Why not?

The error I get is:

Use of inherited AUTOLOAD for non-method Object::Hi() is deprecated at
Object.t line 22.

Really terribly helpful, isn't it? I can do stuff in Perl, but having
a super-duper handle on WTF the AUTOLOAD stuff is doing with coderefs
is a bit beyond me :)

If anyone has a good answer for why this is happening, I'd love it. If
anyone has a good suggestion for which FM to read, I'll do so.

3. Is there a way to set $self->{Sub} without using 'sub'?

Thanks,

--Peter
peterkayatwork (at) yahoo.com
 
X

xhoster

I have an object (using standard AUTOLOAD from perltoot) that I want to
pass a coderef to and be able to run the coderef from.

If I do this:
new {

You need "sub new {". Please post real code.
... # standard object creation from perltoot
$self->{Sub}=sub { return $self->Real(@_) };
return $self;
};

You said you wanted to pass the coderef into the object, but here
you are hard coding it, rather than getting it out of new()'s argument
list!

sub Real
{
my $self=shift;
my $arg=shift;
return "[$arg]\n";
}

then I can call it via:
print &{$object->Sub}("Hi");
and get
[Hi]

Really? I get:

Can't locate object method "Sub" via package "foo" at object.pl line 23.

I can't tell if forgot to define the method Sub, or if you forgot to put
curlies around your dereference of $object using the key 'Sub'.
Again, post real code.

Few questions:
1. Is this a Bad Way To Do It?

Yes. Not passing a coderef into an object is a bad way to pass a coderef
into an object. Not posting real code is a bad way to get help on your
code. And piercing the object encapsulation by directly dereferencing the
object from outside the package (which it looks like you may be doing) is
usually a bad way to do OO.
2.
This doesn't work:
$self->{Sub}=\&{$self->Real};

Why not?

I think it is invoking $self->Real, and then trying to do \&{} on the
results of that invocation.

3. Is there a way to set $self->{Sub} without using 'sub'?

$self->{Sub}=$coderef;


Xho
 
P

peterkayatwork

Please post real code.

######################################################
# Parent.pm
#!/usr/bin/perl

package Parent;
use strict;
use warnings;

use Carp;

our $AUTOLOAD;

my %fields = (
Name => undef,
Sub => undef,
);

sub new
{
my $class=shift;
my $self={
_permitted => \%fields,
%fields,
};
bless ($self, $class);
return $self;
}

# Automatically create methods for everything defined in %fields above:
sub AUTOLOAD {
my $self = shift;
my $type = ref($self)
or croak "$self is not an object - cannot AUTOLOAD";

my $name = $AUTOLOAD;
$name =~ s/.*://; # strip fully-qualified portion

unless (exists $self->{_permitted}->{$name} ) {
croak "Can't access `$name' field in class $type";
}

if (@_) {
return $self->{$name} = shift;
} else {
return $self->{$name};
}
}

1;

######################################################
# Object.pm
#!/usr/bin/perl

package Object;
use strict;
use warnings;

use Parent;
our @ISA=qw(Parent);

sub new
{
my $class = shift;
my $self = $class->SUPER::new();
my($element);
# Not needed yet:
# foreach $element (keys %fields) {
# $self->{_permitted}->{$element} = $fields{$element};
# }
# @{$self}{keys %fields} = values %fields;

# Give this derived object a default bit of code:
$self->{Sub}=sub { return $self->Real(@_) };
return $self;
}

sub Real
{
my $self=shift;
my $arg=shift;
return "[$arg]\n";
}


1;

######################################################
# Object.t
#!/usr/bin/perl
use strict;
use warnings;

use Test::More;

use Object;

my $object = new Object;

print &{$object->Sub}("Hi");

######################################################
# Command line:
perl Object.t [Hi]

######################################################
Is there a better/more appropriate way to post all the code?
--Peter
peterkay (at) yahoo.com
 
P

peterkayatwork

######################################################
# Object.t
#!/usr/bin/perl
use strict;
use warnings;

use Test::More;

use Object;

my $object = new Object;

print &{$object->Sub}("Hi");

Sorry, this should have been:

is(&{$object->Sub}("Hi"), "[Hi]", "Can call coderef from
$object->Sub");

(with appropriate command line result)

It's a bit simplier then what I'm really doing, but illistrates the
situation :)

--Peter
 
A

A. Sinan Unur

(e-mail address removed) wrote in @g14g2000cwa.googlegroups.com:
Please post real code.
....

# Object.t
#!/usr/bin/perl
use strict;
use warnings;

use Test::More;

use Object;

my $object = new Object;

print &{$object->Sub}("Hi");

######################################################
# Command line:
perl Object.t [Hi]

######################################################
Is there a better/more appropriate way to post all the code?

Code that works would be great.

D:\Home\asu1\UseNet\clpmisc> z
You tried to run a test without a plan! Gotta have a plan. at
C:/opt/Perl/lib/Test/More.pm line 362
# Looks like your test died before it could output anything.

After fixing that:

D:\Home\asu1\UseNet\clpmisc> z
1..1
not ok 1 - Can call coderef from Object=HASH(0x19178e8)->Sub
# Failed test (D:\Home\asu1\UseNet\clpmisc\z.pl at line 69)
# got: '[Hi]
# '
# expected: '[Hi]'
# Looks like you failed 1 test of 1.

So, we fix that by changing the test case to

is( &{$object->Sub}("Hi"),
"[Hi]\n",
"Can call coderef from $object->Sub"
);

D:\Home\asu1\UseNet\clpmisc> z
1..1
ok 1 - Can call coderef from Object=HASH(0x19178f4)->Sub

I did slightly modify your code to put everything in one script. The
modified version follows below.

I did follow the messages in this thread, but I am still not sure what
your actual question is.

#!/usr/bin/perl

package Parent;
use strict;
use warnings;

use Carp;

our $AUTOLOAD;

my %fields = (Name => undef, Sub => undef);

sub new {
my $class = shift;
my $self = {
_permitted => \%fields,
%fields,
};
bless $self, $class;
}

# Automatically create methods for everything defined in %fields above:
sub AUTOLOAD {
my $self = shift;
my $name = $AUTOLOAD;
$name =~ s/.*://;

unless (exists $self->{_permitted}->{$name} ) {
croak "Can't access `$name' field in class ".__PACKAGE__;
}

if (@_) {
return $self->{$name} = shift;
} else {
return $self->{$name};
}
}

package Object;

use strict;
use warnings;

use base 'Parent';

sub new {
my $class = shift;
my $self = $class->SUPER::new();

$self->{Sub} = sub { return $self->Real(@_) };
return $self;
}

sub Real {
my $self = shift;
my $arg = shift;
return "[$arg]\n";
}

package main;

use strict;
use warnings;

use Test::More tests => 1;

my $object = Object->new;

is( &{$object->Sub}("Hi"),
"[Hi]\n",
"Can call coderef from $object->Sub"
);

1;
 
X

xhoster

######################################################
# Parent.pm
#!/usr/bin/perl

Thanks, now I can run your code, and have slightly better idea of what you
are trying to do. (Although I still don't know *why* you are trying to do
it.)

However, when I replace your code

$self->{Sub}=sub { return $self->Real(@_) };

With the other code from your original message:

$self->{Sub}=\&{$self->Real};

I do not get the error you report in your original message.
I get:

Use of uninitialized value in concatenation (.) or string at Object.pm line
33. Undefined subroutine &main::[]
called at Object.t line 13.

So it looks like I was right before, $self->Real is being invoked,
returning "[]\n", which is interpreted as the name of a subroutine. If you
want to delay the execution of $self->Real, then you do have to use sub {}
to do it.

######################################################
Is there a better/more appropriate way to post all the code?
--Peter
peterkay (at) yahoo.com

Well, I would have just put all the packages into one file. But the way
you did it was perfectly reasonable.

Xho
 
P

peterkayatwork

A. Sinan Unur said:
I did follow the messages in this thread, but I am still not sure what
your actual question is.

I guess the only question I have left is:

3. Is there a way to set $self->{Sub} without using 'sub'?

I want to pull this effect off:
$self->{Sub} = sub { return $self->Real(@_) };

without using 'sub'. I'd love to know how to correctly set up the code
ref directly. But as was pointed out:

$self->{Sub}=\&{$self->Real};

seems to be trying to call $self->Real and then execute the return
string as code...
use base 'Parent';

Where the h*ll is this documented?

--Peter
peterkayatwork (at) yahoo.com
 
P

Paul Lalli

A. Sinan Unur wrote:

Where the h*ll is this documented?

The profanity is unnecessary, and is unlikely to encourage anyone to
render your assistance. (No, I'm not saying I or anyone else will
refuse to help you if you use "hell" - with or without censoring - I'm
just saying it doesn't aid your cause at all).

The base pragma, like all modules and pragmas, is documented in the
associated perldoc:
perldoc base
or
http://perldoc.perl.org/base.html

Paul Lalli
 
E

Eric Schwartz

Paul Lalli said:
The base pragma, like all modules and pragmas, is documented in the
associated perldoc:
perldoc base
or
http://perldoc.perl.org/base.html

I think the real question was, "How should I have known I am supposed
to 'use base'? And that's a fair question. I looked in perltoot--
it's not even mentioned. In perlobj I see one mention, but it's very
much in passing, and unless you're reading carefully for references to
'use base', it's easy to miss. I see a nice explanation in perlboot,
but I don't know that I had even heard of that one until I was
searching for further references in perltoot.

'perldoc base' is only useful if you knew you needed 'use base' in the
first place, and wanted to know more. Otherwise, it's just lost in a
sea of very similar looking documentation.

-=Eric
 
P

Paul Lalli

Eric said:
I think the real question was, "How should I have known I am supposed
to 'use base'? And that's a fair question.

Eh. Maybe. I could see the OP's question being taken either way.
Either "Why isn't it documented that I should use this?" or "I am
completely unable to locate the documentation for this pragma."

Ambiguity strikes again.

Paul Lalli
 
X

xhoster

I guess the only question I have left is:

3. Is there a way to set $self->{Sub} without using 'sub'?

I suppose you could set up either a source filter or a properly prototyped
subroutine. Then instead of using "sub" you could use
"supercallifragilisticexpialidotious" or whatever you prefer. I'm not sure
why you would want to do this.
I want to pull this effect off:


without using 'sub'.

Why? What other keywords do you have an aversion to? What is next, do you
want to use lexical variables without using "my"? Do you want to use
modules without using "use" or "require"? Do you want to use grep without
using "grep"?

If you have a Perlish reason for wanting to avoid "sub", and if you tell us
what that reason is, perhaps we can help. If you just have some irrational
aversion to the three letter combination "sub", then I'm afraid that is a
psychological problem, not a Perl problem.

Where the h*ll is this documented?

perldoc base.

Xho
 
A

A. Sinan Unur

I think the real question was, "How should I have known I am supposed
to 'use base'?

No one said that the OP should have used 'use base'?

The use base line was in the code I posted in response to the OP's
question. I used it rather than mucking with the @ISA by instinct, and
did not even comment on it.
And that's a fair question.

Of course it is. And it can be answered by the OP reading the
documentation, or asking that particular question, instead of responding
with a profanity to my post.

Sinan
 
P

peterkayatwork

Paul said:
The profanity is unnecessary, and is unlikely to encourage anyone to
render your assistance. (No, I'm not saying I or anyone else will
refuse to help you if you use "hell" - with or without censoring - I'm
just saying it doesn't aid your cause at all).

Yes, I suppose you're right; I was flustered and using "natural"
language.
The base pragma, like all modules and pragmas, is documented in the
associated perldoc:
perldoc base

Ah. I can see clearly now - I looked under perltoot and under '-f
use', where it's certainly not documented. I read it not as (with
parens for logic, not perl):

use (base 'Parent'); # parent is DO to base

but as

(use base) 'Parent'; # base is somehow modifying use

I suppose I could even smack myself in the head and exclaim "dumbass!"

I'll claim I'd had a long day! :)

--Peter
 
P

peterkayatwork

If you have a Perlish reason for wanting to avoid "sub", and if you tell us
what that reason is, perhaps we can help. If you just have some irrational
aversion to the three letter combination "sub", then I'm afraid that is a
psychological problem, not a Perl problem.

Well, curiosity, anyway :) In Perl, there is More then One Way To Do
It. I'm just looking for that other way.

Besides, can't I claim that the "sub" adds another layer of
subroutines, thus hurting the execution time of my script? Well, ok, I
can claim anything I want - wasn't being serious here.

It's a bit like trying to be a wizard. Doing it without the 'sub'
would be very esoteric, very symbol-intense, and way cool - a true
incantation that only the initiated would ever understand. Of course,
I'd never use it.... I just want to be *able* to.

Ok, didn't claim it was important; I get the feeling I'm not going to
make the "Perl"cut - it's all irrational now :) I've got script that
works just fine with sub. (Thanks for the help, BTW)

--Peter
 
A

Anno Siegel

If you have a Perlish reason for wanting to avoid "sub", and if you tell us
what that reason is, perhaps we can help. If you just have some irrational
aversion to the three letter combination "sub", then I'm afraid that is a
psychological problem, not a Perl problem.

Well, curiosity, anyway :) In Perl, there is More then One Way To Do
It. I'm just looking for that other way.
[...]

It's a bit like trying to be a wizard. Doing it without the 'sub'
would be very esoteric, very symbol-intense, and way cool - a true
incantation that only the initiated would ever understand. Of course,
I'd never use it.... I just want to be *able* to.

Apparently you want to generate Perl code without using the Perl
compiler -- in other words, you want to write a partial Perl compiler
in Perl. While a full implementation of Perl in Perl might be of
some theoretical interest, the compilation of a single one-purpose
subroutine you seem to attempt would accomplish nothing except keep you
busy for a *long* time. It may be a feat, but it would make you look
about as wizardly as someone who has cut holes in the floor of a car and
is pushing it around with their feet. "Look, no engine."

Anno
 
M

Manni Heumann

This thread reminds me of a piece of code I've seen recently.
I want to pull this effect off:


without using 'sub'.

How about
$self->{sub} = \&Real;

and then later call it like that:

&{self->{sub}}( $self, @something );


I stared at those lines quite a while and they sure worked. Yet, they still
seemed at least odd, if not plain wrong.

To put it differently: Can you emulate a OO closure by using a non-oo closure
and then simply provide the $self argument yourself?


Manni
 
M

Manni Heumann

Lawrence said:
Well -- consider the following: Would that work with inheritance?

Once you 'break' the binding to the class, you have lost the ability
to inherit from your class.

Fair enough; very good point.

But since I can guarantee that noone will ever inherit from the particular
class where I've seen this used, can you (or somebody else) provide even more
good points?
 
J

Joe Smith

3. Is there a way to set $self->{Sub} without using 'sub'?

I want to pull this effect off:


without using 'sub'.

Why? A coderef is created using 'sub'. A coderef is executed
by invoking it as a function. Assuming you could create a coderef
without 'sub', it would still have to be invoked as a
subroutine/function to get executed.
Besides, can't I claim that the "sub" adds another layer of
subroutines, thus hurting the execution time of my script?

No. You have to go through internal process of pushing things
onto the call stack to execute the code; so there is no getting
around that bit of overhead.
-Joe
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,902
Latest member
Elena68X5

Latest Threads

Top