[comp.lang.eiffel] Old confusion

ian@syacus.acus.oz.au (Ian Joyner) (05/21/91)

Paulo Barreto asked the following question on an internal Unisys newsgroup,
and as he does not have access to the InterNet, I agreed to post it here
for him.

>
I'd like to know what should an Eiffel compiler do when it finds two or
more occurences of the same call preceded by "old" within an assertion.

If that call produces side effect, the values of these occurences may
be different. For instance:

class TROUBLE
export headache
feature
	aux: INTEGER;
	
	puzzle: INTEGER is
		do
			Result := aux;
			aux := aux + 1;  -- side effect!
		end; -- puzzle

	headache is
		ensure
			old puzzle = old puzzle -- what now
		end -- headache
end -- class TROUBLE
<

I already responded to Paulo, what I thought the answer was, although,
I wasn't certain. I will forward any responses to him. Thanks.

-- 
Ian Joyner                                     ACSNet: ian@syacus.oz
ACUS (Australian Centre for Unisys Software)   DNS:  ian@syacus.oz.au
Tel 61-2-390 1328      Fax 61-2-390 1391       UUCP: ...uunet!munnari!syacus.oz

tll@nntp-server.caltech.edu (Tal Lewis Lancaster) (05/22/91)

ian@syacus.acus.oz.au (Ian Joyner) writes:

>Paulo Barreto asked the following question on an internal Unisys newsgroup,
>and as he does not have access to the InterNet, I agreed to post it here
>for him.

>>
>I'd like to know what should an Eiffel compiler do when it finds two or
>more occurences of the same call preceded by "old" within an assertion.

>If that call produces side effect, the values of these occurences may
>be different. For instance:

>class TROUBLE
>export headache
>feature
>	aux: INTEGER;
>	
>	puzzle: INTEGER is
>		do
>			Result := aux;
>			aux := aux + 1;  -- side effect!
>		end; -- puzzle

>	headache is

		-- this needs some kind of body to even compile

>		ensure
>			old puzzle = old puzzle -- what now
>		end -- headache
>end -- class TROUBLE
><

>I already responded to Paulo, what I thought the answer was, although,
>I wasn't certain. I will forward any responses to him. Thanks.

First, this example is a good one for showing how to not program in
Eiffel.  It is also shows why it is bad to have functions with
side-effects.  Assuming headache was given a body and CHECK_ALL_ASSERTIONS
flag has been turn on for the class, headache would trigger an EXCEPTION
and the routine will fail when it is called.  However if the class
didn't have assertion monitoring on the ensure statement will never
be executed.

It may be interesting to note that in Version 3, the above ensure statement
is syntatically incorrect

Expression -is defined as- Standard_expression | Old_expression

Old_expression -is defined as- old Standard_expression

Before these were:

Expression -is defined as- Constant | Call | Operator_expression |
				Current | Old_value | Nochange

Old_value -is defined as- old Expression

>-- 
>Ian Joyner                                     ACSNet: ian@syacus.oz
>ACUS (Australian Centre for Unisys Software)   DNS:  ian@syacus.oz.au
>Tel 61-2-390 1328      Fax 61-2-390 1391       UUCP: ...uunet!munnari!syacus.oz


Tal Lancaster

rick@tetrauk.UUCP (Rick Jones) (05/22/91)

In article <1991May21.040742.1520@syacus.acus.oz.au> ian@syacus.acus.oz.au (Ian Joyner) writes:
>Paulo Barreto asked the following question:
>>
>I'd like to know what should an Eiffel compiler do when it finds two or
>more occurences of the same call preceded by "old" within an assertion.
>
>If that call produces side effect, the values of these occurences may
>be different. [ example deleted ]

This is really an issue of application design, and is beyond the control of the
compiler.  The Eiffel "philosophy" is that routines should be either functions
or procedures;  i.e. a routine which returns a value is a function, and should
always be written without side effects.  It can therefore be called repeatedly
without changing the state of the system.  A procedure which does change the
state should not have a return value.

The compiler can never totally enforce this, although it does produce warnings
if you call a function and don't assign its return value.  (If it is a true
function without side-effects the statement is pointless, if the statement is
not pointless the routine must change the state so it should not have a return
value.)  I must confess that I sometimes break this rule, but only to the
extent of having a procedure return a boolean to indicate whether it succeeded
or not - and such routines would have no value in as assertion statment.

The use of function calls within assertions is one particular reason for
observing this rule.  This is essential regardless of whether "old" is used,
since the presence of an assertion should never change the semantics of the
application.  If an assertion calls any function which has a side effect, then
the application will behave differently depending on whether assertions are
enabled or not.  This is clearly a situation to avoid!

I believe the compiler should be free to assume that functions do not have side
effects.  If you write functions with side effects and then use them in
assertions, you are on your own.

-- 
Rick Jones, Tetra Ltd.  Maidenhead, Berks, UK
rick@tetrauk.uucp

Any fool can provide a solution - the problem is to understand the problem

craig@leland.Stanford.EDU (Craig Chambers) (05/24/91)

In article <1166@tetrauk.UUCP>, rick@tetrauk.UUCP (Rick Jones) writes:
|> The Eiffel "philosophy" is that routines should be either functions
|> or procedures;  i.e. a routine which returns a value is a function, and should
|> always be written without side effects.  It can therefore be called repeatedly
|> without changing the state of the system.  A procedure which does change the
|> state should not have a return value.
|> 
|> The compiler can never totally enforce this, although it does produce warnings
|> if you call a function and don't assign its return value.

The compiler *could* enforce that functions have no side-effects.
According to OOSC, this restriction is not enforced because it should
be legal to make "benevolent" side-effects to the concrete state as
long as the abstract state is not affected (and it's probably
impossible in general to verify that the abstract state doesn't
change, especially since there's no specification of what the abstract
state is and how it's constructed from the concrete state).

I have two questions of Eiffel converted.

1. How often do functions make benevolent side-effects to the concrete
state that do not affect the abstract state?  If much of the system
relies on functions being side-effect-free (e.g. assertions), then
wouldn't the advantages of compiler-enforced purity of functions
outweigh the advantages of allowing benevolent side-effects?

2. Do you actually believe that functions should be side-effect-free?
My view is that side-effecting procedures (even of the abstract state)
that return a result are too useful to forgo.  For example, when I
call Stack.Pop I want to get back the element popped off the stack; I
don't want to have to call Stack.Top, save the result somewhere, and
then call Stack.Pop to remove it from the stack. Also, I believe that
somewhere in OOSC (I can't find it now) is described a technique
whereby instead of writing a side-effecting function (a procedure that
returns a result) the programmer writes a procedure that stores its
result in some instance variable which can then be accessed by a
corresponding function.  This approach seems silly; it's more verbose
for the programmer (both the class implementor and the client) and
less efficient since I assume an extra word of storage needs to be
allocated in every such object.

Perhaps an intermediate step would be to support both procedures that
can return results *and* compiler-enforced side-effect-free functions;
only the latter could be used in assertions.

-- Craig Chambers

ajk@wren.cs.rmit.OZ.AU (Alan Kent) (05/24/91)

craig@leland.Stanford.EDU (Craig Chambers) writes:
>2. Do you actually believe that functions should be side-effect-free?
...
>Perhaps an intermediate step would be to support both procedures that
>can return results *and* compiler-enforced side-effect-free functions;
>only the latter could be used in assertions.

I also like the idea of side-effect-free functions, but I need them
for a slightly different reason. What I need them for is if I write
a query evaluator to evaluate a query from a parse tree, I should be
able to write an optimizer which rearranges the tree without changing
the result of the query. This requires that all functions called are
without detectable side effects.

It also has some interesting potential for compiler optimization, although
I do not know how practical it would be.

ps: What was that OOSC thing you mentioned a few times? Sounds interesting...

Alan Kent
ajk@goanna.cs.rmit.oz.au

sakkinen@jyu.fi (Markku Sakkinen) (05/27/91)

In article <1991May23.212347.23486@leland.Stanford.EDU> craig@self.stanford.edu (Craig Chambers) writes:
>In article <1166@tetrauk.UUCP>, rick@tetrauk.UUCP (Rick Jones) writes:
>|> The Eiffel "philosophy" is that routines should be either functions
>|> or procedures;  i.e. a routine which returns a value is a function, and should
>|>  [...]
>
>The compiler *could* enforce that functions have no side-effects.
>According to OOSC, this restriction is not enforced because it should
>be legal to make "benevolent" side-effects to the concrete state as
>  [...]
>I have two questions of Eiffel converted.
>
>1. How often do functions make benevolent side-effects to the concrete
>state that do not affect the abstract state?  If much of the system
>relies on functions being side-effect-free (e.g. assertions), then
>wouldn't the advantages of compiler-enforced purity of functions
>outweigh the advantages of allowing benevolent side-effects?

No real answer, but ...
For comparison, the 'const' specifier for C++ "member functions"
(introduced in Version 2.0 or 2.1) refers to the concrete state.
Peter Grogono's language Dee (article in Structured Programming,
January 1991) follows actually the same principle;  but 'function'
means something that does not alter the 'self' object nor any of its
components, and 'procedure' may both alter them and return a result.
(This is what Craig suggested for Eiffel, too.)

Somehow the alternative chosen for Eiffel looks well in line with
other design decisions in the language, but in principle
one could have _both_ degrees of side-effect freedom available.
To go a bit further, a _true_ function should not be allowed to
have side effects anywhere (except possibly benign ones), including
parameters and global variables.  Such constraints don't exist in C++,
nor in Dee as far as I know.

>2. Do you actually believe that functions should be side-effect-free?
>My view is that side-effecting procedures (even of the abstract state)
>that return a result are too useful to forgo.  For example, when I
>call Stack.Pop I want to get back the element popped off the stack; I
>  [...]

I like the compromise solution.
Of course, "functions" are only syntactic sugar for a special case
(exactly one result to return) if procedures can have 'out' parameters.
Here the problem with Eiffel, and other languages that have adopted
Lisp-like reference/value semantics, is that you cannot use a parameter
of an _atomic_ type to return a result.  I suppose that holds also
for expanded types in current Eiffel; am I wrong?

Markku Sakkinen
Department of Computer Science and Information Systems
University of Jyvaskyla (a's with umlauts)
PL 35
SF-40351 Jyvaskyla (umlauts again)
Finland
          SAKKINEN@FINJYU.bitnet (alternative network address)

rick@tetrauk.UUCP (Rick Jones) (05/30/91)

In article <1991May23.212347.23486@leland.Stanford.EDU> craig@self.stanford.edu writes:
> [ concering functions with or without side-effects ]
>
>I have two questions of Eiffel converted.
>
>1. How often do functions make benevolent side-effects to the concrete
>state that do not affect the abstract state?  If much of the system
>relies on functions being side-effect-free (e.g. assertions), then
>wouldn't the advantages of compiler-enforced purity of functions
>outweigh the advantages of allowing benevolent side-effects?

I don't think I've ever written a function which does this in the way that OOSC
illustrates the possibility - i.e. a POINT class whose internal representation
is either cartesian or polar according to the most recent usage.  However,
there is a whole category of functions which are essential and do change the
state of the whole system, if not directly the state of the object on which
they are called.  These are functions which create and return new objects.  An
example is a user interface, where some class contains a function to generate
and return a new window.  Part of the operation is to add the new window to the
current display tree, so the total state is changed.  (You can't always do this
conveniently using direct calls to "Create", for various reasons.)

You can of course write a procedure to create the object and attach it to an
attribute, then refer to the attribute to get the result.  But if the semantics
of the class is that every caller should be guaranteed to get a new window, you
just create opportunities for error.

>2. Do you actually believe that functions should be side-effect-free?
>My view is that side-effecting procedures (even of the abstract state)
>that return a result are too useful to forgo.  For example, when I
>call Stack.Pop I want to get back the element popped off the stack; I
>don't want to have to call Stack.Top, save the result somewhere, and
>then call Stack.Pop to remove it from the stack.

This is very much a matter of personal opinion on style.  The no-side-effect
rule makes Stack.Top a repeatable call, so that if you want to refer to the top
element more than once successive calls will get the same result - i.e. you
don't need to save it anywhere at all (you might want to for efficiency
reasons, but that's another story!).  When you've finished with it, Stack.Pop
removes it.  Having a "pop" operation as the only way to access the top element
is, IMO, even worse - it means you can't get a value without changing the state
even if you want to.  Having both operations, and allowing Stack.Pop to also
return the value as a convenience is a reasonable view;  I don't have a
religious dogma on the subject.

>Also, I believe that
>somewhere in OOSC (I can't find it now) is described a technique
>whereby instead of writing a side-effecting function (a procedure that
>returns a result) the programmer writes a procedure that stores its
>result in some instance variable which can then be accessed by a
>corresponding function.  This approach seems silly; it's more verbose
>for the programmer (both the class implementor and the client) and
>less efficient since I assume an extra word of storage needs to be
>allocated in every such object.

Sometimes it's a bit silly - in particular the example I gave of returning a
booean to indicate success - but sometimes not.  It really comes down to
considering carefully the semantics of the class.  Is the result of the
procedure an intrinsic property of the object, or a transient value?  The
success/failure indication is arguably a transient condition - having the
object retain "success/failure of last procedure" may be of value, but in most
cases probably not.

The converse is that of a sequential file class, where an object represents an
open file.  One attribute of such an object is "current line".  Accessing it
should not cause it to change, and moving to the next line is done with a
procedure.  This is noticeably different from the traditional treatment of
sequential files where "get line" both reads a line and moves on to the next
one.  In fact the reason for the traditional approach has little to do with
programming techniques and a lot to do with the influence of historic hardware
(when you are controlling a paper tape reader the only way you get the next
item is to make the tape move!).

>Perhaps an intermediate step would be to support both procedures that
>can return results *and* compiler-enforced side-effect-free functions;
>only the latter could be used in assertions.

This is an interesting idea, and seems to be worth considering as an addition
to the compiler.  Do the compiler writers have any comment on this?

-- 
Rick Jones, Tetra Ltd.  Maidenhead, Berks, UK
rick@tetrauk.uucp

Any fool can provide a solution - the problem is to understand the problem

craig@leland.Stanford.EDU (Craig Chambers) (05/31/91)

In article <1170@tetrauk.UUCP>, rick@tetrauk.UUCP (Rick Jones) writes:
|> ... However,
|> there is a whole category of functions which are essential and do change the
|> state of the whole system, if not directly the state of the object on which
|> they are called.  These are functions which create and return new objects.  An
|> example is a user interface, where some class contains a function to generate
|> and return a new window.  Part of the operation is to add the new window to the
|> current display tree, so the total state is changed.  (You can't always do this
|> conveniently using direct calls to "Create", for various reasons.)

Sounds like this is very much in conflict with the rules suggested in
OOSC.  According to them, you should be able to call a function many
times over idempotently, and include them in assertions that may or
may not execute.   If your function adds a window to the screen, this
clearly is not going to follow the "rules" for functions.

I guess the answer to my original question seems to be that people
agree with the OOSC style rules when they work, but feel free to craft
other solutions when the style isn't practical.

-- Craig Chambers