| Home | Forums | Reviews | Guides | Newsgroups | Register | Search |
![]() |
| Thread Tools |
|
Rainer Weikusat
Guest
Posts: n/a
|
The complete text is available here:
http://search.cpan.org/~doy/Moose-2....l/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. |
|
|
|
|
|||
|
|||
| Rainer Weikusat |
|
|
|
| |
| Marc Girod |
|
|
|
| |
|
Marc Girod
Guest
Posts: n/a
|
On Mar 3, 10:39*am, Marc Girod <marc.gi...@gmail.com> wrote:
> Right, apart for making overriding possible. Sorry, I obviously meant overloading. |
|
|
|
|
|||
|
|||
| Marc Girod |
|
Rainer Weikusat
Guest
Posts: n/a
|
Marc Girod <> writes:
> 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 <rweiku...@mssgmbh.com> wrote: [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. [...] [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: 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, >> 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. 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'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'... 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. |
|
|
|
|
|||
|
|||
| Rainer Weikusat |
|
Rainer Weikusat
Guest
Posts: n/a
|
Ben Morrow <> writes:
> Quoth Rainer Weikusat <>: >> >>[*] 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. |
|
|
|
|
|||
|
|||
| Rainer Weikusat |
|
Rainer Weikusat
Guest
Posts: n/a
|
Ben Morrow <> writes:
> Quoth Rainer Weikusat <>: >> >>[*] 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. >> >> 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. >> >> 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'. > > 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 [...] >> 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. > > 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. > >> >> 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'... >> >> 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', > > 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. >> ,---- >> | 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'. > > 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. [...] >> 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. > > 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 implies that any object which understands a certain >> set of messages can be used by any code which is also aware of it. > > 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 |
|
|
|
|
|||
|
|||
| Rainer Weikusat |
|
Rainer Weikusat
Guest
Posts: n/a
|
Rainer Weikusat <> writes:
> Ben Morrow <> writes: [...] >> 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'. |
|
|
|
|
|||
|
|||
| Rainer Weikusat |
|
Rainer Weikusat
Guest
Posts: n/a
|
Ben Morrow <> writes:
> Quoth Rainer Weikusat <>: >> Rainer Weikusat <> writes: >> > Ben Morrow <> writes: >> >> 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'), [...] >> 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 :-}. |
|
|
|
|
|||
|
|||
| Rainer Weikusat |
|
Rainer Weikusat
Guest
Posts: n/a
|
Ben Morrow <> writes:
> Quoth Rainer Weikusat <>: >> Rainer Weikusat <> writes: >> > Ben Morrow <> writes: >> >> 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'), [...] >> 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 :-}. |
|
|
|
|
|||
|
|||
| Rainer Weikusat |
|
Rainer Weikusat
Guest
Posts: n/a
|
NB: I'm trying to ignore sideline issues (such as 'Mindless ad hocery
is a very common OO design pattern' focussed on 'properties of the Perl object model'. Ben Morrow <> writes: > Quoth Rainer Weikusat <>: >> Ben Morrow <> 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); ------------ [...] >> 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: 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'. |
|
|
|
|
|||
|
|||
| Rainer Weikusat |
|
|
|
| |
![]() |
| Thread Tools | |
|
|
Similar Threads
|
||||
| Thread | Thread Starter | Forum | Replies | Last Post |
| Math.random() and Math.round(Math.random()) and Math.floor(Math.random()*2) | VK | Javascript | 15 | 05-02-2010 03:43 PM |
| random.random(), random not defined!? | globalrev | Python | 4 | 04-20-2008 08:12 AM |
| Compiler warnings vs remarks mystery | gil@waves.com | C++ | 6 | 06-28-2007 08:56 AM |
| Extended ASCII characters in PIX's remarks. | AM | Cisco | 0 | 12-30-2004 08:21 AM |
| remarks in Intellisense | MattB | ASP .Net | 6 | 09-07-2004 06:07 PM |
Powered by vBulletin®. Copyright ©2000 - 2013, vBulletin Solutions, Inc..
SEO by vBSEO ©2010, Crawlability, Inc. |




