Re: on the fly methods
by Fletch (Bishop) on Feb 27, 2007 at 16:35 UTC
|
This is Perl, not Ruby. Methods are subs in a given package, not properties of individual instances. You possibly could do some dark magic with AUTOLOAD (make it lookup say $self->{_instancemethods}->{$AUTOLOAD} and call that if it exists), or eval new code into the instance's package (eval qq{ package @{[ ref $self ]}; sub { "NEW CODE" }}; however that's going to make the method available on all instances of that class (and its descendants) not just the one in $self)).
Perhaps you could elaborate on what you're trying to do and you might get better suggestions (e.g. yes, you're trying to emulate Ruby's
class << obj
def new_method
"fweeee"
end
end
in order to add a method on a single instance).
Update: Formatting and wording tweaks.
| [reply] [d/l] [select] |
|
| [reply] |
|
Actually Class::MOP does not provide for a (direct) way to add methods to an instance, it mostly deals with just classes. And with Moose, there is no way to add a single method on a per-instance basis really either, you do it with runtime Roles instead, like this:
{
package My::Class;
use Moose;
sub hello { "Hello" }
package My::Role;
use Moose::Role;
sub foo { "Foo" }
}
my $obj_1 = My::Class->new;
my $obj_2 = My::Class->new;
My::Role->meta->apply($obj_1);
print $obj_1->foo; # print "Foo"
print $obj_2->foo; # dies with a method not found error
Also Class::Trait does this same type of thing using Traits instead of Roles.
Ruby accomplishes this type of thing with "Eigenclasses" which were in the Perl 6 metamodel at one time, but were not backported to Moose (because they tended to make simple metaclass things overly complex). The Ruby idiom of:
class << obj
def new_method
"fweeee"
end
end
roughly translates into this:
obj.add_singleton_method(lambda { "fweee" })
where add_singleton_method is just a method in Object. Something like this could be added to Moose without too much trouble at all really (no AUTOLOAD tricks needed either).
| [reply] [d/l] [select] |
|
| [reply] |
|
I had never heard of Moose before this, but I think it's potentially something I could one day use in all my new OO code. Wow, just wow.
| [reply] |
|
|
Re: on the fly methods
by Thelonius (Priest) on Feb 27, 2007 at 16:54 UTC
|
sub makeMethod {
my $this = shift;
$this->{_coderef} = eval $this->{_codestring}
}
sub callMethod {
my $this = shift;
$this->{_coderef}->(@_)
}
If you want to do something like this: $this->makeMethod( "somenewname", "some code" );
$this->somenewname();
then I think you'll have to use AUTOLOAD or some module that provides that functionality.
| [reply] [d/l] [select] |
Re: on the fly methods
by agianni (Hermit) on Feb 27, 2007 at 18:13 UTC
|
use Class::Prototyped;
my $p = Class::Prototyped->new();
$p->reflect->addSlot( sub1 => sub { print "this is sub1" } );
$p->sub1 # prints "this is sub1";
It also provides a whole lot of other dynamic inheritance options if you want to build entire class hierarchies on the fly. | [reply] [d/l] |
Re: on the fly methods
by Rhandom (Curate) on Feb 27, 2007 at 16:47 UTC
|
There are various ways. The least dirty is to use the AUTOLOAD system as in the following.
package Foo;
use strict;
use vars qw($AUTOLOAD);
use Carp qw(croak);
sub new { bless {}, __PACKAGE__ }
sub add_method {
my ($self, $name, $code) = @_;
### This following chunk could use Scalar::Util::blessed
### if they have perl 5.8 installed. We will operate without it
if (! ref $code) {
# scary and sort of ugly but thats what the OP wanted
$code = eval $code;
}
# (! Scalar::Util::reftype($code) ne 'CODE') {
if (! UNIVERSAL::isa($code, 'CODE')) {
croak "Usage : add_method(method => sub {})\n
add_method(method => 'sub {}')";
}
$self->{'_methods'}->{$name} = $code;
}
sub AUTOLOAD {
my $self = shift;
my $name = $AUTOLOAD =~ /(\w+)$/ ? $1 : croak "Invalid method \"$A
+UTOLOAD\"";
my $code = $self->{'_methods'}->{$name}
|| croak "No such method \"$name\" via package ".__PACKAGE__;
$self->$code(@_);
}
my $foo = Foo->new;
$foo->add_method(bar => sub { print "bar\n" });
$foo->add_method(baz => 'sub { print "baz\n" }');
$foo->bar;
$foo->baz;
The other way of doing this sort of thing involves manipulating the symbol table which isn't hard, but I wouldn't recommend if you are just starting out.
my @a=qw(random brilliant braindead); print $a[rand(@a)];
| [reply] [d/l] |
|
# (! Scalar::Util::reftype($code) ne 'CODE') {
if (! UNIVERSAL::isa($code, 'CODE')) {
How about:
unless (defined &$code) {
Also, isa() is a method, not a function. Using it as a function can break many things. | [reply] [d/l] [select] |
|
Sorry to the original thread as we are now a bit off the beaten topic.
Yes. It is a method. It will skip $code's ->isa. It breaks overloading. It also works in 99% of the use cases (probably more) that the general programming population will use and in those cases where somebody has created an object that pretends to be code they can simply pass sub { &$code } and suddenly it works fine. Changing to $code->isa('CODE') in this case would actually break 99% of use cases because it only allows for objects that are CODE (not bare coderefs). I think it is wonderful that Perl 5 (and other languages) can call methods in a more functional form if the need arises.
Using defined &$code almost works but it breaks if a non-coderef reference is passed in. It also doesn't go the full route of supporting overloaded objects so it isn't any more of a solution.
The only thing that "might" be able to handle 100% of cases is something like
use Scalar::Util qw(blessed);
my $is_code = blessed($code) ? $code->can('&{}') : ref($code) eq 'CODE
+';
UPDATE: - Yup - I missed blessed coderefs which would have to be checked for with eval { defined &$code }. The corrected code might look like
my $is_code = ! blessed($code)
? ref($code) eq 'CODE'
: $code->can('&{}')
|| $code->isa('CODE') # arguably not a good test (bless
+{}, 'CODE')
|| eval { defined &$code }; # arguably bad because it ge
+ts called in some contexts
Arguably that isn't all that bad. I'm sure there is probably still a case I'm missing. I do know of a few companies that are still pre-Perl 5.8 and the Scalar::Util::blessed route isn't even an option for them.
Yes. I agree that things can break. Perl 5 has problems in being able to type data or easily (s/easily/succinctly/) detect the type or capabilities of data. There are many things that can break during argument passing in Perl 5. There are basic things that can be done to check for basic types of data. If advanced users want to pass in advanced types, the basic checks won't prohibit them - they just force them to wrap them in more palatable forms. Although many times the "basic" checks I've seen are indeed poorly done and restrict viable options that would otherwise work just fine.
I've probably already gone too far. There are many who feel very strongly about this issue. But the picture painted is usually more severe that is necessary and when presented as a blanket statement hides useful, working use cases.
Often on Perlmonks there is the assumption that one doesn't know what they are doing when they actually do. And of course there are those that assume they know what they are doing when they actually do not.
my @a=qw(random brilliant braindead); print $a[rand(@a)];
| [reply] [d/l] [select] |
|
|
Re: on the fly methods
by Zaxo (Archbishop) on Feb 27, 2007 at 19:38 UTC
|
package Foo;
sub makemethod {
my $obj = shift;
$obj->{'_codemethod'} = shift; # takes a real code reference
# in this implementation,
# not a string, so renamed
}
sub callmethod {
my $obj = shift;
$obj->{'_codemethod'}(@_);
}
# . . .
Don't need no stinkin' Ruby ;-)
| [reply] [d/l] |
Re: on the fly methods
by fenLisesi (Priest) on Feb 27, 2007 at 16:39 UTC
|
$this->{_coderef} = sub{ return "Don\'t think so"; };
## ... and later:
printf "%s\n", $this->{_coderef}->();
Do you have to create your methods on the fly? There could be better ways. Cheers.
Update: Changed "_codestring" to "_coderef" | [reply] [d/l] |
Re: on the fly methods
by Moron (Curate) on Feb 27, 2007 at 16:53 UTC
|
No need to eval the code, for example: $this -> { _codereference } = sub { return "Do " . shift() . " so.\n";
+ }
# ...
print FH $this -> { _codereference }{ "believe" );
| [reply] [d/l] |
Re: on the fly methods
by stvn (Monsignor) on Feb 27, 2007 at 21:54 UTC
|
| [reply] |
Re: on the fly methods [Thanks!]
by Ojosh!ro (Beadle) on Feb 28, 2007 at 08:55 UTC
|
Thank you brothers.
Firstly, my excuses, I have been unclear. I think however I got enough replies and suggestions to try.
Al2O3 I'm unfamiliar with. Anything I want to try?
But thanks very much. I'll go through the options you offered and see what flavour suits me best.
my($s,$b,@c,@b)=qw(0 0 5C 5F 2F 20);while(int($s+.45)<2){$b[0].=chr(hex($c[int($s+.45)]));$b[1].=chr(hex($c[int($s+2.45)]));$s+=.55}while(1){print $b[$b];$b--;$b*=$b}#Fishnets
| [reply] |