home *** CD-ROM | disk | FTP | other *** search
- Path: sparky!uunet!mcsun!fuug!demos!kiae!glas!demos!ucc.su.oz!extro.ucc.su.OZ.AU!maxtal
- From: maxtal@extro.ucc.su.OZ.AU
- Newsgroups: comp.lang.c++
- Date: 18 Jul 92 16:04 MDT
- Subject: Re: many set/get member functions in a
- Sender: Notesfile to Usenet Gateway <notes@glas.apc.org>
- Message-ID: <1992Jul18.120442.20292@ucc.su.oz>
- References: <1992Jul08.121838.13948@jho.com>
- Nf-ID: #R:1992Jul08.121838.13948@jho.com:2043735141:1992Jul18.120442.20292@ucc.su.oz:1800623228:001:9947
- Nf-From: extro.ucc.su.OZ.AU!maxtal Jul 18 16:04:00 1992
- Lines: 279
-
-
- In article <9220019.7137@mulga.cs.mu.OZ.AU> fjh@mundil.cs.mu.OZ.AU (Fergus James HENDERSON) writes:
- >maxtal@extro.ucc.su.OZ.AU (John (MAX) Skaller) writes:
- >
- >>>Programmers are notorious for making unwarranted assumptions:
- >>
- >> Agreed. So the assumptions the programmer DOES make should
- >>be PUBLIC and ADVERTISED. It is part of the contract between
- >>the class and the user.
- >
- >I would much rather the the programmer NOT make them in the first place,
- >if this is possible.
-
- I dont understand.
-
- The assumptions about the form of a class ARE the class semantics.
- Mathematically, they ARE the axioms of the type.
-
- Having a well defined interface is only PART of the semantics.
-
- For example, an object with + and * by scalar defined is NOT
- yet a vector: you need to add the distributive law:
-
- a(b+c)=ab+ac
-
- The problem in C++ is you say about + and * but not the
- fact that there IS a distributive law. Any class derived
- from vector MUST have + and * defined, and the language
- enforces that.
-
- It also must define + and * to obey the distributive law,
- and the language CANNOT enforce that.
-
- Modern computer science would LIKE to enforce the semantics,
- but it is like the halting problem: it cant be done in general.
-
- So it is up to you the programmer to do it.
-
- In the case of C++ there is ONE case only that you can do it.
- That is where some component is unconstrained.
- A public variable of that component type GUARRANTEES that
- that it be unconstrained.
-
-
- >And get/set functions do allow that.
-
- Yes, they void the guarrantee.
-
- >> Wrong argument. IF someone wanted to change that function,
- >>I woudl sure want to know about it, because my whole
- >>program RELIES on the ability to just set and get wages.
- >
- >But I am not going to take that ability away!
-
- But you JUST DID. You can change get and set and my routines
- will still compile with yours---they just won't work.
-
- The boss will blame ME. When I find out you screwed up
- I will have your hide. It will cost you at least one shout at the pub :-)
-
- >I *do* want to be able to redefine the effects of calling get/set if the
- >specification changes.
-
- NO. If the specification changes, and you change the class
- semantics you want EVERY use of the class to FAIL TO COMPILE.
-
- I do this REGULARLY. I change the NAME of the function
- to one I haven't used before. Then I recompile everything.
- Wherever the cursor stops on the old name, I examine the
- code and context and change to the new one IF and ONLY IF
- the change is transparent.
-
- *I* have to do this because the compiler cant.
-
- Lets not get confused: changes to implementation OUGHT
- to be transparent, changes to semantics OUGHT to be opaque.
-
- In C++ you can *almost* hide implementation details.
- Unfortunately, the BEST the language can do to advertise
- semantics is to advertise the interface. That is not good enough.
- [The rest is traditionally done with comments]
- To say that is what you want is to completely misunderstand the
- purpose of compile time guarantees.
-
- The fact is you only THINK you know what you are doing.
- I often THINK I know what I am doing and the compiler tells me
- I dont. It is *almost* always right.
-
- >> This is NOT NOT NOT a change in implementation. THIS IS
- >>A CHANGE IN SEMANTICS. Conversely to your argument, the big
- >>problem in C++ (among other languages) is that this sort of
- >>tampering CANNOT be prevented. In a "real" language the semantics
- >>would be specified and your program would fail, it would
- >>dial the police and have you arrested for interfereing with the
- >>payroll.
- >
- >In any real-world program, the specifications will change which will require
- >you to change the semantics. Attempting to prevent these sort of changes
- >is counter-productive.
-
- No, the system should not prevent you changing the semantics.
- Of course not. IT SHOULD make sure that ALL the ramifications of
- the changes are examined before you make some silly half arse
- ill-considered changes. It cant actually do this of course.
-
-
- But you want the opposite: you want to be able to change
- the semantics PRIVATELY. One view of programming with classes
- is called CONTRACT programming: each class and user of the class
- makes guarrantees and in returns takes on certain
- responsibilities.
-
- That arrangement: the CONTRACT, IS the semantics.
- The contract is PUBLIC. If you go and change the effect
- of some set function you are BREAKING the contract.
- So then my code won't work. This is not allowed.
-
- Instead you must REWRITE the contract. To do that
- you must rope in and obtain all concerned. ALL the users
- of the class must agree. INCLUDING my piece of code,
- which relied on certain semantics from your class.
-
- So, if you really want to change the contract
- you better tell me. Even better, the COMPILER should tell
- me.
-
- IT DOES if that change involves a change in the
- interface (Good). It doesn't if the meaning but not
- the interface changes. (bad).
-
-
- >
- >> Why: well, how about:
- >>
- >> set_hourly_wage(double wage){hourly_wage=wage*10.0;}
- >> get_hourly_wage(){return 20* hourly_wage;}
- >>
- >>See: you can't tell me THAT isn't a change in SEMANTICS!
- >
- >Yes, but now you have broken a promise that was implicit in the class interface.
-
- Of course. Exactly. EVERY CHANGE IN MEANING NECESSARILY
- BREAKS SOME PROMISE whether implicit in the class interface
- or not.
-
- AND IN THE CASE OF PUBLIC VARIABLE THE PROMISE IS EXPLICIT
- AND THE COMPILER WILL NOT LET YOU CHANGE THE SEMANTICS.
-
- >(At least for English-speaking programmers. If the methods had been
- >void blurhf_wage(double wage) and double sqlop_wage() then the above change
- >would not be breaking any promises.)
-
- But if the VARIABL were public and called X7163 or WKhjdusjk
- the promise woudl STILL BE BROKEN AND STILL CAUGHT BY THE COMPILER.
- GET IT: THIS IS A GUARRANTEE. ONE OF THE FEW. It does not depend
- on you ability to speak English. Or to undertand someone elses
- naming convention.
- >
- >If you use public variables, then you are promising more than you need to -
- >using get/set avoids this.
-
- Usually you are right. I would not think public variables
- should be used often. More often than set/get functions,
- which IMHO are used by people whoe THINK that doing this
- makes the program object oriented and wonderful.
-
- >
- >Using a public variable prevents both benign and malevolent changes to the
- >semantics, so there is, as you say, a tradeoff here. However attempting to
- >prevent malevolent changes in the class implementation seems pointless
- >to me: no matter what you do, the class implementor can always screw you
- >right royally, so when it comes to the crunch, you have to trust them.
-
- No, you don NOT have to trust them. C++ ensures type safe
- arguments to functions, FORTRAN did not.
-
- In C++ if your change is significant enough to warrant
- an change in interface (or indeed in the private interface)
- then it is guarranteed (with a make system) to recompile
- all code and check again for compatibility.
-
- In Eiffel, if a base class has an invariant, then all the
- derived classes must obey it too. And this can be tested
- at run-time. In C++ it is a matter of trust, but not in Eiffel.
- (Of course that is not all of semantics)
-
- And one wants to protect against ALL changes in semantics:
- not against making the change, but against making the change without
- informing ALL the users of the class.
-
- >
- >No matter whether you use a public data member or get/set functions,
- >you will never be able to provide any useful guarantee of any sort about the
- >semantics of any class.
-
- Oh bull:
-
- class M2x2 { public: float a11, a12, a21, a22;}
-
- The guarrantee is that I can have any 2x2 matrix. You CANT stop
- me, EVEN in a derived class.
-
- >
- >If you assume that the class writer is of evil intent, then he/she can do
- >
- >class Matrix {
- > ...
- >public:
- > Matrix::Matrix(...) { abort(); } // Ha, ha, ha! Beat that!
- > ...
- >};
-
- class MyMatric : public Matrix {
- public:
- MyMatrix(){ // real constructor }
- };
- >
- >So what it comes down to is that the users of a class *have* to trust the
- >class implementor to some degree.
-
- Yes. But as little as possible!
-
- Why? Well, the class implementor is usually ME. I want the
- COMPILER to as much of the work as possible.
-
- >Using public variables buys you very
- >little.
-
- Agreed. It is only a small issue.
-
- But small issues demonstrate poor thinking and that leads
- to flawed designs.
-
- If you want an example, have a look at the original
- Borland Object classes. They use get and set all over the
- place, they did about everything wrong you possible could
- in C++. Why? Because they were inspired by Smalltalk classes.
- And Smalltalk is a totally different language.
- >
- >As for reasons why you might want to change get/set for a matrix class,
- >even though matrices are objects with a very precise mathematical semantics,
- >there are many:
- > - efficiency: sparse arrays, determinant caching, representing
- > diagonal matrices efficiently, etc.
-
- Yes. I already agreed with efficiency as a reason.
- >
- > - storing matrices on disk instead of in memory (if you don't have
- > enough memory, then this is more than an efficiency issue)
- > (although this one is cheating a little bit :-)
-
- No, acceptable reason.
- >
- > And now without cheating:
- > - array bounds checking
-
- Well, thats cheating a bit: you should use an instance
- of a proper array class I suppose.
-
- > - ensuring that uninitialized elements are not referenced
-
- Yes, but you might also initialise them so the case
- never arises.
-
- > - in derived class OnScreenMatrix, display the new value of the matrix
- > elements on screen whenever they are changed
- > - etc.
-
- Sure. Here you would write a 'set' function. (No get function
- is needed though). But it would not be enough: what if I use
- an assignment??
-
-
- --
- ;----------------------------------------------------------------------
- JOHN (MAX) SKALLER, maxtal@extro.ucc.su.oz.au
- Maxtal Pty Ltd, 6 MacKay St ASHFIELD, NSW 2131, AUSTRALIA
- ;--------------- SCIENTIFIC AND ENGINEERING SOFTWARE ------------------
-
-