some random remarks about Moose::Manual::Concepts

  • Thread starter Rainer Weikusat
  • Start date
R

Rainer Weikusat

The complete text is available here:

http://search.cpan.org/~doy/Moose-2.0604/lib/Moose/Manual/Concepts.pod

,----
| Attributes are not methods, but defining them causes various accessor
| methods to be created. At a minimum, a normal attribute will have a
| reader accessor method. Many attributes have other methods, such as a
| writer method, a clearer method, or a predicate method ("has it been
| set?").
`----

This idea seems to have been 'borrowed' from 'Javabeans'. But
considering that the whole point of classes is to hide their
implementation details from class users, automatically generating
a set of 'access methods' which is isomorph to the set of implementation
details seems wrong to me. Some 'attributes' (or 'properties') may
indeed need to be publically accessible but the majority of them
shouldn't: There's no conceptual difference between a 'structure'
which has only public members and a class which exposes all of its
attributes via 'getter' and 'setter' &c methods, the latter is just a
way to accomplish the same as the former with more overhead.

,----
| A method is very straightforward. Any subroutine you define in your
| class is a method. Methods correspond to verbs, and are what your
| objects can do. For example, a User can login.
`----

The 'static OO language' background is again shining through here
clearly. The people who are usually credited with inventing 'modern
OO' in form of Smalltalk didn't think of methods as 'subroutines tied
to an instance of a datastructure' but as 'messages' which can be sent
to otherwise intransparent 'objects' which may or may not react to
such a message in whatever way they chose to. The same is true for
'Perl OO' -- any method name can be used on any object instance and it
is up to the class to react to this message in whatever way it sees
fit, including that what it sees fit may change over time. Perl is
clearly not 'modern' here insofar modern is regarded as equivalent to
'try to get as much Smalltalk out of a C-compiler which can
conceivably be gotten out of it without adding intolerable overhead
for 1980s hardware' aka 'C++' and all its conceptual descendants.

,----
| A role is something that a class does. We also say that classes
| consume roles. For example, a Machine class might do the Breakable
| role, and so could a Bone class. A role is used to define some concept
| that cuts across multiple unrelated classes, like "breakability", or
| "has a color".
|
| [...]
|
|
| Role are somewhat like mixins or interfaces in other OO languages.
`----

An 'interface' is the Java-way to work around the problem that
'methods' are not 'messages' and hence, without additional declaration
hackery, instances of unrelated classes can't be used by the same code
even despite they may define other methods compatible with what this
code expects. Since 'Perl OO' is message-based and not 'compile-time
relationship' based, this kludge simply isn't needed: Any Perl code
can send any kind of message to any Perl object.

The reason why I'm refering to this as 'a kludge' is that the sole
purpose of an interface declaration is to inform the compiler that two
things which aren't different in a certain aspect, eg, when two
unrelated classes implement two identically-named methods with
identical return types and signatures (prototypes?) are really not
different in this aspect despite the compiler is not capable of/
suposed to(?) detect this on its own.

,----
| Method Modifiers
|
| Could only be done through serious symbol table wizardry, and you
| probably never saw this before (at least in Perl 5).
`----

I indeed didn't see this anywhere since I read through the CLOS
combination rules for generic methods for the last time, something I
always considered a prime example of the desire to build the universal
abstraction gone wild without any concern for good design and
practical usefulness ('good design' is supposed to refer to
"perfection is not when there's nothing more to add but when there's
nothing more to be removed").

Strangely, I also didn't miss it.

,----
| Type
|
| Hand-written parameter checking in your new() method and accessors.
|
| With Moose, you define types declaratively, and then use them by name
| with your attributes.
`----

With Perl, I write code which works when its arguments satisfy certain
constraints and leave it to the caller to meet these
constraints. I'm not usually interested in fighting my tools for
ideological reasons such as the assumption that 'strong typing' would
be a desirable property in its own right.
 
M

Marc Girod

Difficult to address 'random remarks'...
Especially I cannot do it in the context of Moose::Manual::Concepts
which could build up a consistent background: I never used Moose.

the latter is just a
way to accomplish the same as the former with more overhead.

Right, apart for making overriding possible.
This is once again a case of pay now, find why (maybe) later.
The people who are usually credited with inventing 'modern
OO' in form of Smalltalk didn't think of methods as 'subroutines

Didn't Larry Wall explain that Perl is a postmodern language?
without adding intolerable overhead
for 1980s hardware' aka 'C++' and all its conceptual descendants.

For C++ at least, the overhead is not in performance...
The reason why I'm refering to this as 'a kludge' is that the sole
purpose of an interface declaration is to inform the compiler that two
things which aren't different in a certain aspect

Er... the class declaration would have been enough for that.
Java interfaces are a kludge because their single motivation
is to compensate for the lack of multiple inheritance of
behavior, by breaking the 'everything is a class' principle.
I'm not usually interested in fighting my tools for
ideological reasons such as the assumption that 'strong typing' would
be a desirable property in its own right.

I have nothing against the way Perl works.
But I dislike this argument, maybe for 'ideological reasons'.
I take 'ideological' to be here only a derogative variant
of 'conceptual', 'abstract', 'paradigmatic'...
[ I usually reserve the word 'ideology' for justifications
of a de-facto power structure ]
Paradigms are set to validate conjectures which cannot
easily be proven, by building up a framework to produce
testable results, and address known issues.
Strong typing is one such paradigm. Even if it is not trendy
nowadays (and is remote to Perl), I believe the question of
its validity is still open.

This kind of paradigmatic thinking is imho useful, in the
same way as as esthetics or morals: it is too hard to fully
justify every one of one's moves.

In all respect for your competence, your openness, and your
bold critical spirit, Rainer.
Sorry for this out-of-scope discussion (mine, not yours).

Marc
 
R

Rainer Weikusat

Marc Girod said:
Difficult to address 'random remarks'...
Especially I cannot do it in the context of Moose::Manual::Concepts
which could build up a consistent background: I never used Moose.

Well, neither did I. This was just an attempt at a criticism of some
parts of this 'introductory text' I'm especially at odds
with, motivated by the way in which this text markets[*] 'Moose' as
must-have solution to problems the author of Moose invented in order
to solve them (or whose importance he exaggerates greatly ---
preciously few of the constructors I've written so far didn't contain
anything except a 'bless' statement and if they did [almost], this was
usually because object initializion was supposed to be performed by
some overideable method and in these cases, the 'generic' constructor
was always inherited from a base class).

Since there are some point of 'general interest' (to me, at least) in
your text, I'll try to address them despite this is somewhat off topic
here.

[*] A very striking example of marketingspeak duplicity would be the
way this text refers to symbol table manipulations. If code doing this
is written by some class author in order to sovle a real problem, this
is called 'mucking about in the symbol table' or 'symbol table
hackery' but referred to as 'serious symbol table wizardry' when it is
done in order to provide a Moose-feature.
On Mar 2, 5:07 pm, Rainer Weikusat <[email protected]> wrote:

[making all properties of an object available via 'accessor' methods']
Right, apart for making overriding possible.

If the property hadn't been exposed to begin with, overloading the
access method(s) in order to be able to change the implementation
wouldn't be necessary.

[...]

[C++ and "poor men's" message passing]
For C++ at least, the overhead is not in performance...

In C++, a 'virtual method' is one where a call is dispatched at
runtime by loading a function pointer from a certain slot of the
'virtual method table' associated with a particular class. That's
considerably less flexible than 'sending a named message with some
parameters' to an object which interprets this message by runtime
analysis: The compiler resolves the name to a vtable slot at
compilation time and all slots of all applicable vtables are also
populated at compilation time and remain constant at runtime. This
is a lot faster than any kind of 'name-based dispatching' with the
help of a runtime interpreter at the expense of tieing each method to
a class: There's no way for an unrelated object to act on the same
message,
Er... the class declaration would have been enough for that.
Java interfaces are a kludge because their single motivation
is to compensate for the lack of multiple inheritance of
behavior, by breaking the 'everything is a class' principle.

except when something like 'interface declarations' come into play:
This is a way to inform the compiler that unrelated classes do
actually have a common 'subset of message they can act on' aka
'compatible methods'. Something similar can be achieved with 'mutiple
inheritance' but only by introducing relatively awkward (IMO)
artificial abstractions. An example which came to my mind earlier
today: Both houses and cars have windows made of glass which can be
opened and closed. This would be a 'common interface' of these two
kinds of things. It could expressed with inheritance by defining
something like an 'intransparent thing with people in it who need/want
to look outside' class but (IMHO) a car is not something like a house
or vice versa, they just have some common characteristics, aka 'share
some set of messages/ methods'.

Also, the ancestors of a class are an implemenation detail of the
class (inheritance is about code reuse) and 'common behaviour of
different classes' shouldn't require them to share some part of their
implementations.
I have nothing against the way Perl works.
But I dislike this argument, maybe for 'ideological reasons'.
I take 'ideological' to be here only a derogative variant
of 'conceptual', 'abstract', 'paradigmatic'...

An 'ideology' is a philosophical system sharing the trait that its
proponents consider it an absolute, universal truth which has to be
accepted uncritically if one doesn't want to forfeit one's 'immortal
soul' with usual (monotheistic) religious systems: It is based on a
set of 'core values' which are regarded as desirable because of
themselves, not because of something like 'practical usefulness', as
in

,----
| Type
|
| Hand-written parameter checking in your new() method and accessors.
|
| With Moose, you define types declaratively, and then use them by name
| with your attributes.
`----

The possibility that someone wouldn't want to check 'the types of
arguments passed to his subroutines' doesn't even enter the equation
here: The only two 'valid' options are 'write code whose sole purpose
is to compensate for what is regared as deficiency of the language' or
'use the wonderful "declarative system" which provides all this code
for free'. But - as I already wrote - I never do that in order to
force the invocation to fail when 'someone' performed it in a way I
didn't consider sensible, only if the subroutine should actually
perform different operations based on the kind of arguments which were
passed to it. This implies that any object which understands a certain
set of messages can be used by any code which is also aware of it.
 
R

Rainer Weikusat

Ben Morrow said:
Quoth Rainer Weikusat said:
[*] A very striking example of marketingspeak duplicity would be the
way this text refers to symbol table manipulations. If code doing this
is written by some class author in order to sovle a real problem, this
is called 'mucking about in the symbol table' or 'symbol table
hackery' but referred to as 'serious symbol table wizardry' when it is
done in order to provide a Moose-feature.

You are being so overbearingly arrogant it makes my teeth hurt.
The perl symbol table is not a simple system. There are people in
the world who understand it better than you do

You have absolutely no idea of my level of 'understanding' of anything
and this 'en minature' rant has no relation whatsoever to the text I
wrote you happened to attach it to.

[...]
[making all properties of an object available via 'accessor' methods']
the latter is just a way to accomplish the same as the former with
more overhead.

Right, apart for making overriding possible.

If the property hadn't been exposed to begin with, overloading the
access method(s) in order to be able to change the implementation
wouldn't be necessary.

Attributes in Perl should in general be wrapped in accessor methods,
whether those methods are considered 'public' or 'private' (or some sort
of C++ish 'protected'), so that subclasses can change the way they
work. Every time you write $self->{foo} you are requiring that every
subclass must use a hashref-based object containing a 'foo' key with the
semantics you expect.

As I wrote in my original text: Attributes shouldn't be exposed at
all. A class should provide methods with some kind of 'useful behaviour',
both for subclassed and class users and how this behavior is actually
implemented shouldn't be anybody's business. And I meant that. That's
called 'encapsulation' and it is generally a good thing because it
decouples the interface from the implementation.

I was planning to write a more detailed reply but the lack of
understanding you've shown here, both about basic OO concepts and the
two postings I wrote, has made me lose any interest in that.
 
R

Rainer Weikusat

Ben Morrow said:
Quoth Rainer Weikusat said:
[*] A very striking example of marketingspeak duplicity would be the
way this text refers to symbol table manipulations. If code doing this
is written by some class author in order to sovle a real problem, this
is called 'mucking about in the symbol table' or 'symbol table
hackery' but referred to as 'serious symbol table wizardry' when it is
done in order to provide a Moose-feature.

You are being so overbearingly arrogant it makes my teeth hurt.
The perl symbol table is not a simple system. There are people in
the world who understand it better than you do

You have absolutely no idea of my level of 'understanding' of anything
and this 'en minature' rant has no relation whatsoever to the text I
wrote you happened to attach it to.

[...]
[making all properties of an object available via 'accessor' methods']
the latter is just a way to accomplish the same as the former with
more overhead.

Right, apart for making overriding possible.

If the property hadn't been exposed to begin with, overloading the
access method(s) in order to be able to change the implementation
wouldn't be necessary.

Attributes in Perl should in general be wrapped in accessor methods,
whether those methods are considered 'public' or 'private' (or some sort
of C++ish 'protected'), so that subclasses can change the way they
work. Every time you write $self->{foo} you are requiring that every
subclass must use a hashref-based object containing a 'foo' key with the
semantics you expect.

As I wrote in my original text: In general, attributes shouldn't be
exposed at all. A class should provide methods with some kind of
'useful behaviour', both for subclasses and class users and how this
behavior is actually implemented shouldn't be anybody's business. And
I meant that.

[...]
[C++ and "poor men's" message passing]
without adding intolerable overhead for 1980s hardware' aka 'C++'
and all its conceptual descendants.

For C++ at least, the overhead is not in performance...

In C++, a 'virtual method' is one where a call is dispatched at
runtime by loading a function pointer from a certain slot of the
'virtual method table' associated with a particular class. That's
considerably less flexible than 'sending a named message with some
parameters' to an object which interprets this message by runtime
analysis:

Yes, C++'s object model is junk. I think we all know that, and I'm not
sure what relevance it has to either Perl or Moose. (In case you were
under any misconception, Moose is not trying to be 'C++-in-Perl', it's
trying to be 'CLOS-in-Perl', approximately.)

My general impression would be that it is trying to be 'Java in Perl'
(that would be 'C++ for dummies'), with a couple of the more weird
CLOS features added in because the Moose author enjoyed being the
Seriously Supercool Symbol Table Wizard[tm]. But that's really besides
the point when discussing message passing semantics for inter-object
communication.
You are basically talking about the difference between what ObjC (and
therefore I assume Smalltalk?) calls 'formal' and 'informal protocols',
and saying that you prefer informal protocols.

I have no idea what 'Objective-C' calls a 'formal' or an 'informal'
protocol, consequently, I cannot possibly have written anything about
that. What I was writing about was the ability to send arbitrary
messages to object instances which enables unrelated objects to
provide compatible interfaces if so desired. And this really means
'unrelated objects', not

[...]
Yes. This is why I would prefer classes (or, actually, objects) to
declare their conformance or not to something equivalent to a formal
protocol as the only externally-visible way of asking 'is this a duck?'.
Java interfaces are identical to formal protocols;

objects which have an indirect relation to each other because somebody
defined a 'meta-class' (the term 'meta' is here used with a different
meaning than it is usually used for OOP) and the objects are both
'meta-instances' of the 'meta-class'. That's nothing but an
inheritance-relationship with an additional level of indirection
(losely spoken) born out of the necessity to be more flexible in this
respect than 'James Gosling originally believed to be necessary' (also
losely spoken).

Asking an object "Are you a duck?"[*], to stay with this example, is
something which shouldn't ever be necessary, and the only sensible
answer would be "Why do you care?" (I tend to answer the ritual 'Where
are you from?' question in this way precisely because my
'implementation' shouldn't be anybody's business). The object is used
in a certain context and either, it provides 'suitable behaviour' aka
'reacts in a sensible way to certain messages sent to it', than, the
final result will hopefully be something somebody considers useful and
otherwise, it will be a (usually fatal) runtime error.

[*] Assuming the answer was "I'm a value beef meal", what precisely
would that mean?

[...]
In my ideal object system there would be no classes; instead every
object would specify that it conforms to some list of protocols, and
would acquire method implementations from some list of roles, where some
of those roles might require the object implement a given protocol
before they can be included. It would also include something along the
lines of COM's QueryInterface, preferably invoked implicitly to avoid
the ridiculous verbosity of typical COM systems, so that an EntishHound
object can be treated as a Tree and get an appropriate ->bark for that
use, and then be treated as a Dog and get a different appropriate ->bark
(C< say "woofrum woofoom" >) for that use.

This looks like a somewhat informal description of a different
'general bureacratic schema to classify and organize them all' and one
which requires even more 'boilerplate declarations' than 'formally
defined' class hierarchies. I have no experience with that but
generally, the same objections: This complication isn't necessary and
I'm strongly inclined to suspect that it is rather psychologically
than technically motivated. Someone's fear of the unknown is supposed
to be dealt with by 'controlling everything upfront' (Lest the
nameless chaos will eat us all, mark my words!).

That's not how Perl-OO works and I'm quite happy with that.
The word you are looking for is 'dogma'.

'Dogma' is a religious term. AFAIK, it's the Roman-Catholic equivalent
of an axiom (and the implication that theology and mathematics are
closely related at a 'structural' level is absolutely intentional).
'Ideology' properly means something rather different, except that,
as with many philosophical terms, certain 20thC political groups
have twisted it to the point where it conveys very little beyond
'this is BAD'.

I've used it in the way it is usually used in contemporary German, eg
Marxism/ Communism would be called 'an ideology' (as would be
[Jehova!], fascism). The way 'strong typing' is usually advocated has
certain similarities to that, eg, the notion that people who disagree
with 'the idea' are not simply people with a different opinion but
'beings of lesser value' (as in 'You are being so overbearingly
arrogant it makes my teeth hurt.'). So far, the results haven't been
equally murderous but considering that people have already demanded
execution of 'global warming sceptics', this is probably rather a
problem of not being capable of physically exterminating the
unbelievers and not of not desiring to do so.
If you had actually read the damn documentation instead of jumping on
your soapbox to proclaim Stevan Little as the Antichrist

I haven't proclaimed anyone as anything, despite I've been so
overbearingly arrogant to use certain quotes from a text I read which
happened to cast a somewhat-less-than-favorable light onto the people
who wrote the text I was referring to. I didn't refer to some other
text you seem to be referring to and if the 'concepts' text I did read
doesn't describe the thing it is supposed to describe correctly, as
you're suggesting, that would be another problem with it.

[...]
The latter case is where the type system is useful. It's also useful for
catching errors early: if someone passes an X object when they
should

"... just use Ada, dammit, and stop being so arrogant to assume they
had the right to a different opinion !!!"

Strong-typing advocacy 101.

[...]
This constraint ('Any object which understands a certain set of
messages') can be easily expressed in the Moose type-constraint system,
should you desire to do so,

But I don't desire to do so because I don't buy into the theory/
ideology/ dogmatology/ you-name-it that 'strong typing' is the only or
the only True[tm] way to deal with certain aspects of 'programming
languages and environments'. That's something certain people have been
at loggerheads about since the 1970s (probably earlier) and
specifically, something some people from Europe considered to be
absolutely essential from a mostly theoretical point of view while
'some other people' from the USA built practically useful, complex
computer systems without it (or mostly without it). This constitutes
empirical evidence that it isn't really essential and hence, all the
vitriol, since humans never change their opinons on anything just
because they are/ were demonstrably wrong.

My opinion on that would be 'Could we perhaps but the matter to a rest
and focus on more practical stuff' (Did I tell you of a really great,
'weakly typed' OO system I've been using productively to solve all
kinds of 'real-world' problems for more than 15 years? No antlers
attached :).
 
R

Rainer Weikusat

Rainer Weikusat said:
[...]
Attributes in Perl should in general be wrapped in accessor methods,
whether those methods are considered 'public' or 'private' (or some sort
of C++ish 'protected'), so that subclasses can change the way they
work. Every time you write $self->{foo} you are requiring that every
subclass must use a hashref-based object containing a 'foo' key with the
semantics you expect.

As I wrote in my original text: In general, attributes shouldn't be
exposed at all. A class should provide methods with some kind of
'useful behaviour', both for subclasses and class users and how this
behavior is actually implemented shouldn't be anybody's business. And
I meant that.

This (what Ben Morrow wrote) is actually a sufficiently mind-boggling
idea that a more detailed comment seems appropriate. The first
observation about the 'every attribute access should go through an
accessor method' statement would be that this is impossible because
there's no way to implement 'an accessor method' if the accessor
method itself needs an accessor method to access the attribute. It
follows that the set of methods provided by a class must be
partitioned into two subsets, the subset of methods which access
class attributes directly and the subset of methods which don't do
that. Consequently, 'the class' is actually composed of 'the core
class' which is nothing but a 'dumb' data structure with some named
properties whose values users can change or retrieve with the help of
methods and a general-purpose subroutine library which could really
work with objects of any class that has (somewhat uselessly) been
'joined at the hip' with the 'core class' by residing in the same
package and one can conjecture that the set of 'general purpose
subroutines' joined to any particular class is usually envisioned as
empty, ie, that - conceptually - this amazing viewpoint doesn't really
regard objects as 'objects' providing some kind of useful behaviour
but as 'dumb data structures' manipulated by 'the code' as it sees fit,
with the additional possibility to change the 'physical
implementation' of this 'dumb datastructure' if this should be
regarded as useful for some reason I cannot currently imagine.

This is pretty consistent with a lot of Java code I've seen in the
last three weeks and I completely aggree that the Perl object system
is ill-suited for providing the equivalent of 'C structs' in Perl.

NB: Inconsistencies in this description might be a result of me still
trying to get my head around the implications of this. It is certainly
very much different from my idea of 'classes and objects'.
 
R

Rainer Weikusat

Ben Morrow said:
[...]
This (what Ben Morrow wrote) is actually a sufficiently mind-boggling
idea that a more detailed comment seems appropriate. The first
observation about the 'every attribute access should go through an
accessor method' statement would be that this is impossible because
there's no way to implement 'an accessor method' if the accessor
method itself needs an accessor method to access the attribute.

Obviously the accessor methods themselves need to access the attribute
directly; my point was that nothing else should.

Yes. And because of this, the 'class' is actually just a structure
composed of 'some perl object' (in the sense of scalar, array, ...)
which provides access to some set of named properties via methods and
some part really should belong to the class but does for 'weird
reasons'.

[...]
Suppose I am selling books. So I have a
whole lot of Book objects, each with 'price' and 'discount' properties,
and a method on Book

sub charge {
my ($self) = @_;

my $price = $self->{price};
my $discount = $self->{discount};

$price - POSIX::floor( ($price * $discount) / 100);
}

Now suppose some of my books are part of a standardised series, where
the pricing for each book in the series is a property of the Series the
Book belongs to rather than the Book itself. What I want to be able to
do is

package Book::InSeries;
use parent "Book";

sub price { $_[0]->series->price }
sub discount { $_[0]->series->discount }

and have the ->charge method above continue to work; as written, this is
not possible because ->charge is accessing the attributes directly.

NB: This is a contrived example but it already has some of properties
I was writing about.

The problem with this is that the 'charge' method is really not at all
related to books but a general calculation. A 'book' is just a thing
with two properties, price and discount, and 'book from a series' is
also something which has these two properties. It is merged in the
book class in order to get 'access' (in German, one would call this
'von hinten durch die Brust ins Auge', 'shot from behind through the
chest into an eye') to the charge method which is attached to book and
this seems to be the path of least resistance, given the properties of
the existing implementation.

The charge method really shouldn't be a method at all but a 'generic
function' (intentional misuse, I re-read some stuff about CLOS
yesterday) which works with any object capable of being queried for a
price and a discount and both 'individually priced book' and 'book
from a series' should be objects of different classes reacting to
'price' and 'discount' messages the charge method can work with
because interface and implementation are 'naturally separate' in
Perl-OO, anyway (especially considering the somewhat shady guy next
door who sells used handkerchiefs he acquired in some not-to-be-named
way. These also have a price and a discount and he would really like
to 're-use' the charge function, possibly without asking for
permission first ...).

I'll try to contrast this with a real example (somewhat simplified):
There's a database class whose purpose of to manage collections of
complex objects on behalf of 'some subsystem' interested in
them. These objects have properties which are really links to other
complex objects and these other objects can be updated in response to
external events. In this case, the linking object is informed that the
linked object has been updated. Depending on the object and the
property, this update may also be of interest to the database object,
eg, because the update to the linking object caused by the update of
the linked object needs to be signalled to another linking object
(possibly managed by another database, although this doesn't matter
here) or the 'object' (a subsystem aka 'classless singleton object' in
this case) which needs to perform some action affecting some part of
the outside world the database class and its instance are not aware
of, depending on where the original update came from aka 'its type'.

This works such that the linking object notifies its 'owner' (the
database) when it actually changed because of an update to a
linked object (which is not always the case). The database, in turn,
maintains a set 'actions to be performed in case of an
update of type X'. A single external event can cause multiple updates
to linked objects which may affect any number of linking objects and
the set of updated linking objects should be passed to any 'action'
routine interested in updates of this type in a single invocation,
after all 'current' events have happened. In order to do this, the
database contains a hash mapping 'update types' to 'Interest'
objects. These define two methods, one for adding a new action to the
set of actions for this update type and one for adding a new updated
objects to the set of objects updated during this 'event cycle'. When
a database is notified of an update to a contained object, it invokes
the 'add updated object' method and when a new 'update listener'
registers an interest in a kind of update, the database invokes the
'add listener' method of the corresponding interest object, possibly
creating it first. It is not anyhow concerned with that the Interest
object actually does.

The interest object maintains an array of actions and (originally) an
array of update objects. When the 'add update object' method is
called and no 'updated objects' array exists yet, one is created and a
task which will invoke all actions registered with this interest
object at some unspecified time after control returned to the
top-level event loop is scheduled. New calls to the 'add updated'
method add new objects to the updated objects array until the
scheduled task runs which 'consumes' all of them and destroys the
'updated objects' array afterwards.

As it turned out to be, the situation that a single linking object is
updated more than one time during an 'event cycle' can also occur. In
order to deal with that, a hash keeping track of which objects are
already in the updated objects array was added to the Interest
class. This (or any other rearranagement of the way the Interest
object keeps its internal data) could be accomplished without
affecting any other part of the whole mechansism because the interface
offered by the class wasn't affected by it. Instead of changing the
Interest object implementation, another option has been to subclass it
and add the 'hash lookup' part by overloading the 'add updated' method
in a way CLOS would call 'an around method', only invoking the
original 'add updated' in case the object to be added wasn't already
added. While such a hypothectical subclass could put its instance data
into a new slot of the 'general object representation' used by the
base class (an anonymous array), it could as well just store it in any
other 'convenient' way and it doesn't have (and doesn't need) any
knowledge about how the 'base class' uses its slots of the array
reference, not even what these slots 'are' (eg, their number and their
'names').

One of the problems with 'OOP' is that simple, contrived examples are
almost always useless except for demonstrating whatever they were
supposed to demonstrate while even relatively simple 'real examples',
as the one described above (certainly a lot less than 100 LOC), are
already so hideously complicated that using them as an explanation is
next to impossible :-}.
 
R

Rainer Weikusat

Ben Morrow said:
[...]
This (what Ben Morrow wrote) is actually a sufficiently mind-boggling
idea that a more detailed comment seems appropriate. The first
observation about the 'every attribute access should go through an
accessor method' statement would be that this is impossible because
there's no way to implement 'an accessor method' if the accessor
method itself needs an accessor method to access the attribute.

Obviously the accessor methods themselves need to access the attribute
directly; my point was that nothing else should.

Yes. And because of this, the 'class' is actually just a structure
composed of 'some perl object' (in the sense of scalar, array, ...)
which provides access to some set of named properties via methods and
some part really shouldn't belong to the class but does for 'weird
reasons'.

[...]
Suppose I am selling books. So I have a
whole lot of Book objects, each with 'price' and 'discount' properties,
and a method on Book

sub charge {
my ($self) = @_;

my $price = $self->{price};
my $discount = $self->{discount};

$price - POSIX::floor( ($price * $discount) / 100);
}

Now suppose some of my books are part of a standardised series, where
the pricing for each book in the series is a property of the Series the
Book belongs to rather than the Book itself. What I want to be able to
do is

package Book::InSeries;
use parent "Book";

sub price { $_[0]->series->price }
sub discount { $_[0]->series->discount }

and have the ->charge method above continue to work; as written, this is
not possible because ->charge is accessing the attributes directly.

NB: This is a contrived example but it already has some of properties
I was writing about.

The problem with this is that the 'charge' method is really not at all
related to books but a general calculation. A 'book' is just a thing
with two properties, price and discount, and 'book from a series' is
also something which has these two properties. It is merged in the
book class in order to get 'access' (in German, one would call this
'von hinten durch die Brust ins Auge', 'shot from behind through the
chest into an eye') to the charge method which is attached to book and
this seems to be the path of least resistance, given the properties of
the existing implementation.

The charge method really shouldn't be a method at all but a 'generic
function' (intentional misuse, I re-read some stuff about CLOS
yesterday) which works with any object capable of being queried for a
price and a discount and both 'individually priced book' and 'book
from a series' should be objects of different classes reacting to
'price' and 'discount' messages the charge method can work with
because interface and implementation are 'naturally separate' in
Perl-OO, anyway (especially considering the somewhat shady guy next
door who sells used handkerchiefs he acquired in some not-to-be-named
way. These also have a price and a discount and he would really like
to 're-use' the charge function, possibly without asking for
permission first ...).

I'll try to contrast this with a real example (somewhat simplified):
There's a database class whose purpose of to manage collections of
complex objects on behalf of 'some subsystem' interested in
them. These objects have properties which are really links to other
complex objects and these other objects can be updated in response to
external events. In this case, the linking object is informed that the
linked object has been updated. Depending on the object and the
property, this update may also be of interest to the database object,
eg, because the update to the linking object caused by the update of
the linked object needs to be signalled to another linking object
(possibly managed by another database, although this doesn't matter
here) or the 'object' (a subsystem aka 'classless singleton object' in
this case) which owns the database needs to perform some action
affecting some part of the outside world the database class and its
instance are not aware of, depending on where the original update came
from aka 'its type'.

This works such that the linking object notifies its 'owner' (the
database) when it actually changed because of an update to a
linked object (which is not always the case). The database, in turn,
maintains a set 'actions to be performed in case of an
update of type X'. A single external event can cause multiple updates
to linked objects which may affect any number of linking objects and
the set of updated linking objects should be passed to any 'action'
routine interested in updates of this type in a single invocation,
after all 'current' events have happened. In order to do this, the
database contains a hash mapping 'update types' to 'Interest'
objects. These define two methods, one for adding a new action to the
set of actions for this update type and one for adding a new updated
objects to the set of objects updated during this 'event cycle'. When
a database is notified of an update to a contained object, it invokes
the 'add updated object' method and when a new 'update listener'
registers an interest in a kind of update, the database invokes the
'add listener' method of the corresponding interest object, possibly
creating it first. It is not anyhow concerned with that the Interest
object actually does.

The interest object maintains an array of actions and (originally) an
array of update objects. When the 'add update object' method is
called and no 'updated objects' array exists yet, one is created and a
task which will invoke all actions registered with this interest
object at some unspecified time after control returned to the
top-level event loop is scheduled. New calls to the 'add updated'
method add new objects to the updated objects array until the
scheduled task runs which 'consumes' all of them and destroys the
'updated objects' array afterwards.

As it turned out to be, the situation that a single linking object is
updated more than one time during an 'event cycle' can also occur. In
order to deal with that, a hash keeping track of which objects are
already in the updated objects array was added to the Interest
class. This (or any other rearranagement of the way the Interest
object keeps its internal data) could be accomplished without
affecting any other part of the whole mechansism because the interface
offered by the class wasn't affected by it. Instead of changing the
Interest object implementation, another option had been to subclass it
and add the 'hash lookup' part by overloading the 'add updated' method
in a way CLOS would call 'an around method', only invoking the
original 'add updated' in case the object to be added wasn't already
added. While such a hypothectical subclass could put its instance data
into a new slot of the 'general object representation' used by the
base class (an anonymous array), it could as well just store it in any
other 'convenient' way and it doesn't have (and doesn't need) any
knowledge about how the 'base class' uses its slots of the array
reference, not even what these slots 'are' (eg, their number and their
'names').

One of the problems with 'OOP' is that simple, contrived examples are
almost always useless except for demonstrating whatever they were
supposed to demonstrate while even relatively simple 'real examples',
as the one described above (certainly a lot less than 100 LOC), are
already so hideously complicated that using them as an explanation is
next to impossible :-}.
 
R

Rainer Weikusat

NB: I'm trying to ignore sideline issues (such as 'Mindless ad hocery
is a very common OO design pattern' :) in order to keep this somewhat
focussed on 'properties of the Perl object model'.

Ben Morrow said:
Quoth Rainer Weikusat said:
Ben Morrow <[email protected]> writes:
[...]
Yes. And because of this, the 'class' is actually just a structure
composed of 'some perl object' (in the sense of scalar, array, ...)
which provides access to some set of named properties via methods and
some part really shouldn't belong to the class but does for 'weird
reasons'.

I don't understand why you seem to think methods which only call other
methods (rather than accessing attributes directly) are not part of the
'real class'. AFAIC, a class has a documented public interface; how that
interface is implemented is not relevant.

Because that's how it happens to work in Perl (or in other OO system
structured around 'message passing'): The only thing which is really
specific to 'the class' is the object representation it uses. Any of
these 'methods invoking other methods' will happily work with any
object which implements the necessary methods, no matter what its
class happens to be.

Somewhat stupid contrived example demonstrating that:
---------------
package Teabag;

sub new
{
my $weight;

$weight = $_[1];
return bless(\$weight, $_[0]);
}

sub weight
{
return ${$_[0]};
}

sub contents
{
return 'tea';
}

sub name
{
return 'teabag';
}

sub unit
{
return 'grams';
}

sub statement
{
printf("The %s contains %s %s of %s.\n",
$_[0]->name(), $_[0]->weight(), $_[0]->unit(), $_[0]->contents());
}

package Gun;

sub new
{
my $weight;

$weight = $_[1];
return bless(\$weight, $_[0]);
}

sub weight
{
return ${$_[0]};
}

sub contents
{
return 'gunpowder';
}

sub name
{
return 'gun';
}

sub unit
{
return 'pounds';
}

package main;

my $gun = Gun->new(3.5);

Teabag::statement($gun);
------------

[...]
OK, so how would you implement the 'charge' function, in Perl? As a
plain function? That works for the case where there is only one possible
charge calculation, but what if I also have other types of object with
different charging rules?

I was refering to the specific example you posted, not to any
conceivable other example you could have posted instead: The 'charge'
function has a single input, namely, 'an object', and the only
requirements for this object are 'it has to implement the price and
discount methods' and not 'it must be a book' (or its class must have
been derived from the 'Book' class). By putting this charge function
into the 'Book' package, you're actually just expressing your opinion
that it should only be used for 'Book objects' but this voluntary
reduction in 'general usefulness' is neither technically required nor
necessarily sensible (since the same 'charging rules' could be applied
to other objects-for-sale as well).

[...]
One of the things Moose makes easy is moving code like this into roles.
That means that otherwise-unrelated classes can consume the role in
order to reuse the code.

As I've shown with my 'silly example' above, otherwise-unrelated
classes can 'reuse the code' without any mechanism for doing so if the
code isn't really dependent on something which is specific to a
particular class. And the 'set of messages' objects of a class
understand isn't.

[...]
I'll try to contrast this with a real example (somewhat simplified): [...]
As it turned out to be, the situation that a single linking object is
updated more than one time during an 'event cycle' can also occur. In
order to deal with that, a hash keeping track of which objects are
already in the updated objects array was added to the Interest
class. This (or any other rearranagement of the way the Interest
object keeps its internal data) could be accomplished without
affecting any other part of the whole mechansism because the interface
offered by the class wasn't affected by it. Instead of changing the
Interest object implementation, another option had been to subclass it
and add the 'hash lookup' part by overloading the 'add updated' method
in a way CLOS would call 'an around method', only invoking the
original 'add updated' in case the object to be added wasn't already
added. While such a hypothectical subclass could put its instance data
into a new slot of the 'general object representation' used by the
base class (an anonymous array), it could as well just store it in any
other 'convenient' way and it doesn't have (and doesn't need) any
knowledge about how the 'base class' uses its slots of the array
reference, not even what these slots 'are' (eg, their number and their
'names').
[...]
One of the problems with 'OOP' is that simple, contrived examples are
almost always useless except for demonstrating whatever they were
supposed to demonstrate while even relatively simple 'real examples',
as the one described above (certainly a lot less than 100 LOC), are
already so hideously complicated that using them as an explanation is
next to impossible :-}.

I didn't entirely follow the bit I snipped, but I don't think it was
really relevant (please correct me if I'm wrong). I believe all you were
saying is that, had you chosen to subclass Interest, you could have done
so without needing to know anything about where it kept its attributes;
but you didn't explain how you would have achieved that.

That was actually a half-way ill thought out afterthought and the
'main' part of the statement was what you snipped. This was supposed
to be the smallest example of 'a real class' I could come up with which
provides 'behaviour' to its users (and whose instances need to keep
'state information' 'in some way' in order to provide this behaviour)
and not 'a panhandle to a set of named attributes'.
 
R

Rainer Weikusat

Ben Morrow said:
Quoth Rainer Weikusat said:
Ben Morrow said:
Quoth Rainer Weikusat <[email protected]>:
[...]

Yes. And because of this, the 'class' is actually just a structure
composed of 'some perl object' (in the sense of scalar, array, ...)
which provides access to some set of named properties via methods and
some part really shouldn't belong to the class but does for 'weird
reasons'.

I don't understand why you seem to think methods which only call other
methods (rather than accessing attributes directly) are not part of the
'real class'. AFAIC, a class has a documented public interface; how that
interface is implemented is not relevant.

Because that's how it happens to work in Perl (or in other OO system
structured around 'message passing'):

Perl's OO system is not particularly structured around message passing,
in the Smalltalk sense.It's actually not that easy to create an object
which accepts arbitrary messages and (say) passes them on to some other
object, especially if you want ->can and so on to work properly on the
proxy.

It is also not particularly 'structured around message passing' in the
'Royal Mail' sense and 'we' haven't even considered the possibility that
'message passing' could also refer to Mr Message fainting or dying,
just with a trivial misspelling. There are any number of possible
interpretations of any word or combination of words I wrote which make no
sense and you've ably demonstrate that you're capable of finding them.
Hmm. It sounds to me as though you are the one trying to turn objects
into fancy structs.

Because of some properties of an example you invented?
One of the points of OO is method dispatch or
polymorphism or whatever you want to call it: that you can send the same
message to different objects and they will behave differently. The only
(reasonable) ways to achieve that in Perl are to put the method
implementations directly into the class concerned, or to put them into a
superclass.

This is another point which - so far - hasn't been discussed and isn't
relevant.
Somewhat stupid contrived example demonstrating that:
---------------
package Teabag;

sub new
{
my $weight;

$weight = $_[1];
return bless(\$weight, $_[0]);
}

sub weight
{
return ${$_[0]};
}

sub contents
{
return 'tea';
}

I take it you consider these constants to be part of the internal
representation of the object?

I consider this sufficient in the context of a simple example
demonstrating what I intended to demonstrate: Subroutines which don't
do anything with objects except 'invoke some methods' can work with
any Perl object, its class notwithstanding.
[...]
The problem with this is that the 'charge' method is really not at all
related to books but a general calculation. A 'book' is just a thing
with two properties, price and discount, and 'book from a series' is
also something which has these two properties.

OK, so how would you implement the 'charge' function, in Perl? As a
plain function? That works for the case where there is only one possible
charge calculation, but what if I also have other types of object with
different charging rules?

I was refering to the specific example you posted, not to any
conceivable other example you could have posted instead:

You didn't answer the question:

The answer is irrelevant for the properties of your contrived example
I wrote about.
Now suppose I have a different situation. I have Book and Book::InSeries
objects as before, which should use this charge function:

sub charge {
my ($self, $count) = @_;

my $price = $self->price;
my $discount = $self->discount;

$count * ($price - $price * $discount);
}

In the Book case, ->price and ->discount are properties; in the InSeries
case, they are taken from the object's ->series property. I also have
Bulk objects, which should use this charge function:

sub charge {
my ($self, $count) = @_;

my $base = $self->price;
my $discount = $self->discount;
my $cutoff = $self->cutoff;

my $price = $count > $cutoff ? $base * $discount : $base;
$count * $price;
}

How would you implement these three classes? (I am genuinely interested
in your answer to this question.)

And I'm not interested in createing a string of different
explanations trying to match the speed at which you can come up with
new contrived examples which have been 'constructed' such that the
previous explanation isn't applicable to them anymore because this is
just an endless chase of a goalpost which keeps being moved.

"Wer Ohren hat, der hoere. Wer nichts hoeren will der laessts
halt". Pax Hibiscus.
 
R

Rainer Weikusat

[...]
It is also not particularly 'structured around message passing'

In case this seems a little harsh to someone: Considering that this is
the 2nd "Let's move elsewhere!" posting, I'm assuming that this is
mainly an attempt at talking circles around me in order to obscure a
point instead of a discussion intended to clarify one. Since this
cannot possibly lead anywhere, I see no reason to engage in it.
 
S

Steve May

Quoth Rainer Weikusat said:
[...]
It is also not particularly 'structured around message passing'

In case this seems a little harsh to someone: Considering that this is
the 2nd "Let's move elsewhere!" posting, I'm assuming that this is
mainly an attempt at talking circles around me in order to obscure a
point instead of a discussion intended to clarify one. Since this
cannot possibly lead anywhere, I see no reason to engage in it.

Let's recap, shall we?

- This subthread started with my assertion that, in Perl, attributes
should always be wrapped in accessor methods, since that makes
subclassing easier;

- you found this concept 'mind-boggling', since apparently you
believe that methods which only call other methods should not be
part of a class (you have yet to explain where you think they
*should* go...);

- I asked you to explain this belief;

- your explanation was that 'that's how it happens to work in Perl
(or in other OO system structured around 'message passing')';

- I pointed out that Perl's OO is not particularly structured around
message passing, at least not as I understand the term; I also
pointed out that since Perl doesn't have generics or multimethods
there isn't anywhere to put functionality related to a class of
objects *except* in the class.

I don't think I'm the one talking in circles.

Ben

Ben,

I've been following your posts in this thread and for what it's worth
you've managed to clarify a few perlish OO concepts for me, at least.

Thanks,

Steve
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,982
Messages
2,570,190
Members
46,736
Latest member
zacharyharris

Latest Threads

Top