anjiro has asked for the wisdom of the Perl Monks concerning the following question:

So I keep hearing (from various AI professors) how great lisp is. They're probably right. One of the big things is macros: how they allow you generate new code on the fly. While writing some nice, old-fashioned perl code, I noticed myself making a bunch of nearly-identical functions to set and retrieve values in my object, and thought "Wow! What we need here is macros!" So here's my take on it. My main question is whether there's a better way than using evals to do this; the second question is what's the better way in general to do this?

sub _setter_getter_maker { my $subname = shift; my $funcref; $funcref = eval<<"END"; sub { my(\$self, \$$subname) = \@_; \$self->{'$subname'} = \$$subname if defined \$$subname; return \$self->{'$subname'}; }; END return $funcref; }

The idea for use is basically thus:

my $pretend_object = {}; my $new_func = _setter_getter_maker($pretend_object, 'attrib'); #Now we can set a value for 'attrib' in this object $new_func->('value');

Replies are listed 'Best First'.
Re: Pretending to be lisp: macros
by Zaxo (Archbishop) on Sep 18, 2003 at 02:09 UTC

    The usual way to generate automatic accessors in perl is with sub AUTOLOAD {...}. The Camel (3ed.) gives an example starting on p. 337. That is immediately followed by an example of generating them by name with closures, instead.

    After Compline,
    Zaxo

      Handy hint to anyone looking at Camel pg. 337: check the errata! Two of the variables are switched, making for some interesting bugs.
Re: Pretending to be lisp: macros
by simonm (Vicar) on Sep 18, 2003 at 04:13 UTC
    There are lots of method-generator packages already available to do this kind of work for you. (I'm assuming that you're not stuck on the idea of using "pretend objects" and would be happy with a blessed object instance.)

    You could do something like this with Class::MakeMethods easily enough:

    package MyObject; use Class::MakeMethods::Autoload { 'new' => 'Template::Hash::new', '.*' => 'Template::Hash::scalar -self_closure', }; package main; my $object = MyObject->new(); my $new_func = $object->attrib(); $new_func->('value');

    The Class::MakeMethods::Autoload statement says that we want to generate a constructor for "new", and for any other method name we want to construct a "scalar -self_closure" method.

    The "-self_closure" flag informs Class::MakeMethods that you don't want a simple accessor -- you want to return a closure bound the the target object that will act as an accessor.

Re: Pretending to be lisp: macros
by sgifford (Prior) on Sep 18, 2003 at 04:38 UTC
    I think you want the even lispier idea of closures:
    sub _setter_getter_maker { my($self,$subname) = @_; return sub { my($val)=@_; $self->{$subname} = $val if defined $val; return $self->{$subname}; } } my $pretend_object = {}; my $new_func = _setter_getter_maker($pretend_object, 'attrib'); #Now we can set a value for 'attrib' in this object $new_func->('value'); print $pretend_object->{'attrib'},"\n";
Re: Pretending to be lisp: macros
by Anonymous Monk on Sep 18, 2003 at 02:28 UTC

    Slap me around and call me Susan (actually, my name's Jim, but that's another story), but I don't like this. From an OO tandpoint, this makes no sense. Methods are supposed to be attached to the object they're supposed to work upon. With this method, you may create multiple methods that do the same thing, but none of which are accessible directly from the object they work upon. For instance:

    # yes, an associative array...but make it objectified my $pretend_object = bless({},"Pretend::Object"); # enter a block for whatever reason for (1..10) { # ok, since we decided to skip on creating methods accessible # from the object, let's create our accessor method here # note: it gets recreated every loop my $func = _setter_getter_maker($pretend_object,'attrib'); $func->($_); } # stupid accessor fell out of scope :-/ my $func = _setter_getter_maker($pretend_object,'attrib'); print $func->();

    You're probably asking yourself, "who would code like that?" Trust me, more people do than you'd expect. Just have a class that generates these accessor methods. Of course, accessor methods are generally considered to be bad design in OO. But anywho, perhaps a module from which these methods are inherited would be more appropriate?

    Anonymously yours,
    Anonymous Monk

Re: Pretending to be lisp: macros
by adrianh (Chancellor) on Sep 18, 2003 at 10:26 UTC

    This isn't really the same as Lisp macros. You're doing stuff at runtime. Lisp macros do stuff at compile time. You're using existing syntax to generate a function. Lisp macros make new syntax.

    Not that what you're doing is not interesting in its own right. Although (as others have pointed out) there are many other ways of doing similar things, and using the {} style of eval would be more efficient.

    You might find the discussion on Macros, LFSPs and LFMs of interest.

Re: Pretending to be lisp: macros
by bsb (Priest) on Sep 19, 2003 at 03:52 UTC
    Filter::Simple + Text::Balanced = macros
    There's a macro module on cpan, seems too fresh though.

    The advantage that Lisp macros have over most other non-lisp languages is that they can manipulate the syntax tree as a data structure. This would be like mixing B::Generate into the mix above.

      The advantage that Lisp macros have over most other non-lisp languages is that they can manipulate the syntax tree as a data structure.

      And that is a hellishly large advantage.

      The problem with taking the filter approach in Perl 5 is that multiple filters don't play well together. Since they're based on purely textual substitutions the text that one filter expects can interfere with the expectations of another.

      If, however, everybody is dealing with syntax trees this problem disappears... one of the (many) reasons I'm looking forward to Perl6 :-)

        The only alternative to this in perl5 is B::Utils and B::Generate and you have to be a wizard to do anything with it. Uck.