Stories
Slash Boxes
Comments
NOTE: use Perl; is on undef hiatus. You can read content, but you can't post it. More info will be forthcoming forthcomingly.

All the Perl that's Practical to Extract and Report

use Perl Log In

Log In

[ Create a new account ]

Alias (5735)

Alias
  (email not shown publicly)
http://ali.as/

Journal of Alias (5735)

Monday September 03, 2007
09:52 PM

Jonathan Rockway throws down the gauntlet on Object::Tiny

[ #34329 ]

In what I'm sure he meant to be a throwaway rhetorical question, Jonathan asks:

> Why bother when Class::Accessor::* already does the same thing?

OH IT'S SO ON! :)

So after writing some quick benchmarks (in the examples directory of the new Object::Tiny 1.03) and doing some investigations and tweaking (stole a small trick from Class::Accessor::Fast to make my accessors slightly faster), here is the answer to Jonathan's question, from the Object::Tiny docs.

------------------------------------------------------------------------------

Why bother when Class::Accessor::* already does the same thing?

As a class builder, Object::Tiny inevitably is compared to
Class::Accessor and related modules. They seem so similar, so why would
I reimplement it?

The answer is that for experienced developers that don't need or want
hand-holding, Object::Tiny is just outright better, faster or cheaper
on every single metric than Class::Accessor::Fast, which
is the most comparable member of the Class::Accessor::* family.

Object::Tiny is 93% smaller than Class::Accessor::Fast

Class::Accessor::Fast requires about 125k of memory to load.

Object::Tiny requires about 8k of memory to load.

Object::Tiny is 75% more terse to use than Class::Accessor::Fast

Using Object::Tiny requires the least possible number of keystrokes
(short of making the actual name Object::Tiny smaller).

And it requires no ugly constructor methods.

I mean really, what sort of a method name is 'mk_ro_accessors'. That sort
of thing went out of style in the early nineties.

Using Class::Accessor::Fast...

  package Foo::Bar;
  use base 'Class::Accessor::Fast';
  Foo::Bar->mk_ro_accessors(qw{ foo bar baz });

Using Object::Tiny...

  package Foo::Bar;
  use Object::Tiny qw{ foo bar baz };

You might note I've been a little generous there by reusing the class
for the method call (which is how the SYNOPSIS lists it). The alternative
commonly used by a lot of people, to avoid Repeating Yourself (which is
one of those things you Don't do) is to use the longer (in this case)
and even uglier __PACKAGE__ compiler flag.

Further, Object::Tiny lets you pass your params in directly, without
having to wrap them in an additional HASH reference that will just be
copied ANYWAY inside the constructor.

Using Class::Accessor::Fast...

  my $object = Foo::Bar->new( {
      foo => 1,
      bar => 2,
      baz => 3,
  } );

Using Object::Tiny...

  my $object = Foo::Bar->new(
      foo => 1,
      bar => 2,
      baz => 3,
  );

Object::Tiny constructors are 110% faster than Class::Accessor::Fast

Object::Tiny accessors are identical in speed to Class::Accessor::Fast
accessors, but Object::Tiny constructors are TWICE as fast as
Class::Accessor::Fast constructors, DESPITE C:A:Fast forcing you to pass
by reference (which is typically done for speed reasons).

  Benchmarking constructor plus accessors...
               Rate accessor     tiny
  accessor 100949/s       --     -45%
  tiny     182382/s      81%       --
 
  Benchmarking constructor alone...
               Rate accessor     tiny
  accessor 156470/s       --     -54%
  tiny     342231/s     119%       --
 
  Benchmarking accessors alone...
             Rate     tiny accessor
  tiny     81.0/s       --      -0%
  accessor 81.0/s       0%       --

Object::Tiny pollutes your API 95% less than Class::Accessor::Fast

Object::Tiny adds two methods to your class, new and import. The
new constructor is so trivial you can just ignore it and use your own
if you wish, and the import call will shortcut and do nothing (it
is used to implement the "use Object::Tiny qw{ foo bar baz };"
syntax itself).

So if you make your own versions of either of the two methods, you can
ignore the Object::Tiny one.

Class::Accessor::Fast isn't quite as light, adding all sorts of useless
extra methods. Worse, it adds man of them as public methods!!!

Why on earth would you want to allow your users to add more accessors to the classes they are using at run-time?

Here's what the classes used in the benchmark end up like.

    DB<1> use Class::Inspector
 
    DB<2> x Class::Inspector->methods('Foo_Bar_Tiny');
  0  ARRAY(0xfda780)
     0  'bar'
     1  'baz'
     2  'foo'
     3  'import'
     4  'new'
 
    DB<3> x Class::Inspector->methods('Foo_Bar_Accessor');
  0  ARRAY(0xfdb3c8)
     0  '_bar_accessor'
     1  '_baz_accessor'
     2  '_carp'
     3  '_croak'
     4  '_foo_accessor'
     5  '_mk_accessors'
     6  'accessor_name_for'
     7  'bar'
     8  'baz'
     9  'best_practice_accessor_name_for'
     10  'best_practice_mutator_name_for'
     11  'follow_best_practice'
     12  'foo'
     13  'get'
     14  'make_accessor'
     15  'make_ro_accessor'
     16  'make_wo_accessor'
     17  'mk_accessors'
     18  'mk_ro_accessors'
     19  'mk_wo_accessors'
     20  'mutator_name_for'
     21  'new'
     22  'set'

Object::Tiny adds 2 extra methods to your class.

Class::Accessor adds 16 extra methods, plus one more for every accessor.

Object::Tiny doesn't have the caveats of Class::Accessor::Fast

When you call use Object::Tiny qw{ foo bar baz } it isn't treated as some
sort of complete specification for the class, it's just a list of accessors
you want made for you.

So if you want to customize foo you don't need to get into contortions with
"pure" base classes or calling alternate internal methods. Just make your own
foo method and remove foo from the list passed to the use call.

Object::Tiny is more back-compatible than Class::Accessor::Fast

Class::Accessor::Fast has a minimum Perl dependency of 5.005002.

Object::Tiny has a minimum Perl dependency of 5.004.

Object::Tiny has no module dependencies whatsoever

Object::Tiny does not load ANYTHING at all outside of it's own single .pm file.

So Object::Tiny will never get confused in odd situations due to old or weird
versions of other modules (Class::Accessor::Fast has a dependency on base.pm,
which has some caveats of it's own).

--------------------------------------------------------------------

So yes, Object::Tiny kicks Class::Accessor's ass all over the interpreter.

Now personally, the question I think is more relvant is...

> If all you want is a bunch of simple accessors, why use a helper class at all?

The answer for me is convenience. And that's why Object::Tiny exists, for times when I'm lazy and in a hurry, and I just want to press less buttons to get the same thing as I would have typed anyway.

And for me, Class::Accessor doesn't deliver on that need.

The Fine Print: The following comments are owned by whoever posted them. We are not responsible for them in any way.
 Full
 Abbreviated
 Hidden
More | Login | Reply
Loading... please wait.
  • Thanks for the response :)

    BTW, s/on/an/ in my name.
  • Regrettably, it still requires a blessed hashref, but that's a small price to pay for a prototyping tool.

  • Hi, regarding

    Further, Object::Tiny lets you pass your params in directly, without having to wrap them in an additional HASH reference that will just be copied ANYWAY inside the constructor.

    and

    Object::Tiny accessors are identical in speed to Class::Accessor::Fast accessors, but Object::Tiny constructors are TWICE as fast as Class::Accessor::Fast constructors, DESPITE C:A:Fast forcing you to pass by reference (which is typically done for speed reasons).

    Speed is not the only reason, and I believe its no

    --
    life is short
    • ..the error message from perl will tell me the exact
            line number where I made the mistake, and not the line inside your
            constructor where you assign @_ to an hash (the common approach).

      That's what Perl Best Practices recommends too, I think,
      although it looks terrible.
  • Unfortunately, Object::Tiny accessors are read-only.

    So there's one little thing that Class::Accessor has over Object::Tiny. (For about 30 seconds until Adam goes and adds it.)

    Otherwise, Object::Tiny++

    • Accessors ARE read-only...

      Frankly, I don't get this obsession people have with mutators.

      90% of the time, you want to make a data object of some sort with the attributes fixed at create-time.

      90% of the time, it makes little to no sense to have values changing after the object is created.

      This sort of thing is silly.

      my $object = Foo->new;
      $object->param1('foo');
      $object->param2('bar');


      It leaves the code in a transitional state that may will be illegal.

      FAR better to just provide it to the constructor, en
      • It’s not like it’s hard to write them manually. What O::T provides is just a simple hash-bashed object after all.

      • > Accessors ARE read-only...
        > Frankly, I don't get this obsession people have with mutators.
        > 90% of the time, you want to make a data object of some sort with the attributes fixed at create-time.
        > 90% of the time, it makes little to no sense to have values changing after the object is created.

        I strongly disagree.

        Accessors are invented to get control over accessing the object attributes. From where did you derive the restriction to read-only or write-only? I have never read such res

      • Accessors are not read only. For a polymorphic system that allows transparent overloading of setter methods, writing to objects must be done via accessors too.

        I would actually to so far as to that the initialization should use the accessors too (encapsulation of validation, rocket engine startup, etc.).

        Object::Tiny should be renamed Object::Immutable::Tiny

          • Well, I follow one simple rule. I don't care about the object representation (Hashref, Arrayref, InsideOut, esoteric stuff, ...) and never access instance vars accept through accessors ... and I haven't written a simple accessor myself since 1999. So an object that doens't have setters is immutable.
  • Class::Accessor has the advantage that it doesn't abuse import() to do something other than importing just to get a more compact syntax.
    • Last time I checked though, the abuse of import is hardly considered a cardinal sin on the level of, say, adding UNIVERSAL:: methods.
    • Does it matter?

      In fact, since O::T creates accessors with the given names, it’s arguable whether it’s even abuse in the first place.

      • I find it unnecessarily confusing to use import for non-importing tasks. It's usually done only as syntactic sugar, not because it actually needs to happen at that stage. It's not a huge problem, but I appreciate the fact that modules like Test::More let's me use a more traditional alternative syntax.
            • I like the idea of generating the accessors at compile time. In that regard, is is a lot like importing.
    • To be clear, the abuse in import is the @ISA manipulation.

      Otherwise, the import is passed a list of methods and Object::Tiny creates them in the caller's namespace. That they happen to be created on the fly instead of mapped to subroutines defined in Object::Tiny doesn't really matter, in my opinion.

  • You might want to move the POD document to a separate .pod file or inline them before __END__, so it doesn't increase the memory usage.

    The document is a few kilobytes but apparently that would be a bit siginificant for your ::Tiny module if you want to claim the small memory footprint :)
    • Well, scratch this silly comment :) I remember I've heard that Perl allows a run-time access to the POD document tree in some of the past YAPCs but cant find the reference to it anymore. I guess it doesn't matter as long as you use __END__. I might be wrong again :)
      • It's probably a good idea anyway, it might save reading an extra block from the filesystem :)
  • I should comment for anyone reading this on the use.perl.org front page that I tend not to proof journal entries, because I don't expect them to end up on the front page :)

    There's a cleaned up revised version in my actual journal page.
  • Can you make a similar comparison between Object::Tiny and accessors.pm? At least this module uses the same syntax as Object::Tiny, declaring accessors in the "use" line.