Update: the package now defines attributes for First, Last, and Count, has 36 tests and updated POD. The link below has been updated to point to the new package.

In this node, I mentioned an idea I had about altering a subroutine's behavior using attributes to make it easy to understand what is intended. Rather than put together a full package, I've created a small test package that defines just two attributes "Arrayref" and "Iterator". If called in scalar context, it returns a reference to an array. Otherwise, it returns the array. An alternate "NOVOID" parameter may be specified to make calling it in void context fatal.

The benefit is an easy to use/understand module that simplifies many subroutine context behaviors without forcing us to continually rewrite them. There's a bit of POD and some tests to show what's going on. Below is a sample of how you might use it:

use base 'Sub::Attributes'; sub reverse_me : Arrayref(NOVOID) { # calling in void context is now f +atal return reverse @_; } my @list = reverse_me(1,2,3); # (3,2,1) my $list = reverse_me(1,2,3); # [3,2,1] sub do_stuff : Iterator { # calling in void context is legal, but a no +-op return @_; } my @list = do_stuff(1,2,3); # simply returns the list my $list = do_stuff(1,2,3); # iterator object print $iterator->next; # prints 1 print $iterator->next; # prints 2 print $iterator->next; # prints 3 print $iterator->prev; # prints 3 print $iterator->prev; # prints 2 print $iterator->prev; # prints 1

Personally, I don't know that the iterator object is useful. It doesn't delay creation of the list and undefined (or false) items in the list can break things like while (defined $result->next). If you can convince me why I should keep it, I will. It's just there for demo purposes.

Suggestions, code reviews, or general rants appreciated.

Cheers,
Ovid

New address of my CGI Course.

  • Comment on RFC: Sub::Attributes -- alter subroutine context behavior with attributes
  • Download Code

Replies are listed 'Best First'.
Re: RFC: Sub::Attributes -- alter subroutine context behavior with attributes
by Aristotle (Chancellor) on Dec 10, 2003 at 00:30 UTC
    It doesn't delay creation of the list and undefined (or false) items in the list can break things like while (defined $result->next).

    That's why you make your iterator return an empty list when you reach the end of its list and then test using while (my ($item) = $result->next).

    While I like the concept, I'm not partial to the name. Sub::Context is not much better, though slightly more descriptive; I have very little idea what to expect by just seeing that name. Sub::ContextAware is very close, but I find it slightly misleading as well, so I propose Sub::ContextAwareness. It would be even better to work "Default" into the name somewhere, but that'd get unwieldly for not too much gain.

    Makeshifts last the longest.

      Sub::Context (by chromatic) exists on CPAN and does context sensitive dispatch; I think your suggestions would muddy the existing namespace. Since this is attribute based, and people are already aware of wantarray and probably Want, I'd suggest some variation of: Attribute::Want or Sub::Attribute::Want, or maybe using 'context': Attribute::Context.

        ++ to the idea of changing the name to put this in the Attribute:: hierarchy, which seems to be where other such packages have tended to end up. Perhaps Attribute::ArrayContext?
      Oops, I completely forgot to make my real point while arguing technicalia about the iterator. Indeed, I don't think the iterator attribute is very useful as is - I don't think I'd ever want to use the stock iterator provided. It should (at least optionally) take a package name parameter so that users can supply their own iterator classes.

      Makeshifts last the longest.

Re: RFC: Sub::Attributes -- alter subroutine context behavior with attributes
by theorbtwo (Prior) on Dec 10, 2003 at 05:59 UTC

    I'd rather see novoid be an attribute of it's own, rather then an argument to another. It makes sense to use on it's own, it's not logicly a subtype of another contextual thingy.

    I'd also rather it be a warning then fatal. warings::warnif("void", "Useless use of $fullydecoratedname in void context"); That'd have parity with what the core does for some builtins. For that matter, you may want to have a :NoScalar and :NoList. (:NoScalar would match sort, see perldiag's "Useless use of sort in scalar context", which I've never actualy seen. I know of no analog for :NoList, but it would seem to be required for parity.) :NoVoid should also skip running of the actual sub.


    Warning: Unless otherwise stated, code is untested. Do not use without understanding. Code is posted in the hopes it is useful, but without warranty. All copyrights are relinquished into the public domain unless otherwise stated. I am not an angel. I am capable of error, and err on a fairly regular basis. If I made a mistake, please let me know (such as by replying to this node).

Re: RFC: Sub::Attributes -- alter subroutine context behavior with attributes
by Anonymous Monk on Dec 10, 2003 at 07:42 UTC

    Just a note: your test package has an undocumented dependency on the non-standard Test::Exception module.

      Thanks. I'll fix that. I also need to add use 5.006 so that people with older versions won't get nasty surprises.

      Cheers,
      Ovid

      New address of my CGI Course.

Re: RFC: Sub::Attributes -- alter subroutine context behavior with attributes
by jryan (Vicar) on Dec 10, 2003 at 00:17 UTC

    Looks cool, but why is the interface through a base class? From my understanding, Sub::Attributes are there to improve an aspect of functional programming. There is nothing class-related going on, and so providing a class-related interface does not seem to make sense. I haven't had time to look at the internals of Sub::Attributes (I have to procter an exam in 10 minutes), but was this the only way the functionality could work? Could the interface also be made to work through some crazy importation shenanegains?

      It uses a base class as that is the intended implementation from Attribute::Handlers. However, I could have made the attributes universal by stuffing them in the UNIVERSAL class, but that seemed like an abomination (even though many programmers are doing it -- I'm wondering if there is some benefit that I'm missing).

      I had tried setting it up via exporting, but I couldn't get it to work. Probably just a screwup in my syntax somewhere.

      Cheers,
      Ovid

      New address of my CGI Course.

Re: RFC: Sub::Attributes -- alter subroutine context behavior with attributes
by hardburn (Abbot) on Dec 10, 2003 at 14:42 UTC

    I don't know that the iterator object is useful.

    I think it'll be useful as a "do this later" kind of thing. In other words, a programer wants to provide an iterator, but they just want to get something working right now. So they return an array and put the iterator attribute on the subroutine, intending to create a more efficient implementation later.

    ----
    I wanted to explore how Perl's closures can be manipulated, and ended up creating an object system by accident.
    -- Schemer

    : () { :|:& };:

    Note: All code is untested, unless otherwise stated

      No its not. That marries the code to Ovid's idea of what an iterator's API looks like. If I wrote it I'd have returned a sub reference and called it as $result->() and avoided the whole 'next' thing altogether.

        *shrug* Personally, I think it should return a Class::Iterator object (which provides iterator versions of map and grep), though that marries you to Robert Silve's idea of what an iterator API looks like.

        ----
        I wanted to explore how Perl's closures can be manipulated, and ended up creating an object system by accident.
        -- Schemer

        : () { :|:& };:

        Note: All code is untested, unless otherwise stated