From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00 autolearn=ham autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: 109fba,c890e6ab3fb2c5fc X-Google-Attributes: gid109fba,public X-Google-Thread: 103376,c890e6ab3fb2c5fc X-Google-Attributes: gid103376,public X-Google-ArrivalTime: 1995-01-30 14:50:31 PST Newsgroups: comp.lang.ada,comp.lang.c++ Path: nntp.gmd.de!newsserver.jvnc.net!nntpserver.pppl.gov!princeton!gw1.att.com!csn!yuma!purdue!lerc.nasa.gov!magnus.acs.ohio-state.edu!math.ohio-state.edu!howland.reston.ans.net!europa.eng.gtefsd.com!ulowell.uml.edu!swlvx2!jgv From: jgv@swl.msd.ray.com (John Volan) Subject: Subject/Object Confusion Syndrome [was: Ada Objects Help] Sender: news@swlvx2.msd.ray.com (NEWS USER) Organization: Raytheon Company, Tewksbury, MA Message-ID: References: <3f9g1u$j4m@nps.navy.mil> <3fu6qc$pc5@gnat.cs.nyu.edu> <3g3uc0$hm6@watnews1.watson.ibm.com> <3g49bu$7fv@nps.navy.mil> Date: Mon, 30 Jan 1995 22:50:31 GMT Xref: nntp.gmd.de comp.lang.ada:18556 comp.lang.c++:88854 Date: 1995-01-30T22:50:31+00:00 List-Id: milod@netcom.com (John DiCamillo) writes: >ka@socrates.hr.att.com (Kenneth Almquist) writes: >>swdecato@cs.nps.navy.mil wrote: >>> Many Ada folks have demonstrated how Ada objects are created and deleted. >>> My argument against the Ada style was that I felt that the C++ syntax more >>> accurately modelled the englist language. >> >>I don't know about you, but when I issue a command in English I place the >>verb before the noun, as in "Mow the lawn." This translates naturally to >>the C or Ada code: >> >> mow(lawn); >> >>The C++ alternative, >> >> lawn->mow(); >> >>is backwards. > >Actually, your example is backwards. An imperative in English has >an implicit subject (usually 'you', that is, the person being commanded) >and an explicit object (in this case, 'lawn'). So the Ada code would >be: > > mow(robot, lawn); > >and the C++ would be: > > robot.mow(lawn); > Actually, in the first C++ example, I'd say that the lawn is the *subject* of the command, not the *object*! The trouble here is that, although C++ is deemed to be "object-oriented" -- it implements inheritance, polymorphism, dynamic binding, etc. -- nevertheless, its method-calling syntax is really *subject* oriented! Let me explain: If you want to interpret a C++ member-function call such as lawn->mow(); as an English imperative sentence, you really have to translate it as: "Hey, Mr. Lawn! Go mow yourself! (Uh, no offense. :-) (Oh, by the way, mow yourself in whatever fashion is appropriate for whatever type of lawn you are.)" As whimsical and anthropomorphic as this sounds, I seriously believe that this represents the kind of mindset you need to adopt in order to program effectively in any language that uses this kind of syntax -- e.g., C++, Smalltalk, Eiffel, etc. The fact that this sort of thing sounds so whimsical and counter-intuitive is, in my view, a serious problem, something which I call "Subject/Object Confusion Syndrome." The ironic thing is that this confusion only arises because of a choice of *programming language* syntax, yet, in my experience, it's resulted no end of difficulties for engineers trying to grasp the concepts of object-oriented *design*. Every computer program has responsibilities to perform certain actions and computations with respect to the "objects" in its problem domain. A good object-oriented design will take all the responsibilities that pertain to a given class of real-world objects and encapsulate them into a single, cohesive software module. But in order to render that into a programming language that uses this "subject-oriented" syntax, you have to imagine that the objects *themselves* are responsible for the actions that the computer has to "do" to them. Now, we all know that it's really the *computer* that's going to "do" these things -- in other words, the computer is really the "subject" of our program's imperative commands. However, because of the syntax involved, you have to pretend that it's actually the "objects" that are the "subjects" of these commands. In the example above, we know that we're really telling the computer to invoke the "mow" function, and the "lawn" object is really an implied parameter to this function (passed in through the so-called "this" pointer). This parameter has some special semantics -- its run-time type information is used to dynamically dispatch to the appropriate implementation of "mow" -- but it's still just a parameter. However, because of the syntax of the call, we're left with the impression that it's the lawn that is the subject that is doing the mowing. In my experience, this confusion between "subject" and "object" is the single most difficult conceptual hurdle you have to overcome in order to master the object-oriented paradigm, whenever you work in languages like C++ or Smalltalk. Because of this confusion, it can be difficult at times to decide where a certain responsibility should go. For instance, in deciding which object should have the responsibility to "mow", you have to keep remind yourself that the "lawn" object is not really the "lawn" itself. It's just the "part-of-the-computer-responsible-for-doing- things-to-the-lawn" -- including, perhaps, mowing it. However, it is very easy to fall into the trap of inventing spurious "functional" objects, whose only role is to "do" things to your objects of interest. For instance, it may seem perfectly reasonable to argue that lawns don't mow themselves in the real world. Don't you need someone or something to actually do the job? Like, maybe, some kind of "robot" object: > robot.mow(lawn); This may sound okay at first: A "robot" object is chunk of software that knows how to mow a lawn. When we want a lawn mowed, we'll just tell a "robot" object to do it. But in reality this design is horrible! The premise was that there are different kinds of lawn that have to be mowed in different ways. Does this "robot" object now have to know about all these different lawn types? Does it have to do some kind of switch-statement to discriminate among them? Or do we now need to have different classes of robot, one for each type of lawn? If so, then what's the point of having different lawn classes anyway? They seem to be just passive data now. The object-oriented goal of encapsulating data and processing together seems to have been lost. The answer, of course, is that this this "robot" idea just doesn't work. The "lawn" object itself is *already* the "thing-that-knows- how-to-mow-a-lawn." Contrast this with languages like CLOS or Ada95. They also implement inheritance, polymorphism, and dynamic binding, so they are also deemed "object-oriented." However, there was no attempt to adopt a "subject-oriented" method-invoking syntax. Instead, these languages took the old, tried-and-true, imperative subprogram-invoking syntax, and added whatever semantic enhancements were needed in order to support polymorphic dynamic dispatching. Consider the Ada95 example: Mow (The_Lawn); Assuming that The_Lawn is a class-wide (polymorphic) tagged-type variable, this could be translated into an English imperative sentence as follows: "Computer! Go mow the lawn! (Shades of Star Trek? :-) (Oh, by the way, mow the lawn in whatever fashion is appropriate for whatever class of lawn it is.)" Here, "the lawn" is the *object* of the sentence -- it is the thing to which the action of the sentence is directed. In the corresponding Ada code, "The_Lawn" is the "object" to which the action of the "Mow" procedure is directed. The "subject" perfoming this action is, of course, the computer itself, just as in every other imperative programming statement. In fact, you could view the exclamation "Computer!" as an implicit part of every single line of code. The only difference between this subprogram call and an "ordinary" subprogram call are semantic differences, not syntactic ones. "The_Lawn" is a special, polymorphic, "controlling" parameter. It has a run-time tag that will be used to dynamically dispatch to the appropriate implementation of Mow. IMHO, using this traditional imperative syntax is much easier to grasp than "subject-oriented" syntax. When used for dynamic dispatching, it achieves exactly the same semantics as C++'s member-function call. Yet it's much clearer what's "really" going on. There's no phantom "this" parameter that you have to know about in order to explain things. There's still the issue of object-oriented encapsulation: How do we convey the fact that the "Mow" procedure is just an integral part of a single cohesive idea known as the "Lawn" class? C++ makes it a "member function" of a class type, with the confusing effect of making it look like a structural component of every single instance object of that class. Ada95 uses packaging to do encapsulation: We can devote a package to describe the "Lawn" class, encapsulating within it everything that pertains to lawns, including an object-oriented (tagged) type and its primitive operations: package Lawn is type Object is tagged private; procedure Mow (The_Lawn : in out Lawn.Object); ... other primitive operations ("member functions" in C++) private type Object is tagged record ... -- lawn components ("member data" in C++) end record; end Lawn; So, in actuality, a call to the Mow procedure needs to include the package name as context: Lawn.Mow (The_Lawn); -- assuming The_Lawn is of type Lawn.Object'Class It's clear that this "Mow" procedure is a modular part of package Lawn, which encapsulates the *class* of Lawns. "Mow" is *not* a structural part of The_Lawn *object* that is being passed into it. Don't confuse this dotted notation with C++'s dotted member notation. "Lawn.Mow" doesn't mean that "Lawn" is some kind of subject for the verb "Mow". The "subject" of the command is still the computer. If anything, "Lawn" here could be interpreted as an *adverb* qualifying the verb "Mow". All it's indicating is the *scope* in which the Mow procedure is defined. The closest equivalent to this in C++ is using a *scope operator*: lawn->Lawn::mow(); assuming: class Lawn { public: void mow (); ... other members }; Lawn lawn; >>Not to make too big a deal about this--either order (noun before verb or >>verb before noun) is readable once you get used to it. I doubt that one >>order is inherently "better" than the other. > >Agreed. > >>However, using both orders >>in the same program seems like a bad idea if you are concerned about >>readability. > >Oh, no. Here I must disagree. The two syntaxes (in C++) represent >operations with different properties, so having a single syntax is >potentially confusing. Note: I don't believe that the amount of >confusion in either case is significant, but I am satisfied with the >C++ (and Eiffel, and Smalltalk, and...) syntax. I agree that, looking at things mechanically, syntax doesn't really matter much, because the semantics wind up being the same anyway. But I'd say that choice of syntax in a language can have a tremendous impact on how readily software engineers can internalize good object-oriented design techniques, without getting confused and falling into semantic traps. Sure, if you're a "guru," you can get used to whimsical things like lawns mowing themselves, or employees paying themselves their salaries, or enemy missiles engaging and destroying themselves. But should every object-oriented software engineer have to be a "guru" in order to be an effective designer? Well, that's just some food for thought, I don't really want to start a language war over this. Don't mow me down! :-) -- John Volan -------------------------------------------------------------------------------- Me : Person := (Name => "John G. Volan", E_Mail_Address => "jgv@swl.msd.ray.com", Employer => "Raytheon", Affiliation => "Enthusiastic member of Team-Ada!", Shameless_Controversial_Marketing_Slogan_For_Favorite_Language => "<<<< Ada95: The World's *FIRST* Internationally-Standardized OOPL >>>>" & "Inheritance, hierarchical name-space, generic templates, type-safety, " & "readability, C/C++/COBOL/FORTRAN interoperability, numeric precision, " & "multi-threading, distributed systems, real-time, safety-critical ... " & "<< A d a 9 5 : Y o u n a m e i t , i t ' s i n t h e r e >>", Humorous_Language_Lawyerly_Disclaimer => "These opinions are undefined by my employer, so using them would be " & "totally erroneous ... or would that be a bounded error? :-) "); --------------------------------------------------------------------------------