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
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.
I'm convinced! (Score:1)
BTW, s/on/an/ in my name.
Re: (Score:1)
The great thing about multitasking is that several things can go wrong at once.
Looks great! (Score:2)
Regrettably, it still requires a blessed hashref, but that's a small price to pay for a prototyping tool.
Passing hashref to constructor (Score:1)
and
Speed is not the only reason, and I believe its no
life is short
Re: (Score:1)
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.
Object::Tiny accessors are read-only (Score:1)
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++
Re: (Score:1)
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
Re: (Score:1)
It’s not like it’s hard to write them manually. What O::T provides is just a simple hash-bashed object after all.
Re: (Score:1)
Re: (Score:2)
Re: (Score:1)
> 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
Re: (Score:2)
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
Re: (Score:2)
abuse of import (Score:1)
Re: (Score:1)
Re: (Score:1)
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.
Re: (Score:1)
Re: (Score:2)
Re: (Score:1)
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.
Re: (Score:1)
POD (Score:2)
The document is a few kilobytes but apparently that would be a bit siginificant for your
Re: (Score:2)
Re: (Score:1)
Spelling/etc mistakes (Score:1)
There's a cleaned up revised version in my actual journal page.
accessors.pm (Score:1)