I've gotten compiler warnings about this, but I'm not sure if this is really
prohibited by the F77 Standard or if it is merely unaddressed by the standard.
Thanks for any assistance!
: I've gotten compiler warnings about this, but I'm not sure if this is really
: prohibited by the F77 Standard or if it is merely unaddressed by the standard.
As far as I know, the standard does not prohibit this practice. Some
people will tell you it's poor programming (and there are good reasons
for this opinion), but if you know what you are doing, and what the
pitfalls are, why not? I have used logical-valued functions with
deliberate side-effects on common blocks in while-loop tests. No
compiler I have tried has complained.
________________________________________________________________________
John Wingate win...@clark.net jwi...@nswc.navy.mil
I agree and find it also usful in order to get a code easy to read in
constructions like
if( new_values_error( x, y ) ) then
call error_sub
elseif( test1ok( x, y, z1 ) ) then
v = z1 ** 2
elseif( test2ok( x, y, z2 ) ) then
v = z2
else
v = 0.0
endif
where new_values_error reads x and y and performes primarily checks and the
other functions carry out rather complex checks. Of course, test1ok must not
change the values of x or y and test2ok should not.
I also sometime find it useful to return a value by an argument as well as
by the function name as in following example.
if( func1( x, y, f1 ) .lt. 0.0 ) then
v = f1 ** 2
elseif( func2( x, y, f2 ) .lt. 0.0 ) then
v = f2 ** 3
endif
...
real function func1( x, y, f )
....
func1 = ...x...y
f = func1
end
...
If there are any serious opinions against these examples, I would be glad to
get comments on them.
Have a nice weekend!
Kurt
--
Kurt Kaellblad E-mail: kurt.k...@bkl.lth.se
Department of Building Science Phone: +46 46 222 7358
Lund Institute of Technology Fax: +46 46 222 4719
P.O.Box 118, LUND
S-221 00 Sweden
<Speaking for myself, not the department, nor ...>
However, Fortran generally doesn't require that all implementations
(compilers) execute your code the way you expect.
Further, people reading your code might tend to view procedure calls
the Fortran way rather than the C way. That is, they might see
function calls as more in a mathematical sense (no side-effects, except
possibly to speed function execution, which should generally be
transparent to the caller), and subroutine calls as more computing
oriented (focused on discrete steps in time, thus perhaps involving
side effects).
Writing functions that have side-effects isn't a sin, but I'd suggest
that designing functions whose _callers_ must know about their side-effects
is unwise in the Fortran world. (In the C world, its a necessity, though
one can use the "void" form to at least parrot Fortran's CALL, and
avoid designing functions returning more than one value that also
return something other than "void".)
Having been involved in language design issues (as a user especially)
for a few decades, I generally appreciate the more modern simplicity of
a language like C, generally ignoring semantic advantages Fortran
offers.
But as I learn more about the art of language design and code
authorship, I have more appreciation for the distinction Fortran
offers between subroutines and functions (and though that
distinction is a bit overloaded, it's not as badly so as with
other Fortran features like EXTERNAL).
For example, I've recently come to realize that good code _and_
good language design encourage the following tendency:
* The more narrowly a reader focuses on a given chunk of code,
the clearer should be the purpose of that code, and the
information most easily lost should be the implementation,
especially the optimizations used, for that code.
That is, write the code that _does_ what you want in as simple
and narrowly viewed (think small-windowed) chunk of code as
possible ("and get that to work first..." is the classic follow-
on line here, but that's not the thrust of what I'm saying here).
Include details on optimization _outside_ that narrow chunk.
Certainly most or all of our present language choices limit somewhat
the degree of success of application of the above concept. But
it's still a useful thing, I think, to strive for when writing new
code, and when designing new languages or language constructs.
(For example, it'd make VOLATILE variables "go away", to be replaced
by a clean set of CALL'ed subroutines that might always be "inlined"
to equivalent code, but presenting a clearer representation to the
reader of the code, even if that representation happens to be longer.)
So I'd rewrite your first example to:
call new_values( x, y, error )
if (error) then
call error_sub
else
call test1ok( x, y, z1, ok )
if (ok) then
v = z1 ** 2
else
call test2ok( x, y, z2, ok )
if (ok) then
v = z2
else
v = 0.0
end if
end if
end if
from your original:
if( new_values_error( x, y ) ) then
call error_sub
elseif( test1ok( x, y, z1 ) ) then
v = z1 ** 2
elseif( test2ok( x, y, z2 ) ) then
v = z2
else
v = 0.0
endif
Yes, your original is shorter, but F77 does not require it to be
compiled so it actually works (!), and further, I think my version
is easier for a Fortran programmer to grasp what is really going on
(because he expectds a subroutine call might modify one or more of
its arguments, might have side-effects, &c). This is especially
important for larger projects. (Remember, the C version of the above
would be shorter still, but that wouldn't make it better, right? :-)
The change I show follows my rule because while the new code is
larger, understanding that the procedure calls are likely to involve
side-effects does not require also viewing the code for those
procedures as is generally necessary for the "shorter" version --
making the "viewing space" needed to quickly grasp the original,
shorter version quite a bit larger. With my apparently longer
version, the reader probably needs to look at _only_ that version
to quickly realize that each subroutine call is setting more than
one variable (after all, if it was setting only one variable, the
reader might assume it would have been written as a function, no?).
So, I'd recommend avoiding the use of function invocations with
side-effects (as distinct from the writing of functions that use
side-effects to do their job). For procedure invocations that
return more than one value (where "one value" is defined by the language
you're using, e.g. in F90 it can include an entire array), make
them into subroutines, and endure the pain of CALL-ing them.
You might have to type a bit more, and the code might look a bit more
awkward, but it will generally be easier to grasp.
--
James Craig Burley, Software Craftsperson bur...@gnu.ai.mit.edu
>What does the FORTRAN 77 standard say about using the FUNCTION argument list
>to pass values back to the caller, in addition to using the function name?
Fortran 77 (and Fortran 90 as well) _does_ permit function arguments to
return values to the calling routine. However, an implementation is
explicitly permitted by the standard to make a number of optimizations,
and these permissions, in turn, imply a number of restrictions on the
sorts of things legal programs can do with the values returned by
functions [among many other restrictions, of course]. A simple summary of
all these restrictions is that it is the user's responsibility (not the
implementation's) to insure that functions have no side effects within a
given statement. For example, if F and G are functions that (re)define
their arguments, statements like,
A(I) = F(I)
or
Y = G(X) + X
are prohibited (Fortran 77, Section 6.6). Note that this is a restriction
on the programmer and not the compiler: statements like these will not, in
most cases, be diagnosed as errors.
There is one exception to the "...within a given statement" part of this
general rule: the logical expression and executable statement parts of a
logical IF are treated separately, i.e., as if the logical IF where
rewritten as a simple block IF. Thus, it is legal to write something
like,
IF ( H(X) ) Y = X
even if H redefines its argument.
One word of warning: there was a debate [starting in this forum, I think]
some months ago concerning whether some cross-statement optimizations that
would invalidate the simple summary above were, in fact, permitted by the
stanadard. I don't believe consensus was achieved on a strict
interpretation of the standard; nevertheless, I believe this summary
correctly reflects the most aggressive optimizations performed by any
commercial implementation.
HPF introduced (and the draft Fortran 95 standard includes) the notion of
a PURE function that is syntactically restricted to have _no_ side effects
at all. A function defined with the PURE attribute must have all its
arguments declared INTENT(IN).
--
Leonard J. Moss <l...@slac.stanford.edu> | My views don't necessarily
Stanford Linear Accelerator Center | reflect those of SLAC,
MS 97; P.O. Box 4349; Stanford, CA 94309 | Stanford or the DOE
Wow, what a great pun! It isn't a cos, either.
lfm
Let's not go off on a tan....
While it would be a sinh to continue this line of puns, perhaps we should end
it and return to our usual <throat-clearing sound> ... Standard of discussion.
J.
It was a sinh *somebody* was going to say that.
Joe Louderback jg...@virginia.edu
We have met the enemy and they is us. -- W. Kelly
bur...@gnu.ai.mit.edu (Craig Burley) writes:
> l...@pgroup.com (Larry Meadows) writes:
>
>> bur...@gnu.ai.mit.edu (Craig Burley) writes:
>>
>>> Writing functions that have side-effects isn't a sin, but
>>> I'd suggest
>>
>> Wow, what a great pun! It isn't a cos, either.
>
> Let's not go off on a tan....
It was a sinh *somebody* was going to say that.
I take a pretty dim view of these lame, mispelt attempts at puns.
It aint funny.
It isn't mod either.
It scores low on the index of worthwhile information.
I'm going to keep a log of all these posts!!
Then I'm going home, by way of rides in a series of cabs.
(You know, I once found a clog in a cab!)
Or I'll float home in a boat.
Maybe this is getting real boring, I don't know...
...or silly to the max...
...perhaps it's just a sign of the decline of USENET?
:-)
: bur...@gnu.ai.mit.edu (Craig Burley) writes:
: > l...@pgroup.com (Larry Meadows) writes:
: >
: >> bur...@gnu.ai.mit.edu (Craig Burley) writes:
: >>
: >>> Writing functions that have side-effects isn't a sin, but
: >>> I'd suggest
: >>
: >> Wow, what a great pun! It isn't a cos, either.
: >
: > Let's not go off on a tan....
: It was a sinh *somebody* was going to say that.
May the net police cosh you all with their trunchions
for such terrible humor!
But I guess we should exp no more. no more. Sum of us don't
know when to stop.
--
----------------------------------------------------------------------------
Stuart Norris |
Email: nor...@cfd.mech.unsw.oz.au | School of Mechanical Engineering
Work: Tel: (+61 2) 385 5738 | University of New South Wales
Fax: (+61 2) 663 1222 | Sydney 2052
Home: Tel: (+61 2) 313 0662 | Australia
----------------------------------------------------------------------------
Damn; I've mislaid my cosh.
John Harper Mathematics Dept. Victoria University Wellington New Zealand
>Kurt, and others who like using function side-effects, I think we would
>all agree that writing code can be made _easier_ by designing functions
>that way. (_Writing_ and _maintaining_ code are two different things,
>of course....)
James, first I want to thank for your follow-up. It's normally a pleasure to
read your articles. I hope all can read your full article, I will not quote
it totally, only discuss some points.
1. Readability, a part of _maintaining_.
I suggested to use functions with side-effects (in this case returning
values through arguments) as in following example:
if( new_values_error( x, y ) ) then ! read x and y
call error_sub
elseif( test1ok( x, y, z1 ) ) then ! test x and y, assign z1
v = z1 ** 2
elseif( test2ok( x, y, z2 ) ) then ! further test of x and y, assign z2
v = z2
else
v = 0.0
endif
>Writing functions that have side-effects isn't a sin, but I'd suggest
>that designing functions whose _callers_ must know about their side-
>effects is unwise in the Fortran world.
>That is, write the code that _does_ what you want in as simple
>and narrowly viewed (think small-windowed) chunk of code as possible.
>So I'd rewrite your first example to:
>
> call new_values( x, y, error )
> if (error) then
> call error_sub
> else
> call test1ok( x, y, z1, ok )
> if (ok) then
> v = z1 ** 2
> else
> call test2ok( x, y, z2, ok )
> if (ok) then
> v = z2
> else
> v = 0.0
> end if
> end if
> end if
>The change I show follows my rule because while the new code is
>larger, understanding that the procedure calls are likely to involve
>side-effects does not require also viewing the code for those
>procedures as is generally necessary for the "shorter" version --
>making the "viewing space" needed to quickly grasp the original,
>shorter version quite a bit larger. With my apparently longer
>version, the reader probably needs to look at _only_ that version
>to quickly realize that each subroutine call is setting more than
>one variable
Many times you must look into subroutines as well as functions in order
to understand the code.
>For procedure invocations that return more than one value (...), make
>them into subroutines, and endure the pain of CALL-ing them. You might
>have to type a bit more, and the code might look a bit more awkward,
>but it will generally be easier to grasp.
I'd also recommend to avoid functions with side-effects in most cases but
to use them when it is obvious that the code will be more readable. Of
course, one must remember what the F77 standard says in 6.6 about function
calls in expressions. Furthermore, you may name the functions in order to
show that there are side_effects, for example READ_AND_CHECK( x, y ) &c.
Extend my example to, let say, 20 ELSE IF-blocks, rewrite it in your way
and you will have a code segment with:
Up to 21 END IF statement after each other at the end.
Indent of up to ?? positions leaving few positions for the statements
at the deepest level, resulting in a lot of continuing lines.
This will normally *not* make it easier to grasp.
>So, I'd recommend avoiding the use of function invocations with
>side-effects (as distinct from the writing of functions that use
>side-effects to do their job).
I read this as you accept a function with side-effects if it is necessary
for the function to do its job. Three questions arise: Why not always use
subroutines if you have side-effects? What is the difference between these
functions and those I use? How can a reader in this case see that the
function has side_effects?
(A historical note: Some 20 years ago I read a Fortran textbook which
explicit declared it as a good programming style to return a functions
value even through an argument).
2. Execution of the example.
>However, Fortran generally doesn't require that all implementations
>(compilers) execute your code the way you expect.
>, but F77 does not require it to be compiled so it actually works (!),
The F77 standard says:
11.6: IF (e) THEN ... 11.6.3: ... Execution of a block IF statement causes
evaluation of the expression e. If the value of e is true, normal execution
sequence continues with the first statement of the IF-block. ... If the
value of e is false, control is transferred to the next ELSE IF, ELSE, or
END IF statement that has the same IF-level as the block IF statement.
In 11.7 a similar description is given for ELSE IF-blocks.
IMO this is rather clear and must mean, that F77 standard requires that
my code must be executed in the way I expect. If a compiler execute it in
another way due to optimization or other things - yes, that's another story.
Have a nice day!
I read this as you accept a function with side-effects if it is necessary
for the function to do its job. Three questions arise: Why not always use
subroutines if you have side-effects? What is the difference between these
functions and those I use? How can a reader in this case see that the
function has side_effects?
[Wish I had time to respond to the rest, but nothing particularly important
aside from this question as far as I can see...basically the problem is
that F77, even F90, has some restrictions in the syntactic-sugar area that
make writing certain kinds of code in C-style form, but I believe
that means you should write such code Fortran-style to make it clear
what is really going on -- using subroutine calls instead of function calls
if more than just a single value is returned, as the value of the function.
Of course, many existing libraries encourage violations of this notion!]
The difference I was talking about is between a function that is designed
to have side effects vs. one that is simply implemented to use side
effects.
The former is illustrated by your examples. Here is a (dumb?) example
of the latter:
C function to calculate factorial
INTEGER FUNCTION FACT(I)
C argument I must be >= 0, though for now 1 is returned for <= 0
INTEGER I
C HFACT is highest factorial already calculcated
C J is a DO variable
C MAXFAC is set to the number of calculated factorials to remember
C KFACTS is an array of the factorial for each index, up to HFACT
INTEGER HFACT,J,MAXFAC,KFACTS(0:MAXFAC)
PARAMETER (MAXFAC = 1000)
SAVE HFACT,KFACTS
DATA HFACT/0/,KFACTS(0)/1/
C see if we've already calculated this factorial
IF (I.LE.HFACT) THEN
IF (I.LE.0) THEN
FACT = 1
ELSE
FACT = KFACTS(I)
END IF
RETURN
END IF
C need to actually calculate the factorial
FACT = KFACTS(HFACT)
DO 10 J=HFACT+1,I
FACT = FACT * J
IF (J.LE.MAXFAC) KFACTS(J) = FACT
10 CONTINUE
IF (I.LE.MAXFAC) THEN
HFACT = I
ELSE
HFACT = MAXFAC
END IF
END
The hand-typed (and probably broken) code above is just to illustrate
a function whose _callers_ never really care that it is implemented
using side-effects; they don't see any side effects. But the function
is indeed implemented using side effects. A thoroughly optimizing
standard-conforming F77/F90 compiler cannot "break" such a function,
even if it completely removes the KFACTS array, expands it,
whatever. But a function whose callers _expect_ side effects might
indeed have some of its design elements optimized away, and a program
that depends on those side effects is generally not standard-conforming.
In summary, I think that, in Fortran, it is best to view function
calls as "calculations", independent of time-based constraints, whereas
subroutine calls are more general and involve time constraints (do this,
then do that). The above function can be viewed that way, even
though it is implemented via side-effects; functions returning values
via their arguments or via COMMON cannot be so viewed, and IMO it
is unwise to design them that way in the first place.
There seems to be some confusion here about what constitues a "side effect".
I would say that a "side effect" is only something that might be seen from
outside the routine. A function like your example, which has static storage
inside it which may be modified on any given call, does not have side effects
as far as I am concerned. As another example, consider a random number
generator which maintains an internal state. Do you call this a case of
a function with side effects?
--
Ronald Sverdlove Computational Science Research
r...@sarnoff.com David Sarnoff Research Center
Tel. 609-734-2517 CN 5300
FAX 609-734-2662 Princeton, NJ 08543-5300
Since according to the g77 info Burley is going on vacation and may miss
this I will answer instead.
Yes, a "function" with internal state has side effects, and is described as
such in the literature.
Even by your own definition, a "random" number generator has side effects.
For example, in the statement
A = random()*B + random()*C
the value of A will depend on which random() is executed first. Therefore,
the side effect is indeed seen outside the routine (otherwise why have the
side effect?). Depending on the quality of the "random" number generator,
(which of course does not generate true random numbers) the order of
execution of random() can introduce different statistical behavior in A.
Note, that the Fortran standard may allow one of the invocations of random
to be optimized away to get
A = random()*(B+C)
with entirely different statistical properties. Many compilers do not
perform such optimizations, and I believe the standard does clearly define
the order of execution of
A = random()*B
A = A + random()*C
and may clearly define
A = ((random()*B) + random()*C).
Note this is just one more example of why random number generators are
notoriously difficult to create and use properly.
Note other side effects of some "functions" that are often not recognized as
such includes I/O, or the modification of COMMON or global variables.
------------------------------------------------------------------------
William B. Clodius, wclo...@lanl.gov
NIS-1, Los Alamos National Laboratory
------------------------------------------------------------------------
> There seems to be some confusion here about what constitues a "side effect".
> I would say that a "side effect" is only something that might be seen from
> outside the routine.
... such as by calling the `function' twice and getting different
results each time, per a random number generator for example. The
comp.lang.functional FAQ says:
The term `purely functional language' is often used to describe
languages which perform all their computations via function
application. This is in contrast to languages, like Scheme and
Standard ML, which are predominantly functional but also
allow `side effects' (computational effects caused by expression
evaluation which persist after the evaluation is completed).