Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

constructors vs. subclasses?

10 views
Skip to first unread message

Ivan Shmakov

unread,
Jun 17, 2013, 11:10:12 AM6/17/13
to
(ISTR that there was a discussion of this somewhere in the
Perl's documentation, but I cannot find it right now.)

So, I'm implementing a subclass SubClass to the class Class:

package SubClass;
use base 'Class';

Then, how do I write a suitable constructor for SubClass, given
that I need to associate some additional data with the object?

Indeed, I may know that the object is a reference to a hash, so:

sub new {
my ($class, $param_my, @param_super) = @_;
my $self
= Class::new ($class, @param_super)
or die ();
$self->{"SubClass::param_my"}
= $param_my;
## .
$self;
}

However, do I understand it correctly that once the Class'
author switches to, say, a reference to a list, I'm out of my
luck?

I wonder, will it help if the Class' author provided a way for
the subclasses to associate arbitrary data with the object?
Consider, e. g.:

package Class;

sub appdata {
my $self = shift;
return ($self->{"appdata"} = shift)
if (@_);
return ($self->{"appdata"});
}

package SubClass;

## a hack: replace appdata with a "nested" version?
sub appdata {
my $self = shift;
my $myself = $self->SUPER::appdata ();
return ($myself->{"appdata"} = shift)
if (@_);
return ($myself->{"appdata"});
}

sub new {
my ($class, $param_my, @param_super) = @_;
my $self
= Class::new ($class, @param_super)
or die ();
$self->SUPER::appdata ({ "param_my" => $param_my });
## .
$self;
}

And will the hack above allow for this to be repeated for
SubSubClass (and then, if done again, further)?

TIA.

--
FSF associate member #7257

Rainer Weikusat

unread,
Jun 17, 2013, 11:19:21 AM6/17/13
to
Ivan Shmakov <onei...@gmail.com> writes:
> (ISTR that there was a discussion of this somewhere in the
> Perl's documentation, but I cannot find it right now.)
>
> So, I'm implementing a subclass SubClass to the class Class:
>
> package SubClass;
> use base 'Class';
>
> Then, how do I write a suitable constructor for SubClass, given
> that I need to associate some additional data with the object?
>
> Indeed, I may know that the object is a reference to a hash, so:
>
> sub new {
> my ($class, $param_my, @param_super) = @_;
> my $self
> = Class::new ($class, @param_super)
> or die ();
> $self->{"SubClass::param_my"}
> = $param_my;
> ## .
> $self;
> }
>
> However, do I understand it correctly that once the Class'
> author switches to, say, a reference to a list, I'm out of my
> luck?

If you want to do that regardless of the representation of the
superclass, you could use a hash indexed by refaddr($self)
(Scalar::Util::refaddr) to store additional attributes[*]. This will then
also need a destructor in order to delete the 'inside out' data when
an instance of the class is being destroyed.

[*] Since 'stringification' can be overloaded, the 'printed
representation' of a class can change at runtime.

Ivan Shmakov

unread,
Jun 17, 2013, 1:17:54 PM6/17/13
to
>>>>> Rainer Weikusat <rwei...@mssgmbh.com> writes:
>>>>> Ivan Shmakov <onei...@gmail.com> writes:

[...]

>> So, I'm implementing a subclass SubClass to the class Class:

>> package SubClass; use base 'Class';

>> Then, how do I write a suitable constructor for SubClass, given that
>> I need to associate some additional data with the object?

[...]

> If you want to do that regardless of the representation of the
> superclass, you could use a hash indexed by refaddr ($self)
> (Scalar::Util::refaddr) to store additional attributes [*]. This
> will then also need a destructor in order to delete the 'inside out'
> data when an instance of the class is being destroyed.

Indeed, that may work. Thanks!

Now, what if I'm facing this issue from the other side?
Is there a particular technique that'd allow for /my/ class to
be easily subclassed in the way described?

TIA.

> [*] Since 'stringification' can be overloaded, the 'printed
> representation' of a class can change at runtime.

Ben Morrow

unread,
Jun 17, 2013, 3:01:29 PM6/17/13
to

Quoth Rainer Weikusat <rwei...@mssgmbh.com>:
Don't use a plain hash for this job, use a fieldhash (created with
Hash::Util::Fieldhash). It will track object destruction automatically,
and will handle changing all the keys when a new thread is created in a
threaded perl.

Ben

Rainer Weikusat

unread,
Jun 17, 2013, 3:29:12 PM6/17/13
to
Ben Morrow <b...@morrow.me.uk> writes:
> Quoth Rainer Weikusat <rwei...@mssgmbh.com>:
>> Ivan Shmakov <onei...@gmail.com> writes:

[...]

>> > However, do I understand it correctly that once the Class'
>> > author switches to, say, a reference to a list, I'm out of my
>> > luck?
>>
>> If you want to do that regardless of the representation of the
>> superclass, you could use a hash indexed by refaddr($self)
>> (Scalar::Util::refaddr) to store additional attributes[*]. This will then
>> also need a destructor in order to delete the 'inside out' data when
>> an instance of the class is being destroyed.
>
> Don't use a plain hash for this job, use a fieldhash (created with
> Hash::Util::Fieldhash). It will track object destruction automatically,
> and will handle changing all the keys when a new thread is created in a
> threaded perl.

Please consider making that "I would prefer to use ... because of
...". To me, Hash::Util::Fieldhash is one of the (many) examples of
someone who felt somewhat bored and implemented some useless general
purpose library for some random something which didn't run away
quickly enough. And I generally don't care about the products of this
kind of 'recreational programming': Code should solve real problems
and not programming problems and 'abstractions' intended to do the
latter should only be introduced after very careful consideration and
if they provide significant benefits. In case of 'abstractions
provided by third party code', it is prudent to be even more careful/
reluctant wrt using them because

- they will contain bugs which will need to be dealt with at
some point in time

- they will also continue to be buggy in the 'official
version' because if the people who control the code cared
about the issue, they wouldn't be there to begin with (IOW,
I will not only need to fix the code but will be forced to
maintain a fork everthereafter)

- sooner or later, they will be changed in a
backwards-incompatible way

Ben Morrow

unread,
Jun 17, 2013, 3:51:44 PM6/17/13
to

Quoth Ivan Shmakov <onei...@gmail.com>:
> (ISTR that there was a discussion of this somewhere in the
> Perl's documentation, but I cannot find it right now.)
>
> So, I'm implementing a subclass SubClass to the class Class:
>
> package SubClass;
> use base 'Class';

base.pm is deprecated. It tries to set up data inheritance using the
also-mostly-deprecated fields pragma, which is not normally useful. Use
parent.pm instead, which just sets up inheritance, or just set @ISA
directly.

> Then, how do I write a suitable constructor for SubClass, given
> that I need to associate some additional data with the object?

This has been a perennial problem with Perl OO since the beginning, so
there are lots of different solutions. ('Problem' in the sense of
'something which requires a solution', rather than 'design flaw'; there
are times when Perl's flexibility in this regard is useful, as well as
times when the lack of an 'easy things should be easy' solution is
irritating.)

The oldest is fields.pm which I mentioned above; since the intention of
this pragma was to interact with the now-removed pseudohashes feature of
perl 5005 and 5.6, it is not a lot of use these days. Nowadays it's most
common to use one of the Class::Accessor family of modules, or Moose.

Both of these deal with the problems you mention below by enforcing some
structure on the internal representation of the object. Normally they
both implement objects as hashrefs keyed by 'attribute name', though
Moose at least is capable of handling objects with other implementations
if necessary.

My recommendation would be to just use Moose. It has a certain amount of
overhead, but for that you get an extremely powerful and flexible
system.

> Indeed, I may know that the object is a reference to a hash, so:
>
> sub new {
> my ($class, $param_my, @param_super) = @_;
> my $self
> = Class::new ($class, @param_super)
> or die ();
> $self->{"SubClass::param_my"}
> = $param_my;

I would say that you should not do this: that is, you should not access
the underlying representation directly. That should be the job of some
piece of code dedicated to that purpose, either implemented directly in
the base class or pulled in from a module. Both Class::Accessor and
Moose provide ways to create new attributes in subclasses which don't
involve playing with the underlying hash directly.

> ## .
> $self;
> }
>
> However, do I understand it correctly that once the Class'
> author switches to, say, a reference to a list, I'm out of my
> luck?

You do. The simplest answer is 'don't do that': that is, the
'subclassing interface' should be considered part of the contract a
class creates, and if it changes this should be documented as a breaking
change. Unfortunately this is often not done; fortunately, a hashref
keyed by attribute name is so much the most common class representation
that it's often possible to subclass on that assumption anyway.

> I wonder, will it help if the Class' author provided a way for
> the subclasses to associate arbitrary data with the object?
> Consider, e. g.:
>
> package Class;
>
> sub appdata {
> my $self = shift;
> return ($self->{"appdata"} = shift)
> if (@_);
> return ($self->{"appdata"});
> }
>
> package SubClass;
>
> ## a hack: replace appdata with a "nested" version?
> sub appdata {
> my $self = shift;
> my $myself = $self->SUPER::appdata ();
> return ($myself->{"appdata"} = shift)
> if (@_);
> return ($myself->{"appdata"});
> }
>
> sub new {
> my ($class, $param_my, @param_super) = @_;
> my $self
> = Class::new ($class, @param_super)

You should call this as a method: Class might inherit its ->new method.

my $self = $class->Class::new(@param_super)

You should probably also consider using SUPER or (better) next::method
instead. (SUPER is only adequate for the case of single inheritance.) Or
just use Moose...

> or die ();
> $self->SUPER::appdata ({ "param_my" => $param_my });
> ## .
> $self;
> }

It's not terribly clear to me how this is supposed to work--how do you
get at 'param_my', for instance?--but I don't think it subclasses
correctly. You've carefully kept the appdata hashes for the different
classes separate, but there's only one ->appdata method, so a method in
class Class calling ->appdata will see the same hash as one in class
SubClass.

If I were going to do something like this I might do this:

package Class;

sub appdata {
my ($self, $for) = @_;
$for //= caller;
$self->{$for} ||= {};
}

and then use that in all classes, for all attributes. So:

package Class;

sub new {
my ($class, $one, $two) = @_;
my $self = bless {}, $class;
my $data = $self->appdata;
$data->{one} = $one;
$data->{two} = $two;
$self;
}

package SubClass;

sub new {
my ($class, $one, $three) = @_;
my $self = $class->Class::new($one, "default for two");
my $data = $self->appdata;
$data->{three} = $three;
$self;
}

which gives us 'package scoped' or 'class scoped' attributes, with the
possibility of peeking at other classes' attributes if we need to.

Ben

Ben Morrow

unread,
Jun 17, 2013, 6:12:04 PM6/17/13
to

Quoth Rainer Weikusat <rwei...@mssgmbh.com>:
> Ben Morrow <b...@morrow.me.uk> writes:
> >
> > Don't use a plain hash for this job, use a fieldhash (created with
> > Hash::Util::Fieldhash). It will track object destruction automatically,
> > and will handle changing all the keys when a new thread is created in a
> > threaded perl.
>
> Please consider making that "I would prefer to use ... because of
> ...".

Please consider minding your own manners before criticising other
people's.

> To me, Hash::Util::Fieldhash is one of the (many) examples of
> someone who felt somewhat bored and implemented some useless general
> purpose library for some random something which didn't run away
> quickly enough.

Anno is not stupid (is there anyone else left who remembers when he was
a regular here?). HUF is not an academic exercise in solving a problem
because it's there to solve (which is an accusation that could
reasonably be levelled at something like Moose, no matter how useful I
believe the result may have turned out to be). It's a specific solution
to the problems inherent in implementing inside-out objects, namely:

- every class needs a destructor to clear entries in the hash, and
subclasses which override the destructor need to be careful to
ensure the base method is called;

- as with any class with a meaningful destructor, care needs to be
taken over what exactly happens during global destruction, which
is much easier to get right from C than from Perl;

- every attribute access needs to use refaddr, which adds verbosity
to what is already a rather verbose construction;

- every class needs a CLONE method to handle thread creation, which
is not particularly straighforward to get right.

> And I generally don't care about the products of this
> kind of 'recreational programming': Code should solve real problems
> and not programming problems

You really don't understand about abstraction, do you? 'Programs which
write programs are the happiest programs of all.' (Alan Perlis)

> and 'abstractions' intended to do the
> latter should only be introduced after very careful consideration and
> if they provide significant benefits. In case of 'abstractions
> provided by third party code',

HUF is not third-party code, it's a core perl feature which happens to
be exposed through a core module. In this respect it's no different from
restricted hashes (via Hash::Util), Perl's Unicode support (via Encode
and PerlIO), or any number of other features which have been added since
Perl gained module support.

Ben

Rainer Weikusat

unread,
Jun 18, 2013, 6:42:40 AM6/18/13
to
Ben Morrow <b...@morrow.me.uk> writes:
> Quoth Rainer Weikusat <rwei...@mssgmbh.com>:
>> Ben Morrow <b...@morrow.me.uk> writes:
>> >
>> > Don't use a plain hash for this job, use a fieldhash (created with
>> > Hash::Util::Fieldhash). It will track object destruction automatically,
>> > and will handle changing all the keys when a new thread is created in a
>> > threaded perl.
>>
>> Please consider making that "I would prefer to use ... because of
>> ...".
>
> Please consider minding your own manners before criticising other
> people's.

I figure that I could get at least a text page worth of derogatory
remarks you made about me (as opposed to statements criticising some
or all of my opinions) out of your postings without much
difficulty, the one above included. This may be your idea of "minding
one's manners" (presumably, everything is fine when targetting people
who "surely deserve it") but it is certainly not mine.
0 new messages