develooper Front page | perl.module-authors | Postings from November 2003

Re: Class::FakeAttributes -- Opinions Wanted

Thread Previous | Thread Next
From:
Smylers
Date:
November 2, 2003 15:40
Subject:
Re: Class::FakeAttributes -- Opinions Wanted
Message ID:
slrnbqb33u.pi.Smylers@stripey.com
A. Pagaltzis writes:

> * Smylers <Smylers@stripey.com> [2003-11-02 18:05]:
> 
> > However, even now I know the name for the technique I don't
> > think it'd be appropriate to call the module
> > Class::InsideOutAttributes (or similar), because that is
> > describing the module's implementation rather than the
> > situation in which it is useful.
> 
> How is "FakeAttributes" any less dependent on implementation?

Well it isn't a great name -- that's one of the reasons for posting
here, in the hope of somebody coming up with a better one!

> The attributes aren't fake, they're actual subclass attributes, only
> called "Fake" because they don't live in the $self hashref.

Exactly.  They're 'fake' in that they aren't considered part of the
object by Perl (and won't be freed by Perl automatically on
destruction).

> An actually implementation agnostic name would probably be something
> like ::SafeInheritance. (Which it isn't completely though, but see
> below.)

Possibly.  But safety isn't really the point -- Wx::StaticBoxSizer can
safely be inherited from, it's just that there isn't anywhere inside it
to store some instance data.  So the class enables instance data to be
attached to an object ('faked' as part of it), rather than adding any
safety.

> > Class::FakeAttributes helps in a different situation, where a
> > class author is subclassing something that is based on, for
> > example, a scalar ref but he/she wants to add instance data in
> > the subclass.
> 
> Yes, Abigail invented inside out objects for two reasons - one of them
> being to be able for a class to be completely superclass
> implementation agnostic. Including choice of superclass $self data
> type.

That's a good reason; that would be a reason for all Perl objects doing
all of their attributes like that (well, except when subclasses want to
access their parents data ...).

> > The methods in this class are intended to help the subclass author
> > access the attributes inside the class definition, not to provide
> > public methods for use by the people creating objects of that class.
> 
> But that loses the other advantage of inside out objects: compile time
> stricture checking.

Whether it 'loses' them depends on where you start from:

  * If you start with wanting to implement an inside-out object (or even
    with an inside-out object, or with Abigail's unreleased
    Class::MethodMakerInsideOut) then obviously using my module instead
    means you've lost an advantage of inside-out objects (but that
    wasn't its purpose).

  * But if, instead, you start with Wx::StaticBoxSizer and want to
    subclass and add some attributes then my module helps to do that --
    you can't have lost anything that you didn't have in the first
    place.

> If you write
> 
>     $attribtue{$self}
> 
> (note the typo for $attribute), Perl will complain. If you write
> 
>     $self->get_attribute('attribtue')
> 
> that's a runtime error at most.  In your module, it isn't even that
> much, because you don't ask the classes' author to declare the
> attribute names so you can't check them.

That's intended.  I'll make this as clear as I can:  My module is not
intended to be a general implementation for people who want inside-out
objects!  Anybody wanting such a module will be disappointed by trying
to use my module!

My module is intended to be used by somebody who wanted to write:

  $self->{attribute}

but can't, because of the implementation of the parent class.  That's
all my module aims for, and nothing more.  And of course anybody who
normally does $self->{attribtue} doesn't get any compile-time checking
either.

> In general I find the interface rather ill thought out; of
> course, if it did the job for you, hey presto.
> 
> But I'd prefer to do
> 
>     for(@{$self->get_attr('foo')}){ ... }          # A
> 
> rather than
> 
>     for($self->attribute_list('foo')){ ... }       # B

Believe it or not, so would I!  Because, in a 'normal' class I'd just
write:

  foreach (@{$self->{foo}}) { ... }                  # C

so merely using $self->get_attribute('foo') in place of $self->{foo} would
seem like the most natural alternative.  And that's what I did first.

The only flaw with that approach is that it doesn't work!

If the 'foo' attribute hasn't been set to anything, then you want an
empty list to iterate over.  With version C, that's what you get.  But
with version A get_attribute() returns undef, which yields an error when
trying to dereference it.  So that's why I came up with version B.

(There's equivalent reasoning behind the existence of push_attribute().)

I don't entirely like the fact that these different methods exist.  An
alternative would be to have the constructor initialize all array refs
that might ever exist in an object to [].  That way version A will
always work, because there's never an attempt to dereference undef.

Now you've brought that up, it makes sense to add it to the module docs:
if you make sure you always initialize your array attributes then
get_attribute() and set_attribute() are all you need; but if you don't
then make sure you use push_attribute() and attribute_list().  That way
it's the class-user's choice, depending on whether he/she likes
initializing things or not.

> > But if the consensus of this list is that I should remove
> > Class::FakeAttributes from Cpan then I shall do so.
> 
> So far it's one man's opinion; that hardly counts as consensus.

Well if nobody else joins in this thread then it looks like the vote is
tied, 1 each way (assuming that I count my own opinion; perhaps I
shouldn't) ... which is a little awkward for being decisive!

> Actually I just noticed it's not even inside out objects. If two
> classes in a hierarchy where one is an ancestor of the other both use
> ::FakeAttributes, they can *still* trample over each other's
> ::attributes, so you still need to know all your ancestors'
> ::attributes' names (including private attributes).

Yes.  Because that's always the case with 'ordinary', hash-ref-based
objects too.  This module is simply trying to provide a way of faking
having a hash-ref-based object when an object is actually based on
something else.  It does not attempt to provide any features whatsoever
that you would not have with a hash-ref-based object!

I think that the main problem here is that I've written a module to do X
that happens to look a little like it might do Y, but it isn't any good
at doing Y and you don't like the fact that it does Y so badly?

It isn't that I don't consider Y to be a valid problem to solve, nor
even that I'm against the existence of a module that solves Y.  It's
just that it isn't this module.

Sorry that go so long.  Does any of that make any sense?

Smylers


Thread Previous | Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About