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 
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


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 ------------------

Newsgroups: comp.lang.c++