2011/11/03

Perl - Tiny lightweight structures module

$ cat struct.pm 
package struct;

use strict;
use warnings;

sub import
{
   shift;
   my ( $name, $fields ) = @_;
   my $caller = caller;

   my %subs;
   foreach ( 0 .. $#$fields ) {
      my $idx = $_;
      $subs{$fields->[$idx]} = sub :lvalue { shift->[$idx] };
   }

   my $pkg = "struct::impl::$name";

   no strict 'refs';
   *{$pkg."::$_"} = $subs{$_} for keys %subs;
   *{$caller."::$name"} = sub { bless [ @_ ], $pkg };
}

1;

$ perl
use struct Point => [qw( x y )];
my $p = Point(10,20);
printf "Point is at (%d,%d)\n", $p->x, $p->y;
$p->x = 30;
printf "Point is now at (%d,%d)\n", $p->x, $p->y;
$p->z = 40;

__END__
Point is at (10,20)
Point is now at (30,20)
Can't locate object method "z" via package "struct::impl::Point" at - line 6.

It's specifically and intentionally not an object class. You cannot subclass it. You cannot provide methods. You cannot apply roles or mixins or metaclasses or traits or antlers or whatever else is in fashion this week.

On the other hand, it is tiny, single-file, creates cheap lightweight array-backed structures, uses nothing outside of core. And I defy anyone to even measure its startup overhead with a repeatable benchmark.

It's intended simply to be a slightly nicer way to store internal data structures, where otherwise you might be tempted to abuse a hash, complete with the risk of typoing key names.

Would anyone use this, if it were available?

7 comments:

  1. +1 for the idea, though the implementation could use a little bit of polish I think.

    Random thoughts:
    - Why is $fields an array ref instead of an array?
    - Should there be some friendly message if someone does "use struct;" or "use struct Foo;"?
    - Should field initialization be required?
    - structs could do with a nice stringification. maybe.
    - "use struct" is *extremely* close to "use strict"
    - Since these are explicitly not objects, it would be nice for the "method missing" message to say something less OOPy and to not mention "struct::impl::" at all.

    ReplyDelete
  2. @PerlPilot: Hmmmmyes, I should respond to these:

    > - Why is $fields an array ref instead of an array?

    A reasonable question. I was thinking about ability to give other options, but I guess those are rare, and can just go in a HASHref at the beginning or end. So maybe an array is nicer.

    > - Should there be some friendly message if someone does "use struct;" or "use struct Foo;"?

    Sounds good.

    > - Should field initialization be required?

    Most likely just assert in the "constructor" that

    @_ == @fields or croak "Usage: $name(".join(",",@fields).")";

    > - structs could do with a nice stringification.
    maybe.

    Hard to see at a glance what that stringification ought to be, really. It's possible there's something but nothing obvious comes to mind.

    > - "use struct" is *extremely* close to "use strict"

    Fair point. How about Struct::Dumb? I quite like that - it suggests "these are stupid", simple stores of data. Also has slight pun-worthy potential. :)

    > - Since these are explicitly not objects, it would be nice for the "method missing" message to say something less OOPy and to not mention "struct::impl::" at all.

    Another good point.

    ReplyDelete
  3. I like the non-OO part. Consider putting it on CPAN.

    ReplyDelete
  4. For C-compat it would be a string and you select the fields with pack/unpack. This way you can even share them with C if you get the sizes and aligns right.

    ReplyDelete
  5. you'd need to work on the namespaces. consider...

    package One;
    use struct Point => [qw( x y )];
    package Two;
    use struct Point => [qw( a b )];

    ReplyDelete
  6. And here it is at 0.01:

    http://metacpan.org/release/PEVANS/Struct-Dumb-0.01

    ReplyDelete