Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

f90:Same name for function and subroutine?

2 views
Skip to first unread message

RHS Linux User

unread,
Feb 12, 1997, 3:00:00 AM2/12/97
to

Dear fortran 90er,
Is it possible to build a module such that BOTH the
function call and the subroutine call have the SAME name, so that

dummy=dump(a,b,c)

CALL dump(a,b,c)

are legal in the main program. In the first case a value is returned
whereas in the latter case I have no use for the returned value, and
only action dump will take place.

My setup is Linux + NAS f90 (+f77,gcc)

regards

Harry Lehto
hle...@astro.utu.fi
Tuorla Observatory

Juergen Kuersch

unread,
Feb 12, 1997, 3:00:00 AM2/12/97
to RHS Linux User

RHS Linux User wrote:
>
> Dear fortran 90er,
> Is it possible to build a module such that BOTH the
> function call and the subroutine call have the SAME name, so that
>
> dummy=dump(a,b,c)
>
> CALL dump(a,b,c)
>
> are legal in the main program. In the first case a value is returned
> whereas in the latter case I have no use for the returned value, and
> only action dump will take place.
>

Harry,

although I can't tell for sure, I do not think that THIS is possible in
Fortran 90.

However, what about a subroutine with an optional argument:

CALL dump(a,b,c,dummy)

or

CALL dump(a,b,c)

?

This is possible using F90's "OPTIONAL" attribute.

GRTX
Juergen Kuersch

----------------------------------------------------------------------------
Juergen Kuersch, mailto:k...@eecs.rwth-aachen.de
Chair of Electrical Engineering and Computer Systems
RWTH Aachen, Germany

Mike Delves

unread,
Feb 12, 1997, 3:00:00 AM2/12/97
to
RHS Linux User wrote: > Dear fortran 90er, > Is it possible to build a module such that BOTH the > function call and the subroutine call have the SAME name, so that > dummy=dump(a,b,c) > CALL dump(a,b,c) > are legal in the main program. In the first case a value is returned > whereas in the latter case I have no use for the returned value, and > only action dump will take place. > My setup is Linux + NAS f90 (+f77,gcc) > regards > Harry Lehto > hle...@astro.utu.fi > Tuorla Observatory Sorry: Fortran 90 doesnt allow this. See Section 14.1.2.3 in the Fortran95 standard, which is explicit in disallowing it. The probable reason is that Fortran does nout use the return type to distinguish between generic function instances (Ada does, but it would be real messy in Fortran to do so because Fortran, unlike Ada, will quietly convert from, eg, INT to REAL in most contexts. Now, think of a subroutine as a function returning VOID, and this is a consistent restriction. Not one that you have to like, though... Mike Delves NASoftware del...@nasoftware.co.uk http://www.nasoftware.co.uk

Dominic Olivastro

unread,
Feb 12, 1997, 3:00:00 AM2/12/97
to RHS Linux User

RHS Linux User wrote:
>
> Dear fortran 90er,
> Is it possible to build a module such that BOTH the
> function call and the subroutine call have the SAME name, so that
>
> dummy=dump(a,b,c)
>
> CALL dump(a,b,c)
>
> are legal in the main program. In the first case a value is returned
> whereas in the latter case I have no use for the returned value, and
> only action dump will take place.
>
> My setup is Linux + NAS f90 (+f77,gcc)
>
> regards
>
> Harry Lehto
> hle...@astro.utu.fi
> Tuorla Observatory


It definitely is NOT possible in F90. Surprisingly, it IS possible in
DecFortran 77, as long as the function is not a character function. And
even in the latter case, a little tweaking will make it work.

--
Dominic Olivastro CHI Research, Inc
e-mail: chiR...@ix.netcom.com fax: 1-609-546-9633
visit our web site at http://www.chiResearch.com

N8TM

unread,
Feb 12, 1997, 3:00:00 AM2/12/97
to

I would think that F90 has enough upward compatibility that we could say
this remains bad practice, not guaranteed to work, but if it does work it
will be like a C function which theoretically should return a value which
may legally be ignored. Syntax checkers ought to complain about it. The
main practical reason why it may work is that there ought to be a way to
use functions written in C.
Tim

Warner Bruns

unread,
Feb 12, 1997, 3:00:00 AM2/12/97
to

Okay, as a lot of people already said, it is not possible,
but why not replace

call something

by

dumvar= something


Warner Bruns

Craig Burley

unread,
Feb 14, 1997, 3:00:00 AM2/14/97
to

There is no reason for Fortran to have a feature to ignore the returned
value of a function.

After all, in Fortran, functions do _nothing_ other than return a value.
They explicitly are _not_ intended to be used to compute side effects.

If you want to accomplish side effects at a certain point in a Fortran
program via a procedure call, you _must_ use a subroutine call to be
safe (and standard-conforming):

CALL DO_SOMETHING(args)

If you want to retrieve a single return value (or, in F90, array) as
part of performing these side effects, append an INTENT(OUT) (output)
argument as appropriate (which may be OPTIONAL in F90):

CALL DO_SOMETHING(args,RTNVAL)

If you write a function invocation, you _are_ saying "I don't care if
the function's side effects, if any, are actually performed -- I care
only about the return value".

Therefore, anyplace you want to do

IGNORE_ME = SOME_FUNCTION(...)

(where IGNORE_ME is an otherwise ignored variable), you can simply replace
it with

CONTINUE

and be assured that (assuming your program used to be standard-conforming)
your program will continue to work properly.

So, if you want to use functions written in C, invoke them in the proper
Fortran way -- call them as subroutines. Ask your vendors to provide
the proper interface magic to make this work easily.


It is too bad the C mind-set long-ago infected the Fortran programming
community with the notion that a function is necessarily imperative
procedure (series of steps) that conveniently returns a value that may
be conveniently ignored. This mind-set infection seems to have been
largely forwarded by popular (but strictly broken) implementations of
libU77, e.g. those offering FGETC and FPUTC as functions.

Fact is, C does not have functions. It has only imperative subroutines,
with a syntactically convenient way to express a single output argument
that may be omitted by the caller. This is one of the weaknesses of C
as compared to Fortran, which has C's imperative subroutines in the form
of CALL and SUBROUTINE, though lacking (to a greater degree in FORTRAN 77
than Fortran 90) the syntactic convenience of expressing a single, optional,
output argument.

Fortunately, the Fortran standards committee seems to have been unaffected
by all this. Witness the new intrinsics added in F90 -- the ones that
necessarily perform important side effects are intrinsic _subroutines_,
including even RANDOM_NUMBER, a subroutine that has a single _output_
argument.

So, exercise for the reader, why is RANDOM_NUMBER in F90 expressed as

CALL RANDOM_NUMBER(VAL)

instead of

VAL = RANDOM_NUMBER()

?? Because the latter form could be legitimately implemented as returning
the "next" pseudo-random number _without_ internally advancing the generator
itself for the next invocation. E.g. an optimizing compiler would be well
within its rights to implement

VAL = RANDOM_NUMBER() + RANDOM_NUMBER()

as

VAL = 2 * RANDOM_NUMBER()

or similar.

The real danger comes when C-oriented programmers write Fortran code like

IF (IS_AN_EMPTY_FILE ('FOO') .AND. DELETE_FILE ('FOO')) ...

expecting that DELETE_FILE will happen only if IS_AN_EMPTY_FILE returns
.TRUE.. Assuming that these functions do what their names suggest in a UNIX
environment (test whether a file exists but has no data, and delete a
file, respectively), a valid Fortran implementation can decide to implement
the above in the following ways (among others):

- Invoke the DELETE_FILE function first, then IS_AN_EMPTY_FILE.

- Invoke IS_AN_EMPTY_FILE but never invoke DELETE_FILE (if that
function has a "known" return value).

- Invoke DELETE_FILE, but never invoke IS_AN_EMPTY_FILE (since the
implementation could be smart enough to realize that if the file
is actually deleted, then it won't be an empty file).

- Do nothing, evaluate as .FALSE..


Fortran's weakness in this area? The fact that it has no way to specify a
function that returns an arbitrary list of values, which explains why
DATE_AND_TIME and SYSTEM_CLOCK, procedures that return gobs of data, are
subroutines instead of functions. (Though they could be said to perform
side effects, these effects are performed as a necessary component of
returning the values -- if the values are known without performing the
effect, then no code will suffer if the effect is not performed.)


I'm also fairly impressed that MIL-STD 1753 was well-designed in the sense
that MVBITS was made a _subroutine_ instead of a function since, again,
it includes a side effect as an important component of its (imperative)
design. (C-oriented programmers might well have made MVBITS a function
with an important side effect.)

Still, either MIL-STD 1753 or F90 (in codifying 1753) could have perhaps
offered a better interface than MVBITS, such as:

OUT = MOVED_BITS(IN,INPOS,LEN,MODIFY,OUTPOS)

That is,

CALL MVBITS(FROM,FROMPOS,LEN,TO,TOPOS)

would look like this instead:

TO = MOVED_BITS(FROM,FROMPOS,LEN,TO,TOPOS)

This would give programmers a bit more flexibility in expressing usage
of the result of moving bits around, e.g. just putting them in a temporary
for comparison, etc. (Obviously 1753 wouldn't have chosen the name
`MOVED_BITS'.)

What's really frustrating is how intent some supposed Fortran enthusiasts
are on dumbing down Fortran (e.g. by turning its functions into
imperative subroutines) just to achieve some of the (dubious) syntactic
convenience of C.

Remember, a good language is not defined solely by the expressive range
of its syntactic constructs -- much of what is worthwhile about a good
language (as a tool of communication) is the power its syntactic constructs
have to express _limitations_ on range of meaning, without needing
additional context.

When a change is made to an existing language that removes useful limitations
on the range of meaning of syntactic constructs, the language is not
improved -- its communicative power is diminished. Fortran 90 diminished
Fortran by adding CONTAINS at the end of the executable statements of a
program unit; C++ diminished C by adding references. In both cases, existing
constructs (such as "R = SIN(S)" in Fortran or "foo(a, b, c);" in C)
suddenly communicated substantially less useful information to their readers
than they did prior to adding these features in these forms.

(Specifically, Fortran programmers can no longer assume "R = SIN(S)"
invokes the SIN intrinsic unless some other specific declaration or use
of SIN _precedes_ that statement; C programmers can no longer assume
"foo(a, b, c);" cannot directly modify the variables a, b, or c.)
--

"Practice random senselessness and act kind of beautiful."
James Craig Burley, Software Craftsperson bur...@gnu.ai.mit.edu

James Giles

unread,
Feb 14, 1997, 3:00:00 AM2/14/97
to Craig Burley

Craig Burley wrote:
[...]

>
> After all, in Fortran, functions do _nothing_ other than return a value.
> They explicitly are _not_ intended to be used to compute side effects.

The Fortran 90 standard document explicitly permits functions to have
side effects. See section 7.1.7 (ISO/IEC 1539:1991(E)). The only
constraint is that a reference to the function is not permitted in a
statement that otherwise refers to the objects being modified by those
side effects.

The HPF standard introduces the concept of a PURE function in which such
side effects are prohibited.

The main problem with side effects is that the standard permits an
implementation to optimize-out function calls if they are not needed to
compute the result of an expreession:

IF (COND .and. F(X)) THEN

When the value of COND is false, the value of F is not needed. So the
compiler is free not to evaluate it. In this case, the variable X is
considered undefined if it is an INTENT(INOUT) or INTENT(OUT) argument -
since F could have been evaluated, but wasn't necessarily. I'm sure we
all hope that in a real implementation X will either still have its
former value or will have one consistent with a complete evaluation of
F, rather that really becoming completely undefined.

The Fortran intrinsics are all defined as PURE functions (in the HPF
sense). Perhaps this is in anticipation of making PURE available in
future standards - and making it default even further in the future. It
certainly makes optimization of code using intrinsics more convenient.

--
J. Giles
Ricercar Software

Toon Moene

unread,
Feb 14, 1997, 3:00:00 AM2/14/97
to

Craig Burley <bur...@gnu.ai.mit.edu> wrote:

>After all, in Fortran, functions do _nothing_ other than return a value.
>They explicitly are _not_ intended to be used to compute side effects.

Craig, I do not believe this. How else would you explain the Fortran 95's
efforts to define "PURE FUNCTION" ?

If it really is "verboten" to have (visible-from-the-outside) side effects in
functions, could someone-in-the-know cite chapter and verse of the applicable
standards in this respect ...

--
Toon Moene (mailto:to...@moene.indiv.nluug.nl)
Saturnushof 14, 3738 XG Maartensdijk, The Netherlands
Phone: +31 346 214290; Fax: +31 346 214286
g77 Support: mailto:for...@gnu.ai.mit.edu; NWP: http://www.knmi.nl/hirlam

Kenneth Plotkin

unread,
Feb 14, 1997, 3:00:00 AM2/14/97
to

In article <3304BE...@cris.com>, James Giles <JGi...@cris.com> wrote:

[snip]


>sense). Perhaps this is in anticipation of making PURE available in
>future standards - and making it default even further in the future. It
>certainly makes optimization of code using intrinsics more convenient.

As a Fortran user, and sometimes re-user of other people's Fortran, I wish
functions had been pure from the start. Years ago the style was generally
to not have side effects in functions; that's how I was taught. The first
time I saw side effects in action was when a collegue who was a
professional programmer spent a week going nuts before he realized that
the program's original author made occasional use of side effects.

Nowadays, though, it's pretty common for a function to be used like a
subroutine, with the function value itself used to report status. With
the djinn out of the bottle, it'll be hard to go back to pure functions.

Ken Plotkin

William Clodius

unread,
Feb 14, 1997, 3:00:00 AM2/14/97
to

Toon Moene wrote:

>
> Craig Burley <bur...@gnu.ai.mit.edu> wrote:
>
> >After all, in Fortran, functions do _nothing_ other than return a value.
> >They explicitly are _not_ intended to be used to compute side effects.
>
> Craig, I do not believe this. How else would you explain the Fortran 95's
> efforts to define "PURE FUNCTION" ?
>
> If it really is "verboten" to have (visible-from-the-outside) side effects in
> functions, could someone-in-the-know cite chapter and verse of the applicable
> standards in this respect ...
>
> <snip>

There are undoubtably several points in the standard which imply, not
that it is verboten, but that it is VERY dangerous to rely on side
effects in a function as their order and even their occurance at all can
be almost arbitrarilly changed by optimization. These points appear at
several points in the standard, because side effects can take several
forms, modification of arguments, common blocks, SAVEd local entities,
I/O, etc.. (Note a side effect that is not visible from the outside is
useless). The best known such points in the Fortran 77 standard (ANSI
X3.9-1978 Fortran 77) are probably

"6.6.2 Order of Evaluation of Functions. If a statement contains more
than one function reference, a processor may evaluate the functions in
any order, except for a logical IF statement and a function argument
list containing function references. ..."

=> within a statement (with the exception of nested function calls, e.g.
F(G(X)) requires that G(X) be evaluated before F is called) you cannot
rely on any order of function evaluation.

"6.6.4 Evaluation of Arithmetic Expressions. The rules given in 6.1.2
specify the interpretation of an arithmentic expression. Once the
interpretation has been established in accordance with those rules, the
processor may evaluate any mathematically equivalent expression,
provided that the integrity of parentheses is not violated ..."

=> that because in mathematics the result of a function is dependent
only on its arguments, if the processor can show that the arguments have
not changed in the flow of control between calls one or more calls can
be optimized away. (As a result evaluating G(X) for the call F(G(X))
shown above need not require a call of G if G(X) has already been
evaluated.)

Also of interest is "15.9.36 Restrictions on Association of Entities"

The net result is that almost any useage of side effects in functions
can be optimized away by the processor. (There are also some side
effects involving subroutines that can be optimized away.) There are
cases where side effects in functions can not legally be optimized away,
but such cases are so context dependent that it is not worth the effort
to show that the side effect will occur with the intended result. You
are far better off avoiding side effects in functions as a hard rule.

(Note it is possible that typical idioms involving error flags (the only
side effect I would like to have) cannot be optimized away, but I
wouldn't bet on it and it would require pouring over the standard with a
magnifying glass to prove it to me.)

--

William B. Clodius Phone: (505)-665-9370
Los Alamos Nat. Lab., NIS-2 FAX: (505)-667-3815
PO Box 1663, MS-C323 Group office: (505)-667-5776
Los Alamos, NM 87545 Email: wclo...@lanl.gov

James Giles

unread,
Feb 15, 1997, 3:00:00 AM2/15/97
to William Clodius, bur...@gnu.ai.mit.edu

The Fortran 90 standard explicitly allows side effects within
functions. Indeed, it give examples of some of the uses of such:

IF(F(X)) A = X
WHERE (G(X)) B = X

F or G may define X.

One would have to conclude that the standard requires this to actually
work as expected, since it's given as an explicit example. The problem
is that modern environments generate the need to more finely interpret
certain statements of the standard in the light of new compilation
and/or operational techniques (such as interprocedural analysis or
parallel computing, respectively).

There are essentially three areas in which these interpretations are
implied. First, a function reference may not effect the value of any
other entity in the same statement. This implies that it *may* effect
the value of other entities *not* referenced in the same statement..
Second, the order of evaluation of functions within an statement is not
allowed to effect the value of the expressions of that statement. This
implies that the order in which functions are evaluated generally *can*
have an effect if they are referenced from different statements. Third,
if the value of a function is not needed in order to determine the value
of an expression that contains it, the function may or may not be
evaluated. This implies that if the value of the function *is* used in
the expression, the function must be evaluated. (Indeed, a stronger
statement is made in section 2.4.3.3 of the F90 standard: "The value of
a function result is determined by execution of the function." No other
means of determining the value of a function are standard conforming.)

From the above interpretation, it should be safe to have functions with
side effects provided you don't use multiple ones in a single statement
and you're careful not to reference them in logical expressions that
might be short-cut, or multiplied by zero, etc. This is overly safe.
Functions with disjoint side effects may be used in the same statement.

Now, as long as I can remember there have been persistent attempts to
weaken this interpretation. These stem from efforts to more
aggressively optimize the code. I will give some examples:

X = F(1) + F(1)

It has long been allowed to call F only once and use the result value
twice. Strictly speaking, this optimization meets the interpretation I
gave above (though only with very narrow and carefull interpretation).
Though, any other side effects would occur only once. Note that:

TEMP = F(1)
X = TEMP + F(1)

would require that the function be executed twice. This has long been
the interpretation. There have been efforts to weaken this though.
Indeed, the committee may have already allowed a reinterpretation to
allow this optimization. However, such a rule might even invalidate the
examples given in the standard itself.

===========================

FUNCTION B(I, J)
B = I - J
PRINT *, 'B'
END

This is from a private email from Craig Burley. The question is whether
the compiler is allowed to notice that the function value B(1,1) is
always zero and never actually call it. Since the only way the standard
specifies to get the value of a function is to execute that function,
such interprocedural analysis is not permitted. Though there can be no
objection to eliminating the calls if every reference to B(1,1) were
replaced by a zero *and* caused "B" to be printed. See "As If" later.

==============================

"6.6.4 Evaluation of Arithmetic Expressions. The rules given in 6.1.2
specify the interpretation of an arithmentic expression. Once the
interpretation has been established in accordance with those rules, the
processor may evaluate any mathematically equivalent expression,
provided that the integrity of parentheses is not violated ..."

Same sort of statements in F90 are in 7.1.7. However, in F90 it is made
clearer (though still not explicitly stated) that the only
mathematically equivalent expressions allowed are through use of
commutative and/or associative rules of the various operators in
expressions. This is made clear by placing these remarks only in those
sections of the standard which refer to the evaluation of intrinsic
and/or defined operators. It doesn't say anything about replacing
function references with methematically equivalent functions or any such
things. The only standard way to find the value of a function is to
execute it.

=============================

Program test
write(*,*) F()+G()
end program test
Function F()
common /kk/ kount
data kount/0/
F = 5
kount = kount + 1
return
End function F
Function G()
common /kk/ kount
data kount/0/
G = 7
kount = kount + 1
return
End function G

Here the intent is to instrument the code to count the number of
references. (I know, the DATA initialization should be in a BLOCK DATA
module and the common block should be SAVEd.) In an earlier email, I
expressed the opinion that this particular construct should be a safe
form of side effect, since the evaluation of F and G don't effect
each-other's values and can be invoked in either order. This is not the
case. The section notes (C.7.2 F90) explicitly permit functions
referenced in the same statement to executed in parallel. So, the above
would only be allowed if SAVEd data and/or COMMON data were inherently
protected by some mutual exclusion mechanism. I don't think the
standard can be read to require mutual exclusion to be intrinsic to
SAVEd and COMMON data (or data shared in any way). So, the above
program must be regarded as non-standard. It would be standard if the
two functions used separate common blocks.

================================

To summarize, the standard does permit side effects. I believe that if
the user restricts reference to such functions to distinct statements,
the standard requires a perfectly predictable and consistent behavior.
Within a statement the order in which the functions (and their side
effects) occur is not predictable. And some expressions may permit the
implementation not to evaluate the function at all. But, if the
function *is* evaluated (that is, if you make use of the function's
result value) then the side effects of that function *must* occur.

It is true of any programming language that the "As If" rule applies.
That is, given a program with a certain behavior you are allowed to
transform it in any way you want (optimization, for example) provided
that the effect of the resulting transformed program is the same *as*if*
the original program were executed. If this were not the case
compilation would not be possible and we'd have to run only on machine
in which Fortran was the machine language. So, optimizations such as
inlining function references and simplifying their behavior are
permitted so long as the effect (on all parts of the program's
environment) is the same as if the function were executed in accordance
to the standard.

Since Fortran 95 should have PURE functions defined, I hope the standard
clarifies all this. That is, it ought to make clear that PURE functions
may be executed in parallel, in any order, their values can be saved and
reused (even when called from different statements), etc. It should
also remove all those possibilities from functions which are not PURE.
The rule should be that imPURE functions should all be executed in some
serial order (though perhaps still not any predefined order if the
references are within a single statement). If a statement references an
imPURE function, execution of that statement should always cause the
execution of that function - as many times as it appears - even if its
value is not needed (obviously, for this rule, the logical expression in
a logical IF is separate from the statement that IF controls). Such a
clarification puts explicit control back into the hands of the user -
where it belongs.

Craig Burley

unread,
Feb 15, 1997, 3:00:00 AM2/15/97
to

In article <5e2ial$5...@news.utrecht.NL.net> Toon Moene <to...@moene.indiv.nluug.nl> writes:

Craig Burley <bur...@gnu.ai.mit.edu> wrote:

>After all, in Fortran, functions do _nothing_ other than return a value.
>They explicitly are _not_ intended to be used to compute side effects.

Craig, I do not believe this. How else would you explain the Fortran 95's
efforts to define "PURE FUNCTION" ?

Among other things, as a way to make it easy for implementations to avoid
some invocations of functions _without_ having to look at the code for the
functions themselves.

E.g., consider:

REAL A(N), B(N)
DO I = 1, N
A(I) = B(I) * FUNC(10)
END DO
FUNCTION FUNC(R)
FUNC = ... R ...
END

Since FUNC has no side effects and doesn't rely on inputs other than its
arguments, I think we'd all agree that a very smart optimizer (like us ;-)
could implement the above as:

REAL A(N), B(N)
FUNC_OF_10 = ... 10 ...
DO I = 1, N
A(I) = B(I) * FUNC_OF_10
END DO

What PURE does is _promise_ the implementation that FUNC always returns the
same result for a given set of input values (and perhaps also that it has
no side effects, or important ones, or whatever, but its important that
the promise includes that FUNC doesn't include some COMMON variable in the
computation of the return value, for example).

Therefore, implementations that are told FUNC is pure can do the same kind
of transformation shown above without having to look at the code of FUNC
itself.

(I'm basing this on what I've been told, and can guess, about PURE -- it's
been awhile if I've looked at any specification for it, if ever.)

Also, if it is true that PURE functions are entirely prohibited from performing
even non-result-affecting side effects, they can be implemented as parallelizable
calls without having to first do the kind of analysis normally needed to assure
that such implementation is safe. (In particular, it would be safe for two
or more invocations of FUNC to execute on the same time on different processors.)

If it really is "verboten" to have (visible-from-the-outside) side effects in
functions, could someone-in-the-know cite chapter and verse of the applicable
standards in this respect ...

See Section 6.6.1 (F77), and (I think) 7.1.7.1, the same basic language in
F90.

Given:

A = B() * C()
END
FUNCTION B()
B = 0
PRINT *, 'ENTER INPUT'
END
FUNCTION C()
C = 0
READ *, D
END

Can the value of the expression "B() * C()" be determined without having
to print "ENTER INPUT" and read a REAL value?

I think it's obvious that it _can_, in the sense that we all can agree
that the value of the expression will be 0 no matter what, and _none_ of
us is likely to resort to any apparent I/O (certainly not the I/O
indicated in the functions) to make this determination.

What I don't see in 6.6.1 is any restriction on the knowledge base of
the implementation to less than that of us humans in resolving whether
"the value of the expression can be determined otherwise".

But, I'm not sure. What I suggest to Fortran _programmers_ is that they
avoid relying on any side effects in functions. (They can program them
in, e.g. counters for profiling info, hash/cache functions to speed up
invocations _when they happen_, etc., they just can't rely on them.)

And, what I suggest to Fortran _implementors_ is that they limit the
knowledge base of "otherwise" in 6.6.1 to the kind of surface analysis
suggested by the example used in that section.

Finally, what I suggest to designers of language/dialect/library
extensions to Fortran is to not design important side effects in
the form of functions. Not just because there might exist implementations
that don't execute them under certain conditions -- even if there aren't
any the designers care about, I'd like them to avoid doing design work
that leads Fortran programmers to conclude that side effects _are_
fully predictable in functions and thus write code that depends on them.

(It's the fact that F90 and MIL-STD 1753 seem to follow this last bit
of advice that suggests I'm not the first to come up with it, and that
alerted me more to the notion that perhaps side-effects were even less
reliable in Fortran than I once thought.)

Craig Burley

unread,
Feb 15, 1997, 3:00:00 AM2/15/97
to

Given:

Thanks to a highly useful email from jgi...@cris.com, I am now somewhat
more certain about what the _standard_ says (F90 anyway).

Essentially, I now believe the example I give above _must_ be implemented
with the side effects (PRINT and READ), even though the value of the
expression "B() * C()" can be determined (by us) to be 0.

First, 2.4.3.3 says "The value of a function result is determined by execution
of the function". But, that isn't, to me, strong enough on the face of it
to override use of the word "otherwise" in 7.1.7.1. That is, I still can
interpret 7.1.7.1 as saying "hey, if you can figure out the value of an
expression somehow, then you don't need to execute the function references
in the expression", and "somehow" includes looking at the code of a function
(as we all do).

But, here's something much more persuasive. 7.1.7.1 essentially says the
following is not standard-conforming:

IF (0 * F(X) .EQ. 0) A = X
...
FUNCTION F(ARG)
F = 0
ARG = 3
END

The reasoning is that the function reference F(X) above "need not be
evaluated", because we can determine that the result of the expression is
0 regardless of what F(X) does. Because it need not be evaluated, it
does _not_ appear to matter (to the standard) that a particular implementation
might always invoke F anyway in that case -- since it need not be evaluated
_in general_, F need not be invoked. And, since F defines its argument,
that means X becomes _undefined_ as a result of the evaluation of the
expression "0 * F(X)" (again, even if, in a particular implementation, F
is actually invoked and thus defines X, X is considered undefined by the
standard).

Now, since X is undefined, it is inherently not standard-conforming to
do the "A = X" statement that _will_ be executed immediately after X
becomes undefined.

Okay so far -- nothing new to me here, hope it's not new to other enthusiasts
for this sort of thing.

But, jgiles points out something quite interesting. Look at 7.1.7 in the
standard, and note an example given there, where it is _explicitly_ stated
that the example is valid even if F defines its argument:

IF (F (X)) A = X

The purpose of this example is to specify that it is okay for F to define X
even though X is used in the same "overall" statement, because the use of
X is in the statement that is executed when the value of the expression
within the IF () is true.

Therefore, it is reasonable to conclude that this example _is_ standard-
conforming:

IF (F(X) .EQ. 0) A = X
...
FUNCTION F(ARG)
F = 0
ARG = 3
END

(Note that only the "0 * " has been removed from the earlier example.)

Now, if it is indeed the case, as I had thought (and no longer think),
that 7.1.7.1 allows an implementation to examine the code in a function
and "execute" it without regard to side effects to determine the result
of an invocation of that function, then the above _cannot_ be standard-
conforming.

Remember: it doesn't matter that a _particular_ implementation might always
invoke F in the above example. 7.1.7.1 says that if the function reference
"need not be evaluated" (not _is_ not evaluated), any entities that it would
have defined become _undefined_.

So, since "F(X) .EQ. 0" need not be evaluated above (we "know" it will be
.TRUE.), X becomes undefined.

And this would render the above example non-standard-conforming.

In fact, I can't think of any way the example in 7.1.7 _could_ be made
standard-conforming and still be useful as an example in that context.

Clearly, given

LOGICAL F
IF (F (X)) A = X
...
LOGICAL FUNCTION F(ARG)
F = .TRUE.
ARG = 3
END

then the "IF (F (X)) A = X" statement would be non-standard-conforming,
since F is "known" to return .TRUE., thus it need not be invoked since
the "F (X)" expression need not be evaluated, thus the assignment
"ARG = 3" _undefines_ the entity associated with ARG, thus X becomes
undefined by "IF (F (X))", thus the "A = X" that follows is not
standard-conforming.

So, obviously the standard had some implicit restriction on function F
when it said that "IF (F (X)) A = X" is standard-conforming even if F
defines X.

Obviously implicit restrictions include "F contains valid Fortran
statements", blah blah blah.

What about less-obvious restrictions? Well, the first that springs to mind
is that, maybe, F never returns .TRUE.. But, that makes the example
nonsensical in context -- the context being to show that it is okay to
execute "A = X" after "IF (F (X))".

The second restriction that springs to mind is that function F must not
be easily predictable in terms of its return value, e.g.:

LOGICAL FUNCTION F(ARG)
COMMON SOMETHING
F = (SOMETHING .LT. 3.1416)
ARG = 3
END

Okay, this seems sensible, except, again, it seems reasonable to conclude
that, if 7.1.7.1's "otherwise" trumps 2.4.3.3's requirement that a function
must be executed to determine its return value, then "IF (F (X))" could
be replaced by "IF (SOMETHING .LT. 3.1416)", appropriately declaring
SOMETHING as the first item in blank COMMON.

In that case, again, "F (X)" need not be evaluated if the implementation
can determine the return value of F -- therefore, X becomes undefined, and,
again, the example is rendered non-standard-conforming.

Even having the return value of F depend on the _incoming_ value of its
argument, ARG, meaning "IF (F (X))" depends on the value of X prior to
the invocation of its statement, would not seem to be immune to the
above reasoning -- the value of "F (X)" can be determined "otherwise"
(that is, without performing the side effects of the code in F), so X
becomes undefined, so the example is not standard-conforming.

Even if people were to claim that these intricate cases don't have the
interpretations I claim they do, and therefore there are cases where
"IF (F (X)) A = X" is a useful and standard-conforming example, I find
it unreasonable to believe the standards committee would use such an
example without spelling out such intricate requirements for function F.

I mean, we _expect_ the implicit requirements that the function itself
look standard-conforming, have the right interface, etc. -- we don't
expect the example to become intelligible (and therefore useful) only
when fairly intricate, but unstated, requirements exist for the
undescribed function F.

So, I conclude that "IF (F (X)) A = X" is standard-conforming even
when F is a function that simply defines its argument to some value
and returns .TRUE..

And, given 7.1.7.1's statements concerning defined->undefined entities,
I therefore conclude that 7.1.7.1's use of the word "otherwise" is,
in fact, trumped (constrained) by the language in 2.4.3.3.

The upshot: for an implementation to determine the value of a function
reference, it must act as if that function was invoked in that context,
even if it "knows" what the function will return in any instance. That
is, side effects performed by the function must be executed.

This does not mean all functions are invoked, because 7.1.7.1 (etc.) still
provide that parts of expressions might not need to be evaluated, and
thus the functions need not be invoked to determine its return value.

So, the following statements _require_ that side effects of the
functions be performed:

I = FOO()
IF (BAR()) CONTINUE
IF (.TRUE. NEQV. BAR()) CONTINUE

However, the following statements do not require that side effects of
the functions be performed:

I = 0 * FOO()
IF (BAR() .OR. .TRUE.) CONTINUE
IF (BAR() .NEQV. BAR()) CONTINUE

I welcome any further insights or conclusions on this.

James Giles

unread,
Feb 15, 1997, 3:00:00 AM2/15/97
to Craig Burley

Craig Burley wrote:

> [...] I'd like them to avoid doing design work


> that leads Fortran programmers to conclude that side effects _are_
> fully predictable in functions and thus write code that depends on them.

Ouch!! I'm never in support of making a language design ambiguous just
to enforce programming style rules. I'd rather disallow side effects
entirely than permit them with *unnecessary* ambiguities. The present
ambiguities only exist because the committee wanted to allow side
effects, but also wanted to allow optimizations in certain contexts that
only really apply to PURE functions. Once an explicit distinction is
available (like allowing functions to be declared PURE), the ambiguity
should be eliminated. Whether it's a good idea for a programmer to
*rely* on side effects is for him or her to decide. The language
designer's goal should be to make all supported features well defined.

Richard Maine

unread,
Feb 17, 1997, 3:00:00 AM2/17/97
to

Craig Burley <bur...@gnu.ai.mit.edu> writes:

> The upshot: for an implementation to determine the value of a function
> reference, it must act as if that function was invoked in that context,
> even if it "knows" what the function will return in any instance. That
> is, side effects performed by the function must be executed.

I disagree, but I don't have the time to participate in an extended
detailed discussion on the subject. The question is somewhat subtle.

The hard-to-find f77 "Fortran Information Bulletin 2" is a set of official
interpretations of f77 published by ANSI. No I don't have an electronic copy.
From page 18

Question:
Are functions with side effects permitted in FORTRAN 77?

Answer:
Certain side effects are prohibitted (6.6)......

However, other side effects are permitted, but the result of using them
is not defined.

I interpret it as being legal for the processor to use *ANY* possible
means, including revelation from heaven, to determine the value of the
function result. (Perhaps it could count the angels dancing on the head
of a pin). I can find no text to limit the "other means". The
examples in the standard aren't sufficiently convincing to the contrary.
I'd interpret them as falling in the above-described category of
"permitted, but the result of using them is undefined." The distinction
between "prohibitted" and "permitted, but having undefined results" is
mostly academic. Both cases mean that the standard gives you no guarantee
of portable behavior.

I am not prepared to argue the point. Just wanted to make one post to say
that I disagreed, but I'll not follow up further. A thourough defense of the
position would take too much time for too little benefit.

--
Richard Maine
ma...@altair.dfrc.nasa.gov

Craig Burley

unread,
Feb 17, 1997, 3:00:00 AM2/17/97
to

Craig Burley <bur...@gnu.ai.mit.edu> writes:

Okay, I'll accept your disagreement without thorough explanation, but would
appreciate if you could illustrate, in a simple way that makes sense in the
context of 7.1.7 of the F90 standard, a function F that may define its
argument X and still be standard-conforming when invoked thusly:

IF (F (X)) A = X

E.g. the example I gave, something like this, cannot be standard-conforming
if I interpret the standard correctly:

LOGICAL FUNCTION F(X)
F = .TRUE.
X = 3
END

The examples in the standard aren't sufficiently convincing to the contrary

to _you_, but to me and other readers, an _explicit_ statement that the
above IF statement is valid even if F defines X is _very_ convincing.

So the question I raised remains -- if, as you say, an implementation can
determine the value of F via revelation from heaven, and if the code for
F defines X (were it executed), then, _by definition_, X becomes undefined
as a result of "IF (F (X))" regardless of what _any_ implementation actually
does. (E.g. in my sample F function, since F always returns .TRUE.,
an implementation may avoid executing it, in which case, by 7.1.7.1, since
F defines X, X becomes _undefined_ upon execution of the "IF (F (X))" code.)

In that case, "A = X" cannot possibly be standard-conforming at that point.

If you can identify which part of this argument is wrong, that would be
helpful, and shouldn't need a thorough rebuttal. E.g. maybe it's wrong
to assume that X becomes undefined just because some implementation out
there can determine the value for F without invoking it (i.e. I'm misreading
the text in 7.1.7.1)? Or maybe it's wrong to assume that the example in
the standard was written without expecting F to _ever_ return .TRUE. (in
which case, one wonders, why is the example there in the first place)?

Note that I am personally somewhat more in _favor_ of _your_ interpretation
of the standard, though by a hair, simply because it gives the programmer
more expressiveness leading to more optimal implementation -- if the programmer
ever really needs a function's side effects to be performed, he can (and
IMO should anyway) recode it as a subroutine.

However, it is the above sequence of logic that leads me to conclude that
not only can a function have side effects, but the result of using them
_is_ defined by the standard -- since the standard explicitly says that in
7.1.7 ("[F may] define X." and related verbage/examples). Of course, that
does not mean _all_ side effects are standard-conforming, as jgi...@cris.com
points out thoroughly enough.

If the fairly clear language in the standard cannot be trusted on this issue,
i.e. if legalistic attempts are made to explain away what seems to be a
clear permitting of defined uses of side effects, then the standard is
useless as such.

On the other hand, if what you're saying is that the standards _committee_
has changed the standard by virtue of subsequent interpretations, then that
is another thing entirely. Fixing bugs in the standard is a good thing, IMO,
even if I (or someone else) might not agree they were bugs in the first
place.

In that case, then I assume it would be safe to "pencil in" the following
statement around the examples in 7.1.7:

There is no way a standard-conforming program can do "IF (F (X)) A = X"
if F ever returns .TRUE. and F also defines X.

That is, the above verbage would replace the current verbage that explains
exactly how to do the above and have it be defined, so people stop trusting
in that (current) explanation in the standard. (Same for the WHERE example,
of course, as it has the same problem.)

Also note that the standard could be further amended in other areas by
simply stating this, if I understand your explanation (and its implications
vis-a-vis 7.1.7.1):

Any function that defines its arguments performs side effects. These
particular side effects are, by definition, not needed to determine the
result of the function invocation, since it is considered conforming for
an implementation to determine the result by edict from Heaven -- and,
clearly, Heaven need not actually define any arguments of a function
(choosing instead, perhaps, to use local temporaries to save traffic
on the Heaven<->Earth bus).

Therefore, since an implementation may avoid fully executing a function
to determine its value, any function that appears to define its arguments
(or, for that matter, global data) _necessarily_, by the rule in 7.1.7.1,
_undefines_ those arguments (and global data) on _all_ implementations.

Again, personally, I have no problem with this. To me, it culminates in
a very simple statement that I think programmers can understand:

The only purpose to design and write a function (and its invocation) is
to yield a resulting value. Anything else the function does is therefore
irrelevant, and therefore all such side effects have undefined behavior,
making the program non-standard-conforming, though, in practice, an
implementation might perform them the "expected" way.

If you have side effects that are important (including, but not limited
to, I/O, modification of global data, modification of dummy arguments),
code them in the form of a subroutine call.

This is what I have been recommending anyway, and it's good to get at least
some apparent backup from someone on the standards committee even though I
disagree with the notion that the standard already says this somehow (and
note I am still working with a very late draft copy, I believe).

(However, perhaps my statement above was too strong -- I can
see how doing a "PRINT *,'FOO'" within a function might not necessarily
render the function invocation non-conforming, just not predicatable
as to whether 'FOO' was actually printed in a specific instance.
Still, defining arguments and then referencing them leaves little room
for doubt -- either it is standard-conforming or it isn't.)

William Clodius

unread,
Feb 17, 1997, 3:00:00 AM2/17/97
to

Note for those not in the know. No one person's word can be taken as the
final authority on the Fortran standard. The ultimate authority rests
with the ISO working group JTC1/SC22/WG5 which often delegates
interpretations of such matters to the ANSI committee X3J3. However,
Richard Maine is the person that currently has primary responsibility
for editing the Fortran Standard and hence should be an unusually
knowledgeable person on such points.

Richard Maine

unread,
Feb 18, 1997, 3:00:00 AM2/18/97
to

Craig Burley <bur...@gnu.ai.mit.edu> writes:

> Okay, I'll accept your disagreement without thorough explanation, but would
> appreciate if you could illustrate, in a simple way that makes sense in the
> context of 7.1.7 of the F90 standard, a function F that may define its
> argument X and still be standard-conforming when invoked thusly:
>
> IF (F (X)) A = X

My personal opinion...

(And as Bill Clodius correctly noted, it *IS* only my opinion. I can
quote the official FIB interpretation, but alas even the interpretation
is less than 100% unambiguous, so I have to interpret the interpretation,
in which case I am not doing so in any official capacity. In particular,
when the official FIB interpretation says that "other side effects are
permitted, but the result of using them is not defined" it fails to
adequately define what "other side effects" this applies to. I read
this as meaning "all other side effects", but the word "all" isn't in
the official version, so it could be some unspecified set less than
all.)

... is that you can't ever invoke F as in the example above and be
guaranteed a consistent behavior. As best as I can tell, the above
code would be "legal but have undefined results". Don't ask me to
give a practical distinction between "illegal" and "legal with
undefined results" because I can't.

I personally think the standard is schizophrenic on the subject.
The "can be determined otherwise" clause allows unlimitted optimizations,
but nothing ever quite explicitly spells out that this makes function
side effects all but useless. Just because I'm editor of the
f95 document doesn't give me permission to rewrite it at will,
particularly on a controversial subject like this. On occasion,
editor is a fancy title for typist. The more controversial the
subject, the more tightly my hands are tied.

I recall a long discussion of this issue a few years back at an X3J3
meeting in Tahoe. My personal recollection is that there was not
enough consensus to support an unambiguous rewrite for either
position, so it was left in its current ambiguous state. To give my
*VERY* personal opinion, the general undercurrent was something like

1. Optimizers should have free reign to be as smart as they can
manage. Optimizability is a critical feature of Fortran and
we don't want to restrict the possible optimizations.

2. But we don't want to make it unambiguously clear that this
makes function side effects useless because there is too much
user code that implicitly assumes otherwise.

This may be an overly cynical viewpoint (particularly the part about
not wanting to make the consequences clear). Perhaps the more
realistic viewpoint is that the committee just couldn't reach consensus
on what I'd regard as the 3 plausible fixes:

1. Limit optimizers so that function calls can be optimized away only
for the very limitted cases like those that the standard gives
examples of.

2. Point out in bold print that you can *NEVER* rely on the behavior of
functions with side effects.

3. Or somehow compromise by allowing a large but finite and well-defined
set of circumstances where optimization is allowed.

Option 1 is distasteful to the "speed crowd". Option 2 would admit that
too much existing code is at best questionable. And option 3 is too
hard to do (or at least I haven't seen anyone suceed).

The result is that the standard (all the versions) is "less than perfectly
clear" on the question. No, I'm not happy with that, but if I "fell on
my sword" about everything that makes me unhappy, I'd be hol(e)y enough
for sainthood (something I otherwise fall far short of). This isn't
one of the points that I decided to continue fighting on because it
is more of an academic than a practical issue. The one case where it
most often arises in practice is with things like random number
generators coded as functions, and those are so subject to trivial
optimizations in common cases (like do i=1,1000; a(i)=myrand(); end do)
that I don't think you are going to get a ruling that they are ok.

The above "explanations" are, of course, personal viewpoints. As with
so many questions about why its that way, the only real answer is that
there was not sufficient consensus on doing anything else.

My personal advice is to avoid depending on function side effects,
regardless of how clear the standard might or might not be on the
subject. This advice is worth at least 10 times what I'm charging
for it. :-)

P.S. And it is a complicated enough question that I doubt a
comp.lang.fortran discussion would be able to bring about a consensus;
its hard enough when you are all in the same room. You might be interested
to know that material from comp.lang.fortran was cited in support of
a position on an unrelated subject at last week's X3J3/WG5 meeting
(you were even one of the people cited, Craig). And after that
citation, a revote of an issue was taken which reversed a vote from
earlier in the week. Whether the votes changed because of those
citations or for some other reason is, of course, hard to say, so
I won't try. It would be unpolitic of me to mention more detail,
as the subject was controversial, but I thought you'd appreciate
knowing that you were heard.

--
Richard Maine
ma...@altair.dfrc.nasa.gov

Toon Moene

unread,
Feb 18, 1997, 3:00:00 AM2/18/97
to

Richard Maine <ma...@altair.dfrc.nasa.gov> wrote:

Thanks for your efforts to make the standardisation effort clear on this
point, but:

> 2. Point out in bold print that you can *NEVER* rely on the behavior of
> functions with side effects.

In Fortran 95, wouldn't *this particular optimisation assumption* be covered
by specifying the function as "PURE FUNCTION" ?

Richard Maine

unread,
Feb 19, 1997, 3:00:00 AM2/19/97
to

Toon Moene <to...@moene.indiv.nluug.nl> writes:

> > 2. Point out in bold print that you can *NEVER* rely on the behavior of
> > functions with side effects.
>
> In Fortran 95, wouldn't *this particular optimisation assumption* be covered
> by specifying the function as "PURE FUNCTION" ?

Partly, but PURE is more stringent than this. Pure functions are not
allowed to have side effects at all. This is enforced by compile time
syntactic restrictions against anything that could even concievably
cause a side effect. For example, a PURE function may never change
the value of a variable in COMMON, imported from a module, or even
a local save'd variable. This is much more stringent than saying that
you can change a variable, but can not subsequently rely on its value.
A PURE function also cannot do any i/o, even to print an error message.
This is pretty "strict" and substantially limits the class of functions
that can usefully be written as PURE. PURE functions are primarily
aimed at parallel implementations where side effects would be a serious
hindrance (PURE came originally from HPF). User elemental functions
(those that can operate on arrays, possibly in parallel) must be PURE.
User functions used in specification statements must also be PURE.

Since you can't have any error messages or return any error flags,
PURE functions had better be pretty simple.

--
Richard Maine
ma...@altair.dfrc.nasa.gov

Michel OLAGNON

unread,
Feb 19, 1997, 3:00:00 AM2/19/97
to

In article <5ed9s2$m...@news.utrecht.NL.net>, Toon Moene <to...@moene.indiv.nluug.nl> writes:
>Richard Maine <ma...@altair.dfrc.nasa.gov> wrote:
>
>Thanks for your efforts to make the standardisation effort clear on this
>point, but:
>
>> 2. Point out in bold print that you can *NEVER* rely on the behavior of
>> functions with side effects.
>
>In Fortran 95, wouldn't *this particular optimisation assumption* be covered
>by specifying the function as "PURE FUNCTION" ?

As far as I could understand the draft,

1. a PURE FUNCTION has absolutely no side effects

2. the compiler must be able to verify that there are no side effects.

The second condition is much more stringent. It prohibits even thinking of
side effects ;-)

Michel

--
| Michel OLAGNON email : Michel....@ifremer.fr|
| IFREMER: Institut Francais de Recherches pour l'Exploitation de la Mer|
| Centre de Brest - B.P. 70 phone : +33-2-9822 4144|
| F-29280 PLOUZANE - FRANCE fax : +33-2-9822 4135|
| http://www.ifremer.fr/ditigo/molagnon/molagnon.html |


James Giles

unread,
Feb 19, 1997, 3:00:00 AM2/19/97
to Richard Maine

Richard Maine wrote:
[...]
> Partly, but PURE is more stringent than this. Pure functions are not
> allowed to have side effects at all. This is enforced by compile time
> syntactic restrictions against anything that could even concievably
> cause a side effect. For example, a PURE function may never change
> the value of a variable in COMMON, imported from a module, or even
> a local save'd variable. [...]

PURE could have been even more stringent (and probably should be). One
of the basic properties you want a PURE function to obey is that its
value depend only on its arguments. So,a prohibition on defining COMMON
variables is not enough, you should have prohibited any references to
COMMON at all - otherwise the execution of another procedure between
calls to your PURE function could change its behavior.

Similarly, the only things you should permit PURE functions to reference
out of a MODULE should be other PURE functions and PARAMETERs, not
MODULE variables or non-PURE procedures.

Now, I've mentioned elsewhere in this thread that there should be a
category of variables (which I call instrumentation variables) which can
be SAVEd, assigned, and shared - the use of which does not affect the
purity of a function. The restriction on instrumentation variables is
that you may not use them in the definition of any non-instrumentation
variables (and their final values would be written to an instrumentation
file during STOP processing). Since these variables have no effect on
the *meaning* of the procedures they appear in, they are safe to use in
PURE functions. (These could be added to a Fortran implementation
without standard sanction, of course. It would just be a convenience to
have them everywhere.)

Loren P. Meissner

unread,
Feb 19, 1997, 3:00:00 AM2/19/97
to

PURE in F95 is pretty close to "absolutely no side effects", but there is
still a bit of leeway in the definition of "side effect".

I always taught my students that a function has one "main effect", which is
to return its result value - - and that anything else that can be detected
outside the function is a "side effect". This includes modifying the value
of a dummy argument or of a global (COMMON) variable, as well as doing any
kind of input or output that changes the state of a file (except perhaps an
internal file that is totally inside the function). F95 has specifically
ruled out all of the obvious kinds of side effects.

However, in principle, a computer could have some kind of external light
that goes on during a MULTIPLY operation, and a PURE F95 function could
make this light go on and off in Morse Code to send a message to a human
who is watching. I suppose that would be a side effect according to my
definition.

In the medical world, we usually learn to think of side effects as BAD, but
this does not follow from the computer-oriented definition. In fact, we
have examples such as a side effect of some kind of skin cream that caused
hair to grow and became a cure for baldness.

--
Loren P. Meissner
<LPMei...@msn.com>

Michel OLAGNON <mola...@ifremer.fr> wrote
. . .


> 1. a PURE FUNCTION has absolutely no side effects

. . .

Otto Stolz

unread,
Feb 20, 1997, 3:00:00 AM2/20/97
to LPMei...@msn.com

Following are two slightly off-topic anecdotes.
Please ignore them kindly, if you are not interested.

Loren P. Meissner <LPMei...@msn.com> wrote:
> there is still a bit of leeway in the definition of "side effect"

> [...] in principle, a computer could have some kind of external


> light that goes on during a MULTIPLY operation, and a PURE F95
> function could make this light go on and off in Morse Code to send
> a message to a human who is watching.

This has indeed be done (watching the light, not programming it in
Fortran 90, that is), at Stuttgart (Germany) in the late 60s.

The computer involved was the university's TR4 (German brand, now
obsolete for quite some time) which then had no spooling system yet,
so it had to be fed with a never-ending series of punched-card jobs.
This was a problem for the night-shift operators who wanted to take
a nap while the TR4 was grinding its data. Of course, they could load
several jobs on the card-reader, but who would tell them when they
would be finished? (Even a formidable card deck could be done with
as a matter of seconds, if the source program contained a syntax
error).

So a staff member constructed an ingenious device. The TR4 had all
its CPU registers connected to little lights. The new device consisted
of two optical sensors to watch the two lights representing the parity
bits (the TR4 had two parity bits per 50 bit word) of the memory
access register (something akin to a data bus). Now, normally these
two bits would change several times per second, but when the system
was in its idle loop, no memory access was performed, so these bits
stayed constant. In this case, a logic circuit rung a bell near the
operator's resting place.


Another computer, the ER 56 (the only computer SEL, now belonging to
Alcatel, ever built, and the one where I took my very 1st programming
course, in Algol 60), had a speaker connected to its accumulator
register, so you could hear it chirp while it was computing. There
was a legendary program, titled "12 German Folk Songs"; you can
imagine what sort of side-effect this program performed :-) This
was only possible, because a staff member had designed, and soldered
into the computer, an instruction to play a (musical) rest (or so
I was told.)

Best wishes,
Otto Stolz

Craig Burley

unread,
Feb 20, 1997, 3:00:00 AM2/20/97
to

In article <uezpx1j...@altair.dfrc.nasa.gov> Richard Maine <ma...@altair.dfrc.nasa.gov> writes:

Craig Burley <bur...@gnu.ai.mit.edu> writes:

> Okay, I'll accept your disagreement without thorough explanation, but would
> appreciate if you could illustrate, in a simple way that makes sense in the
> context of 7.1.7 of the F90 standard, a function F that may define its
> argument X and still be standard-conforming when invoked thusly:
>
> IF (F (X)) A = X

My personal opinion...
[...]


... is that you can't ever invoke F as in the example above and be
guaranteed a consistent behavior. As best as I can tell, the above
code would be "legal but have undefined results". Don't ask me to
give a practical distinction between "illegal" and "legal with
undefined results" because I can't.

Well, here's the simplest case I can think of, that avoids all those
nasty questions about precision and such:

LOGICAL A, F, X


IF (F (X)) A = X

PRINT *, A
END

LOGICAL FUNCTION F (X)
LOGICAL X
X = .TRUE.
F = .TRUE.
END

(The PRINT statement is strictly optional, I believe, but avoids the
question "what if the implementation eliminates everything", which I
don't want to get into. ;-)

I believe there can be _no_ status according to F90 for the above
program other than "standard-conforming" and "not standard-conforming",
for the following reason:

If an implementation is permitted by F90 to avoid actually executing
function F -- observable only in the sense that the "X = .TRUE."
statement is not performed -- then, by 7.1.7.1, which says "If a
statement contains a function reference in a part of an expression
that _need not_ be evaluated, all entities that _would have_ become
defined in the execution of that reference become _undefined_ at the
completion of evaluation of the expression containing the function
reference", X is _necessarily_ undefined following "IF (F (X))",
making the example non-conforming. (Emphasis added to quote by me.)

Basically, legalese aside, the problem is that the standard _must_ say
that the above example is either standard-conforming -- which means
X _is_ set to .TRUE. by function F, which means it _is_ invoked, which
means all implementations _must_ invoke it under those circumstances --
or it is not, because if it is not defined that X is set to .TRUE.,
then there is nothing that defines the program's behavior when X is
not set to something (which must be either .TRUE. or .FALSE.). (The
example could be made even more "strict" by adding

IF (.NOT. X) PRINT *, 1/0

after the first IF statement, which would make the example non-conforming
either if the standard did so explicitly, or said it was somehow "standard"
but not "predictable" -- since the program would clearly divide by zero
if it relied on an unpredictable, but likely, result.)

So, while I accept that you have in mind a distinction between "illegal"
and "legal with undefined results" that might have practical use, I claim
there can be _no_ such distinction for the above example. Either it
is conforming or it isn't.

I appreciate your honest explanation of both your views and some of what
goes in at the standards meetings. And I sure understand the problem they
face -- heck, I'm only one person, and I have _several_ opinions on this
issue. ;-)

But, frankly, there's got to be _some_ organization that is responsible
for deciding, once and for all, either that the existing F90 standard (with
or without its subsequent interpretations/clarifications, which I have yet
to catch up on, though thanks to Janice Shepherd (sp?) at IBM, I do have
much of the printed material) says the above example is, or is not,
standard-conforming -- or is willing to amend the standard to say one or
the other, accordingly.

Because, the upshot of not defining the above program, and related examples,
as standard-conforming is that _all_ functions with side effects are
effectively made, by the absence of their definitions in the standard,
_non-conforming_.

My personal advice is to avoid depending on function side effects,
regardless of how clear the standard might or might not be on the
subject. This advice is worth at least 10 times what I'm charging
for it. :-)

Yes, but since I give the same advice (to compiler _users_ -- I'll tell
compiler _writers_ to always implement function side effects if the
function's result is needed at the point of invocation, which allows
omission of invocation in "A = 0 * F()" but not "A = F()"), it's worth
at least 20 times your fee. ;-)

P.S. And it is a complicated enough question that I doubt a
comp.lang.fortran discussion would be able to bring about a consensus;
its hard enough when you are all in the same room. You might be interested
to know that material from comp.lang.fortran was cited in support of
a position on an unrelated subject at last week's X3J3/WG5 meeting
(you were even one of the people cited, Craig). And after that
citation, a revote of an issue was taken which reversed a vote from
earlier in the week. Whether the votes changed because of those
citations or for some other reason is, of course, hard to say, so
I won't try. It would be unpolitic of me to mention more detail,
as the subject was controversial, but I thought you'd appreciate
knowing that you were heard.

I'm interested to know that, but not entirely surprised. My limited
encounter with X3J3 suggests that it is indeed an organization that is
doing its best to accommodate a wide variety of input, and not making
life _apparently_ easier for itself by ignoring anything not submitted
through official channels. (E.g. they were quite willing to look past
obvious botches in my letter regarding F90 to the issue at hand.)

Regardless, the strength of a committee (or government or foundation or
whatever) ultimately derives from the strengths of its members, so the
fact that one or more members are willing to bring up useful points of
view, even when they're made by people outside the official channels,
IMO makes for a stronger committee.

The challenges I am guessing face X3J3/WG5 come from trying to do too
much -- instead of just clarifying the language of the pertinent
standards and codifying existing practice (with perhaps minor adjustments
to make existing practice work in the context of an industry-wide
language, rather than a vendor- or machine-specific dialect), it
also tries to do new language design. (That is, it adds so much to
existing language that a new language results that has most of the
problems of new languages plus most of the problems of compatibility
with most any existing system.)

So, personally, due to a combination of lack of resources (money and time)
and a feeling that I can accomplish more this way, I prefer to not
get involved in the standards work itself at this point, instead using
my current position (on g77) and perhaps a future role in a g90/g95
project as a way to put forth some new language-design elements, resolve
issues in a variety of ways, and so on in that context. Then, whatever
work I do that is worthwhile might well be picked up by X3J3/WG5/whatever,
and what is not, will fall by the wayside, hopefully be rectified in
future versions of g77 (which, I can say, at least come out a little
faster than new Fortran standards ;-), and not end up as too much of an
albatross around the industry's neck.

As far as this issue, among my many opinions is the strong feeling that
if _any_ argument is made for what programmers either "expect" or "can
be expected to learn, remember, and make intelligent use of", then
that argument should culminate in _one_ of the following two choices:

1. No function side effects have defined (or predictable) results in
Fortran. That is, the above example is not standard-conforming, and
more practical examples are at least not predictable in terms of
whether side-effects occur (though they might be defined in that
they either do, or don't, occur, in the same sense that the standard
defines the effect of "A = 1. / .1" without predicting the value
to which A is set).

2. Function side effects are processed in sequential, strict left-to-right
order as the function invocations appear in an expression (though of
course necessary evaluation takes precedence) before any of their
results are used. So, in "A = F() + G() * H()", F is invoked, then G,
then H, and _then_ their results are used (G's and H's multiplied, then
added to F's). In "A = F() + G(I()) + H()", the order would be F,
I, H, then G, again, because strict left-to-right ordering would be
done (because I believe it would be best understood) in a "greedy"
manner, though if experimentation shows people would naturally expect
F, I, G, H, or even I, F, G, H, then the standard should pick that
one ordering as the only permitted one. (And, of course, none of
these would permit an implementation avoiding invoking F in "0 * F()".)

Since I believe Fortran's strengths include having "real" math-style
functions, I'd tend to lean towards #1 myself. Also, it's a much simpler
language _model_ -- people reading code written to model #1 have a much
clearer picture of the non-mathematical interactions than code written
to model #2, because they don't have to worry about interactions between
function invocations -- there aren't any, so they're made explicit in the
code via "CALL" and so on.

But, what I believe the current F90 standard says is on the #1 side but
_in between_ 1 and 2, while the C language says something more towards
(but not all the way over at) #2.

And, I believe that, given your statements about the F90 standard (that
is, the combination of F90 itself, plus the ambiguous clarifications
offered by various people here and, most importantly, X3J3 itself), the
current _effective_ Fortran standard _is_ #1, exactly.

So, I'd say, X3J3 might as well bite the bullet and go with #1. (This
doesn't affect whether PURE is useful, just perhaps the degree to which
it is useful.)

I wouldn't mind seeing syntactic enhancements made to allow a programmer
to specify that a function invocation _must_, if its value is needed
at that level, be performed. (He already has "CALL" to just get side
effects with optional return values.) E.g., something like:

IF (CALL ( F (X))) A = X

Where CALL is a new "special" intrinsic that means "whatever the argument
returns, but any functions listed therein _are_ called in strict left-to-
right order [or at least called, perhaps order-unspecified as in my
belief about what F90 actually says today]". This would leave optional
whether F need to be invoked in "A = 0 * CALL (F ())", but not in
"A = CALL (0 * F())".

(Perhaps instead of CALL, a more descriptive name like FORCE_CALL is
needed. However, I am _against_ using attributes on names to accomplish
this -- that has the same problem as VOLATILE, in that it means readers
of code must know semantic information to determine whether an expression
is "normal" or not, and if they have to do that, you might as well save
everyone the trouble and go with choice #2 above.)

William Clodius

unread,
Feb 20, 1997, 3:00:00 AM2/20/97
to

Craig Burley wrote:
> I<snip>

> I'm interested to know that, but not entirely surprised. My limited
> encounter with X3J3 suggests that it is indeed an organization that is
> doing its best to accommodate a wide variety of input, and not making
> life _apparently_ easier for itself by ignoring anything not submitted
> through official channels. (E.g. they were quite willing to look past
> obvious botches in my letter regarding F90 to the issue at hand.)
>
> Regardless, the strength of a committee (or government or foundation or
> whatever) ultimately derives from the strengths of its members, so the
> fact that one or more members are willing to bring up useful points of
> view, even when they're made by people outside the official channels,
> IMO makes for a stronger committee.
>
> The challenges I am guessing face X3J3/WG5 come from trying to do too
> much -- instead of just clarifying the language of the pertinent
> standards and codifying existing practice (with perhaps minor adjustments
> to make existing practice work in the context of an industry-wide
> language, rather than a vendor- or machine-specific dialect), it
> also tries to do new language design. (That is, it adds so much to
> existing language that a new language results that has most of the
> problems of new languages plus most of the problems of compatibility
> with most any existing system.)
>
> <snip>

Craig:

There are times when I wish your postings would focus more and this is
one of those times. I have editied out 90%+ of your post and still am
left with more issues than can be properly addressed with this posting.
The above raises all sorts of issues about standardization in general
and language standardization in particular, and has a number of
assumptions that cannot be addressed unless they are made more explicit.
I would suggest an examination of Brian Meeks articles
(http://www.kcl.ac.uk/kis/support/cc/staff/brian/brianpub.html) on
standardization which challenge a number of your conclusions and
probably address some of the assumptions that lead to your conclusions.
In particular I would suggest his articles

Programming languages: towards greater commonality
On Julius Caesar, Queen Eanfleda, and the lessons of time past
Language standards committees and revisions
Too soon, too late, too narrow, too wide, too shallow, too deep

While it would not address your technical conclusions I feel need
challenging, you might also find

The Fortran (not the foresight) saga: the light and the dark

informative on the social (and political) issues of standardization,
with an emphasis, of course, on their impact on the standardization of
Fortran 90. If you can find it I also recomend Computer Standards and
Interfaces Vol 16 Nos 5/6, September 1994, pp 427-437, which is devoted
to language standardization in general.

An amusing (and slightly relevant) side note that I just found today is

http://www.chaos.org.uk/~dave/zen/specs.html

Malcolm Cohen

unread,
Feb 21, 1997, 3:00:00 AM2/21/97
to

In article <330B26...@cris.com>, James Giles <JGi...@cris.com> wrote:
>PURE could have been even more stringent (and probably should be). One

Allow me to dissent.

>of the basic properties you want a PURE function to obey is that its
>value depend only on its arguments.

That is not the rationale for PURE. A PURE function is parallel-safe.
This does not require it to depend only on the values of its arguments.
(PURE-ness only affects whether a function can be called within a FORALL, so
it only needs to be parallel-safe, not arbitrarily optimisable).

>Now, I've mentioned elsewhere in this thread that there should be a
>category of variables (which I call instrumentation variables) which can

Instrumentation data (of a PURE function) that is not updated in a parallel
safe manner is worthless. You really feel a strong need to instrument FORALL?

>be SAVEd, assigned, and shared - the use of which does not affect the
>purity of a function. The restriction on instrumentation variables is
>that you may not use them in the definition of any non-instrumentation
>variables (and their final values would be written to an instrumentation
>file during STOP processing). Since these variables have no effect on

A new meaning for STOP... so implementations would need to keep track of these
global variables in some special list.

>the *meaning* of the procedures they appear in, they are safe to use in
>PURE functions. (These could be added to a Fortran implementation
>without standard sanction, of course. It would just be a convenience to
>have them everywhere.)

...and virtually no-one would use them. A heavy price for everyone to pay for
*your* convenience. Sounds like you need some special "instrumenting" Fortran
implementation; this would not come any cheaper by adding it to the standard.

Cheers,
...........................Malcolm Cohen, NAG Ltd., Oxford, U.K.
(mal...@nag.co.uk)

James Giles

unread,
Feb 21, 1997, 3:00:00 AM2/21/97
to Malcolm Cohen

Malcolm Cohen wrote:
[...]

> That is not the rationale for PURE. A PURE function is parallel-safe.

The concept of PURE functions existed long before parallel computation
was even imagined. To say that the concept only has *ONE* rationale is
just inaccurate.

One of the traditional purposes of PURE functions is to allow
optimizations which skip redundant function references when the
arguments are the same. This may not have been the motivation behind
the Fortran standard committee's version of PURE. It is a valid thing
to consider, however. Indeed, this particular use of PURE functions
predates any consideration for parallel computation.

It is unfortunate that the Fortran committee chose to define the concept
of a PURE function in a manner which is considerably different than the
rest of the computing industry. It will be seen as a foolish step.

[...]



> Instrumentation data (of a PURE function) that is not updated in a parallel
> safe manner is worthless. You really feel a strong need to instrument FORALL?

Who said that the instrumentation would be implemented in a
parallel-unsafe manner. I didn't. Anyone who actually knows about
parallel computing understands that there are ways to protect the update
of shared data - automatically, if such data is confined to certain
kinds of constraints (like instrumentation data *IS*).

And yes, instrumentation is very useful - *especially* for code which is
time-critical (like FORALL). You wouldn't have bothered to code
something with parallel operations if the code were not time-critical.
During testing and design, instrumentation tells you where such work is
most productive. All instrumentation (in parallel code or not) will
probably be turned off in production. But it's very useful to have
during development.

William Clodius

unread,
Feb 21, 1997, 3:00:00 AM2/21/97
to

Craig Burley wrote:
>
> <snip>

>
> The challenges I am guessing face X3J3/WG5 come from trying to do too
> much -- instead of just clarifying the language of the pertinent
> standards and codifying existing practice (with perhaps minor adjustments
> to make existing practice work in the context of an industry-wide
> language, rather than a vendor- or machine-specific dialect), it
> also tries to do new language design. <snip>

As an oustsider with an interest in standardization, I would like to
respond to Craig's assumption, that the only duties of a standardizaion
body is to codify and clarify existing practices. This is widely
perceived to be the only proper role of a standards body, but I beleive
it ignores realities, for several reasons.

First, it assumes that existing practice is already fairly uniform and
consistent. Such an assumption is often valid if the thing being
standardized has developed primarilly under the influence of a single
body or person, e.g., F66 was a minor generalization of IBM's Fortran
IV, Pascal was relatively well defined by Jensen and WIrth, C by
Kernigan and Ritchie. However, if the first standard is successful then
subsequent standards do not have such benefits for several reasons: the
initial standard will focus on areas where there is already consistency
so the easy work in codifying existing practices will be done; there
will be more players in the marketplace and hence more sources of
inconsistency in poorly defined aspects of the standard; and the formal
exitence of the standard can make obvious the portability problems of
deviations from the standard, so that users are less likely to request
extensions from the vendors that are not intrinsically system specific.

Second, it assumes that the players in the industry do not find it
usefull to have the standards committee do some of the design work.
There are often some aspects of standard that could benefit from large
extensions, but the costs of adding those extensions is sufficiently
large that the vendor is unlikely to add such an extension unless he is
confident that the user will take advantage of it, and the user is
unlikely to take advantage of it unless it is widely portable, a classic
chicken or the egg problem. The IEEE floating point arithmetic standard
is perhaps the best example of such a standard's committee design work.
Fortran 77's CHARACTER data type and Fortran 90's module system are
other examples of such features. The failure of the initial Pascal
standard to address such problems is perhaps the best (worst) example of
the consequences if a standard's committee is not sufficiently agressive
in its design work.

Third, it assumes that existing practice is good. Ask in comp.std.c
about LONG LONG and see what the opinions are like. Consider that IBM
and most of the other large vendors did not have a block if construct
before Fortran 77.

All that being said, i.e., that X3J3/SC22WG5 need to do some significant
design work if they are to do its job, that does not imply that at times
they are not overly ambitious in their design work, or that their
designs were always as good as they should be. In retrospect, I believe
they would have been better off doing an F95 for 84, i.e., codifying
much of an alternative standard (MIL_STD_1753), correct obvious problems
with the previous standard, and add one or two of the less controversial
proposals, delayiny subtantial revisions untill Fortran 9X. Still, there
is one other important point your assumptions ignor that made this
difficult

Fourth, it assumes that people will participate in a standardization
effort with little opportunity for innovation or ego gratification.

N8TM

unread,
Feb 22, 1997, 3:00:00 AM2/22/97
to

An extremely interesting post. A comment: People still assume that
anything that was written for IBM, including jumps into DO loops, is in
accordance with some past standard such as f66, and should still work. As
you hinted, f66 left a lot of even then current practice up for grabs, and
by the time the f77 committee went to work, a lot of necessary syntax was
vendor-dependent. Now, it appears that vendors who are serious about
support will perhaps be forced to follow the new standards.
Unfortunately, I can't even expect the facilities of g77 to be available,
so I'm a long way from becoming proficient in the newer dialects.
Tim

Craig Burley

unread,
Feb 22, 1997, 3:00:00 AM2/22/97
to

In article <330DFF...@lanl.gov> William Clodius <wclo...@lanl.gov> writes:

Craig Burley wrote:
>
> <snip>
>
> The challenges I am guessing face X3J3/WG5 come from trying to do too
> much -- instead of just clarifying the language of the pertinent
> standards and codifying existing practice (with perhaps minor adjustments
> to make existing practice work in the context of an industry-wide
> language, rather than a vendor- or machine-specific dialect), it
> also tries to do new language design. <snip>

As an oustsider with an interest in standardization, I would like to
respond to Craig's assumption, that the only duties of a standardizaion
body is to codify and clarify existing practices. This is widely
perceived to be the only proper role of a standards body, but I beleive
it ignores realities, for several reasons.

I am not assuming that the only duties of a standardization body is


to codify and clarify existing practices.

I do believe the only duties of X3J3 are, or should be, to do so.

I have no problem with another standard body being formed with the express
purpose of designing and specifying new language features for Fortran and
distributing these as standards themselves -- and, ideally, as features
in a freely available (or about as free as the standards themselves)
implementation as well.

This would avoid the overloading of a single standards body as currently
seems to be happening, allowing the decision of what goes into _the_
Fortran standard to be restricted much more to codify existing practice --
existing practice, in turn, would be more greatly influenced by the
standardization process itself, in the form of the standardization group
focused on designing new features.

Also, this approach would standard a much better chance of avoiding
the problems you cite concerning overmuch influence of a few large vendors
and the catch-22 of adding substantial new features after users are
willing to commit to using them. It also should mitigate the problem
of bad existing practice (in that the designing body can much more
quickly design a proper solution to a problem addressed by someone's
bad practice, and release it as a "new feature standard", than X3J3
ever will manage to do in its current incarnation).

Finally, it would indeed allow people with large egos (in terms of wanting
to design new features) to sit on a committee that gives them quicker
gratification (and thus exposes the entire industry, or that portion that
is more willing to take a risk on the "new features" standardlets, to
the problems that might result from that), while giving such people less
incentive to sit on the committee with the important job of deciding
what is standard Fortran. I think that might be an improvement, but since
I don't know of anyone (offhand) who has ever sat on X3J3 and has a big
ego, I can't be sure.

Another way to translate all I said above in terms of responding to your
post -- I claimed an operator was overloaded in an unfortunate way, and
you responded as if I'd said some of what it was doing was simply not
worth doing. I don't feel that way. I'd just like to see two distinct
operators doing the two distinct jobs, so it's clear to everyone what's
going on, and the industry would have more visibility into the progress
on the language-features front than it currently has.

Note that the current problem that generated this discussion is a
symptom of the above. In doing Fortran 95, X3J3/WG5 appears to be
acting mostly as a new-features group, with some nods to codifying
existing stuff (HPF) and fixing broken stuff. But, I believe the desire
to do some new features is giving people an excuse ("workload") to _avoid_
doing an example of the sort of thing they _must_ do to be a _useful_
group for standardizing the Fortran language:

They MUST specify the degree to which relying on side effects of
functions is standard-conforming (predictable, unpredictable, or
undefined).

It is less than amusing to me that the committee spends any time designing
some nifty new features and doesn't address something as fundamental
as whether functions may have side effects in various useful ways.

As a result, for the moment, X3J3/WG5 (and I'm very uncertain about
all the relationships between groups here) looks to me less like a
standardization group and more like a wayward, and not entirely useful,
new-languages-design group.

Personally, on the g77 front, I am wearing several hats to more of a
degree. To cope with existing code, I have to think in terms of codifying
(clarifying) existing practice; to handle the portability issues that
result, I sometimes have to eliminate old features (requiring compiler
options to get one behavior or the other) and supply new ones.

(E.g. I'm finally biting the bullet and inventing three new intrinsics,
REALPART, IMAGPART, COMPLEX, which do the "obvious" things with no
conversions. I'd be surprised if any practicing F77 or F90 programmer
is fully aware of exactly what REAL() and AIMAG() do with DOUBLE
COMPLEX arguments, and what CMPLX() does with DOUBLE PRECISION arguments,
on their various compilers. F90 at least makes for consistent
behavior, but unfortunately inconsistent with existing F77 compilers
and/or user expectations. And, I'm at least "documenting away" the
ability to do REAL(C) or AIMAG(C) where C is a complex type,
especially one other than COMPLEX, because of the apparent schism
between F90 and existing F77 code as to what they mean. This is a
case of both codifying/fixing existing practice and doing new design
work.)

Yes, this means I'm overloaded, but I have to be -- there's no
existing standard for a widely portable Fortran implementation that
accepts a great deal of existing code (and properly handles it),
while heading in the general direction of F90 and future standards.
I try to keep my approach to dealing with this as one that makes
careful distinction between roles, but it isn't easy.

Toon Moene

unread,
Feb 22, 1997, 3:00:00 AM2/22/97
to

Craig Burley <bur...@gnu.ai.mit.edu> wrote:

>Yes, this means I'm overloaded, but I have to be -- there's no
>existing standard for a widely portable Fortran implementation that
>accepts a great deal of existing code (and properly handles it),
>while heading in the general direction of F90 and future standards.
>I try to keep my approach to dealing with this as one that makes
>careful distinction between roles, but it isn't easy.

In fact, I wouldn't be surprised if *the most appealing innovation* in the
next issue of g77 (0.5.20) is not the support of the DEC Alpha platform, and
probably some other - inherently 64-bit - architectures, the support of
`intrinsics' normally associated with Unix (the `libU77' implementation by
Dave Love), or the optimisation enhancements, but the _documentation_.

Especially where it details the language g77 accepts, vis a vis the
requirements of the applicable standard(s), users' expectations,
ease-of-implementation and future extensibility ...

William Clodius

unread,
Feb 22, 1997, 3:00:00 AM2/22/97
to wclo...@aol.com

Craig:

Let me be clear about what I think our disagreements are

We both seem to accept that the one of the principle tasks of the
Fortran standards committees should be to clearly define the semantics
of such thing as FUNCTION calls.

From Richard Maine's comments I have the strong impression that the
standards committees have attempted to address that particular issue
several times in the past. It is not clear to me that you recognized
this.

From Richard Maine's comments I have the impression that the reason such
calls have not had their semantics well defined by the committees is
that the committees have not been able to reach a concensus on what the
semantics should be.

There are essentially two well defined extremes for the semantics of
FUNCTION calls, side effects should be allowed and not optimized away,
or side effects should not be allowed. The committee has taken a less
well defined position that some side effects are allowed provided the
user does not mind if they are optimized aways. It has not clearly
identified which side effects should be avoided as the user will almost
never want them to be optimized away, or which side effects are
permissible as the user will not in general mind if they are optimized
away. As a result the language does not require compile time error
messages or warnings for detectable side effects in procedures.

You seem to be attributing the lack of a clear definition of the
semantics of FUNCTION calls to the fact that the standards bodies are
also doing development work which distracts them from issues such as
this. I attribute the lack of a clear definition to the difficulty of
defining a meaningfull middle ground. Quoting Richard Maine

"Perhaps the more realistic viewpoint is that the committee just
couldn't reach consensus on what I'd regard as the 3 plausible fixes:

1. Limit optimizers so that function calls can be optimized away only
for the very limitted cases like those that the standard gives
examples of.

2. Point out in bold print that you can *NEVER* rely on the behavior


of
functions with side effects.

3. Or somehow compromise by allowing a large but finite and


well-defined
set of circumstances where optimization is allowed.

Option 1 is distasteful to the "speed crowd". Option 2 would admit that
too much existing code is at best questionable. And option 3 is too
hard to do (or at least I haven't seen anyone suceed)."

As I understand this and other comments by Richard Maine, the comittees
have essentially decided that they could put all other issues aside and
argue this issue till doomsday and they still would not reach a
consensus. At least for now, they have decided that it is better that
they put their effort into other issues. If that is the case, then the
additional development work that they do is not an issue.

Having said all that let me now try to identify those side effects that
a programmer has explicit control over in implementing a procedure,
i.e., I will not address out of memory conditions, floating point
exceptions etc, and what I think the language restrictions should be.
Let us see the reaction is to a well defined (I hope) intermediate
semantics.

1. SAVEd local variables: I have trouble thinking of any use for SAVEd
local variables in FUNCTIONS where the user would not mind if the side
effects associated with such variables are optimized away. I would
prefer it if the standard deprecated their use in FUNCTIONs.

2. I/O: External WRITE statements are often used for debugging code, but
in such useages optimization is rarely used and when optimization is
used it does not matter if the FUNCTION is not called due to
optimization. READ statements are far less usefull. I would prefer it if
external WRITE statements (but not external READ statements) were
generally allowed in FUNCTIONs.

3. Modifying global variables: For this to be safe under optimization
the compiler would have to do a global analysis of all global variabes
and their possible aliases. Fortran has generally not required such
analyses, and it is not clear to me whether such analyses would scale
linearly with code size. This is also a common source of obscure bugs. I
would prefer it if the standard deprecated such modifications.

4. Allowing arguments with INTENT(IN OUT): It is difficult to rationally
justify their useage as they can easilly lead to hard to find errors
under optimizations allowed by the current standard. I would prefer it
if the standard deprecated such arguments.

5. Allowing arguments with INTENT(OUT): In the absence of an exception
handling facility it is difficult to forbid this. I suspect that such
arguments are used in a manner much less subject to optimization
problems than arguments with INTENT(IN OUT). I suspect that it is also
more widely used and any attempt to forbid this would raise unacceptable
opposition. I would prefer it if the standard continued to allow them.

6. Alternate RETURN: Subject to many of the considerations of arguments
with INTENT(OUT), but already deprecated by the standard.

7. User defined procedure calls: The standard should not make a
exception for calls that under some circumstances can generate
undesirable side effects do not have such side effects in the specific
context of the call, as enforcing such a distinction is impossible as it
is equivalent to solving the halting problem. If the language is
consistent in enforcing the restrictions on function call then function
calls by function calls cannot be a source of problems. Subroutine calls
by functions should not modify SAVEd local variables, global variables,
or perform external WRITEs.

I have probably missed some sources of side effects, but as I see it
"IMPURE" functions should differ from PURE ones only in allowing
external WRITEs and arguments with INTENT(OUT). I also suspect that few
will agree with me on this.

Loren P. Meissner

unread,
Feb 22, 1997, 3:00:00 AM2/22/97
to

I don't know what you mean by "no existing standard for a widely portable
Fortran implementation." The most widely portable implementation that I am
aware of is provided by DEC, and it certainly heads "in the general

direction of F90 and future standards."
--
Loren P. Meissner
<LPMei...@msn.com>

Craig Burley <bur...@gnu.ai.mit.edu> wrote

>. . . In doing Fortran 95
=
F95 is essentially done (except for some final bureaucratic actions). The
current project is affectionately called Fortran 2000.
=

Phillip Helbig

unread,
Feb 23, 1997, 3:00:00 AM2/23/97
to

In article <330F87...@lanl.gov>, William Clodius <wclo...@lanl.gov> writes:

> There are essentially two well defined extremes for the semantics of
> FUNCTION calls, side effects should be allowed and not optimized away,
> or side effects should not be allowed. The committee has taken a less
> well defined position that some side effects are allowed provided the
> user does not mind if they are optimized aways.

> Let us see the reaction is to a well defined (I hope) intermediate
> semantics.
>
> 1. SAVEd local variables: I would


> prefer it if the standard deprecated their use in FUNCTIONs.

Good idea.



> 2. I/O: External WRITE statements are often used for debugging code,

> I would prefer it if
> external WRITE statements (but not external READ statements) were
> generally allowed in FUNCTIONs.

Right. One (both) of the subsets allows this. Nice for error messages
and it doesn't matter if a call is optimised away---no call, no error.

> 3. Modifying global variables: I


> would prefer it if the standard deprecated such modifications.

This is the basic worry that optimising will change the side effects.
It should be deprecated to keep a function more in line with the
mathematical definition, just an abbreviation for some function of the
arguments.

> 4. Allowing arguments with INTENT(IN OUT): I would prefer it


> if the standard deprecated such arguments.

Of course.

> 5. Allowing arguments with INTENT(OUT): In the absence of an exception
> handling facility it is difficult to forbid this. I suspect that such
> arguments are used in a manner much less subject to optimization
> problems than arguments with INTENT(IN OUT). I suspect that it is also
> more widely used and any attempt to forbid this would raise unacceptable
> opposition. I would prefer it if the standard continued to allow them.

Hmmm...I assume this is for error flags and such. I see the rationale
for this, but it does detract somewhat from the FUNCTION concept.

> 6. Alternate RETURN: already deprecated by the standard.


>
> 7. User defined procedure calls: The standard should not make a
> exception for calls that under some circumstances can generate
> undesirable side effects do not have such side effects in the specific
> context of the call, as enforcing such a distinction is impossible as it
> is equivalent to solving the halting problem. If the language is
> consistent in enforcing the restrictions on function call then function
> calls by function calls cannot be a source of problems. Subroutine calls
> by functions should not modify SAVEd local variables, global variables,
> or perform external WRITEs.

Could someone elaborate a bit more on this?

> I have probably missed some sources of side effects, but as I see it
> "IMPURE" functions should differ from PURE ones only in allowing
> external WRITEs and arguments with INTENT(OUT). I also suspect that few
> will agree with me on this.

I basically agree! Only the INTENT(OUT) stuff I'm not sure of. Maybe
someone could try to bring some arguments against it to help shape
opinion.

What about a function which doesn't modify a global variable, but
whose result depends on a global variable? Ths should be deprecated as
well.

The way I see it, one needs an additional category. As someone pointed
out, PURE is often thought of as an aid to parallelisation, and was
introduced into F95 for that reason, but the concept of a PURE function
goes back even further and is really something different.

We need two sets of restrictions (if the first set is fulfilled, does
this imply the second set is as well?), one for parallelisation and one
for clean code. The first set should forbid things in functions which
interfere with parallelisation. As I understand it, this is what F95
PURE is for. The second set should enforce the idea of a FUNCTION as
just an abbreviation for a function of the arguments, that is, depending
ONLY on the value of the (input) arguments and having no side effects.
Exceptions to this could be permitted if IT DOESN'T MATTER that they can
be optimised away. One is INTENT(OUT) arguments, presumable for error
flags. I would like some more discussion here. The other is allowing
WRITE or PRINT statements, for debugging and error messages, and perhaps
STOP for the same reason (why not?). Programmers not concerned at all
with parallelisation might like a way to enforce that FUNCTION code
behaves as a function in the mathematical sense. This might not be
strict enough for the parallelisation concern. (Again, if the
parallelisation restrictions are satisfied, MUST the function be pure in
the mathematical sense?)


--
Phillip Helbig Email ........... p.he...@jb.man.ac.uk
Nuffield Radio Astronomy Laboratories Tel. ...... +44 1477 571 321 (ext. 297)
Jodrell Bank Fax .................. +44 1477 571 618
Macclesfield Telex .................. 36149 JODREL G
UK-Cheshire SK11 9DL Web ..... http://www.jb.man.ac.uk/~pjh/

My opinions are not necessarily those of NRAL or the University of Manchester.
Email will COME from Hamburg for a while longer; send/reply TO either address.


William Clodius

unread,
Feb 23, 1997, 3:00:00 AM2/23/97
to wclo...@aol.com

Phillip Helbig wrote:
>
> In article <330F87...@lanl.gov>, William Clodius <wclo...@lanl.gov> writes:
>
> <snip>

> >
> > 7. User defined procedure calls: The standard should not make a
> > exception for calls that under some circumstances can generate
> > undesirable side effects do not have such side effects in the specific
> > context of the call, as enforcing such a distinction is impossible as it
> > is equivalent to solving the halting problem. If the language is
> > consistent in enforcing the restrictions on function call then function
> > calls by function calls cannot be a source of problems. Subroutine calls
> > by functions should not modify SAVEd local variables, global variables,
> > or perform external WRITEs.
>
> Could someone elaborate a bit more on this?

There is no essential difference in their side effects between

FUNCTION EXAMPLE1(X)
INTEGER :: EXAMPLE1, X
INTEGER, SAVE :: SIDE_EFFECT = 0
SIDE_EFFECT = SIDE_EFFECT + 1
EXAMPLE1 = SIDE_EFFECT
END FUNCTION EXAMPLE1

and

SUBROUTINE SIDE_EFFECT(X)
INTEGER :: X
INTEGER, SAVE :: SIDE_EFFECT = 0
SIDE_EFFECT = SIDE_EFFECT + 1
X = SIDE_EFFECT
END SUBROUTINE SIDE_EFFECT

FUNCTION EXAMPLE2(X)
INTEGER :: EXAMPLE2, X
CALL SIDE_EFFECT(EXAMPLE2)
END FUNCTION EXAMPLE2

The following function EXAMPLE3 has no side effects, but asking the
computer to show that there are no side effects in many similar cases is
asking the impossible.

SUBROUTINE POTENTIAL_SIDE_EFFECT(X, FLAG)
INTEGER :: X, FLAG
INTEGER, SAVE :: SIDE_EFFECT = 0
IF (FLAG /= 0) THEN
SIDE_EFFECT = SIDE_EFFECT + 1
X = SIDE_EFFECT
ELSE
X = 0
ENDIF
END SUBROUTINE SIDE_EFFECT

FUNCTION EXAMPLE3(X)
INTEGER :: EXAMPLE3, X
CALL POTENTIAL_SIDE_EFFECT(EXAMPLE3, 0)
END FUNCTION EXAMPLE3

For example local analysis is insufficient to show the absence of side
effects for

FUNCTION EXAMPLE4(X, FLAG)
INTEGER :: EXAMPLE4, X, FLAG
CALL POTENTIAL_SIDE_EFFECT(EXAMPLE3, FLAG)
END FUNCTION EXAMPLE4

>
> > I have probably missed some sources of side effects, but as I see it
> > "IMPURE" functions should differ from PURE ones only in allowing
> > external WRITEs and arguments with INTENT(OUT). I also suspect that few
> > will agree with me on this.
>
> I basically agree! Only the INTENT(OUT) stuff I'm not sure of. Maybe
> someone could try to bring some arguments against it to help shape
> opinion.
>
> What about a function which doesn't modify a global variable, but
> whose result depends on a global variable? Ths should be deprecated as
> well.
>

This is an area where I consider it impractical to impose restrictions.
This is attempting to require, not that the function be "PURE", but
rather that all arguments to the function be explicit. Global arguments
for Fortran functions are in effect implicit arguments. Such a
restriction is not usefull for several reasons. First, an awfull lot of
code uses global variables. Removing this capability from the language
would result in an uproar that would make the arguments over deleting
features from the language seem to be a tempest in a teapot. Second, if
the global variables can only be changed in the main program or by a
subroutine call then the compiler cannot optimize away such changes.
Third, it makes sense to write code that depends on global constants
such as SPEED_OF_LIGHT, or CHARGE_OF_AN_ELECTRON and not make them part
of the argument list. Fourth, there are numerous cases where the code
defines global variabes at the start of the program and never modifies
them later, maintaining them as arguments is a great inconvenience.

> The way I see it, one needs an additional category. As someone pointed
> out, PURE is often thought of as an aid to parallelisation, and was
> introduced into F95 for that reason, but the concept of a PURE function
> goes back even further and is really something different.
>
> We need two sets of restrictions (if the first set is fulfilled, does
> this imply the second set is as well?), one for parallelisation and one
> for clean code. The first set should forbid things in functions which
> interfere with parallelisation. As I understand it, this is what F95
> PURE is for. The second set should enforce the idea of a FUNCTION as
> just an abbreviation for a function of the arguments, that is, depending
> ONLY on the value of the (input) arguments and having no side effects.
> Exceptions to this could be permitted if IT DOESN'T MATTER that they can
> be optimised away. One is INTENT(OUT) arguments, presumable for error
> flags. I would like some more discussion here. The other is allowing
> WRITE or PRINT statements, for debugging and error messages, and perhaps
> STOP for the same reason (why not?). Programmers not concerned at all
> with parallelisation might like a way to enforce that FUNCTION code
> behaves as a function in the mathematical sense. This might not be
> strict enough for the parallelisation concern. (Again, if the
> parallelisation restrictions are satisfied, MUST the function be pure in
> the mathematical sense?)

> <snip>

I would argue no for both historical and practical reasons.

In thinking about my list of side effects some more it is obvious that I
missed some more, i.e., the STOP statement you mentioned, but also
PAUSE, and various I/O statements such as REWIND, OPEN, CLOSE,
BACKSPACE, ENDFILE, and INQUIRE. Most of these I would also deprecate in
functions. INQUIRE might be an exception.

Other restrictions are that functions which return a LOGICAL value may
not have arguments of INTENT(OUT) or INTENT(IN OUT), and that an entity
may be defined only once per statement.

James Giles

unread,
Feb 23, 1997, 3:00:00 AM2/23/97
to William Clodius

I would still argue that the Fortran 95 proposed definition of PURE is
misleading, short-sighted, and generally undesirable. It's misleading
in that few people from other language backgrounds will recognise it.
They will conclude that Fortran designers don't understand language
concepts and will use it as further ammunition to condemn the langauage.

It is short-sighted in that it is not sufficiently stringent for
parallel computing using anything but the SIMD model. Consider:

X = F(Y) + G(Z)

If X, Y, and Z are all very large arrays, and F is declared to be a PURE
function, then (under the traditional definition of PURE) a MIMD
parallel implementation would be able to run F and G in parallel (even
though G is not PURE). The semi-PURE definition of Fortran 95 PURE
functions would not permit this. Now consider:

X = F(Y)
.... lots of code not referencing X or defining Y ...

If F were PURE in the traditional sense, then the function could execute
in parallel with the following code on an MIMD implementation. This is
not possible with Fortran 95's semi-PURE functions. Finally:

X = F(Y)
.... lots of code not defining X or Y ....
Z = F(Y)

With a traditional definition of PURE, if F is pure the second call to F
could be skipped entirely and be replaced with Z = X. This would be
possible even on conventional non-parallel implementations. It would
not be possible with Fortran 95's definition of PURE

So, it seems short-sighted to define PURE functions in a manner which is
not acceptable to MIMD parallelism, nor to conventional optimization
techniques. Someday you may wish to implement Fortran using such
techniques, but the keyword PURE will already have been associated with
this other characteristic. At that point, do you define PURE_PURE
functions? Or REALLY_PURE? If you really want these semi-PURE
functions now, why not call them something more honest and suggestive of
their purpose - like "SIMD functions"?

Finally, it's undesirable since it's easier to learn and remember the
requirements of genuine PURE functions: they can't do I/O, they can't
use or define SAVEd or global variables, they can't define or re-define
their own arguments, and they can't call imPURE functions or
procedures. They *can* use other PURE functions and they *can* use
globals with the PARAMETER attribute.

With a genuine PURE definition the standard could also simplify the
rules for ordinary non-PURE functions: treating them as ordinary
procedures with just a different means of invoking them. After all, the
present peculiar rules Fortran applies to functions exist to permit
side-effects, but also to permit optimizations that really only apply to
PURE functions. Once the explicit distinction is present, you no longer
need these ambiguous rules.

William Clodius

unread,
Feb 23, 1997, 3:00:00 AM2/23/97
to

James Giles wrote:
>
> I would still argue that the Fortran 95 proposed definition of PURE is
> misleading, short-sighted, and generally undesirable. It's misleading
> in that few people from other language backgrounds will recognise it.
> They will conclude that Fortran designers don't understand language
> concepts and will use it as further ammunition to condemn the langauage.

Note that the concept of PURE is taken from HPF. I suggest that you
examine the list of participants in HPF. You will discover that one of
the most important participants is also a well known and well respected
language designer that has participated in the design of Scheme, CLOS,
Fortan 90, and Java. There may be other less well known, but respected,
names among the list.

>
> It is short-sighted in that it is not sufficiently stringent for
> parallel computing using anything but the SIMD model. Consider:
>
> X = F(Y) + G(Z)
>
> If X, Y, and Z are all very large arrays, and F is declared to be a PURE
> function, then (under the traditional definition of PURE) a MIMD
> parallel implementation would be able to run F and G in parallel (even
> though G is not PURE). The semi-PURE definition of Fortran 95 PURE

> functions would not permit this.<snip>

I am surprised at the association of your comment with this example for
several reasons.

First, under the current language definition, whether or not F and G are
pure the relative order of execution of F and G is undefined and a
compiler is allowed to assume that the result of executing F is
independent of the result of executing G and vice versa. As a result the
language allows F and G to be executed in parallel even if neither of
them are PURE. What the language does not provide is a means of
statically checking the validity of the assumption that the execution of
F and G are independent of one another in this context. It is left to
the programmer to do so. (Note I believe C and C++ have the same
semantics in this regard).

Second, any problems with the parallelization of F due to an impure G
are present even under the strictest possible definition of "PURE".
Examine the following code

U = G(Z)
V = F(Y)
X = U + V

Under the current definition of "PURE" if F and G are both PURE then the
statements

U = G(Z)
V = F(Y)

can be executed in parallel. However, if G is not PURE then the value of
the argument Y may depend on side effects introduced by G and F cannot
be executed in parallel with G under even the strictest definition of
"PURE".

> Now consider:
>
> X = F(Y)
> .... lots of code not referencing X or defining Y ...
>
> If F were PURE in the traditional sense, then the function could execute
> in parallel with the following code on an MIMD implementation. This is
> not possible with Fortran 95's semi-PURE functions.

This is not generally possible even with the "PURE"est of functions. To
see why change the above to

COMMON /PROBLEM/ X
...


X = F(Y)
.... lots of code not referencing X or defining Y ...

Parallelization is defeated not only if the execution of F could be
influenced by the side effects of subsequent code, but also if the
results of other code depends on the execution of F, or rather on the
side effect of the assignment to X that follows the execution of F(Y).

> Finally:
>
> X = F(Y)
> .... lots of code not defining X or Y ....
> Z = F(Y)
>
> With a traditional definition of PURE, if F is pure the second call to F
> could be skipped entirely and be replaced with Z = X. This would be
> possible even on conventional non-parallel implementations. It would
> not be possible with Fortran 95's definition of PURE

Only if it can be shown that the other code does not redefine X or Y.
However, if the compiler can verify that the side effects of the other
code do not redefine X or Y then it should be able to determine the
effects of any other global variables and determine if they are
parallelizable. Global variables under the current definition of PURE
are in effect implicit arguments. Requiring them to be explicit would
require additional arguments. Let us assume that F would be written to
depend on only one global variable under the current definition of PURE
(this is readilly generalize to arbitrary arguments.) Then to make it
PURE in your sense it would have to be rewritten to accept an additional
argument, say U

X = F(Y,U)


.... lots of code not defining X or Y ....

Z = F(Y,U)

If the code redefines U then Z cannot be parallelized.

>
> <snip>


>
> Finally, it's undesirable since it's easier to learn and remember the
> requirements of genuine PURE functions: they can't do I/O, they can't
> use or define SAVEd or global variables, they can't define or re-define
> their own arguments, and they can't call imPURE functions or
> procedures. They *can* use other PURE functions and they *can* use
> globals with the PARAMETER attribute.

As near as I can the the distinction between the the current definition
of PURE and what you want the definition is that you want the
restriction "they can't use or define SAVEd or global variables" while
the current definition has the looser restriction "they can't define
SAVEd or global variables." I don't see any significant differences
between the two definitions in terms of ease of learning.


>
> With a genuine PURE definition the standard could also simplify the
> rules for ordinary non-PURE functions: treating them as ordinary
> procedures with just a different means of invoking them. After all, the
> present peculiar rules Fortran applies to functions exist to permit
> side-effects, but also to permit optimizations that really only apply to
> PURE functions. Once the explicit distinction is present, you no longer
> need these ambiguous rules.
>

> <snip>

James Giles

unread,
Feb 23, 1997, 3:00:00 AM2/23/97
to William Clodius

William Clodius wrote:
>
> I am surprised at the association of your comment with this example for
> several reasons.
>
> First, under the current language definition, whether or not F and G are
> pure the relative order of execution of F and G is undefined and a
> compiler is allowed to assume that the result of executing F is
> independent of the result of executing G and vice versa. As a result the
> language allows F and G to be executed in parallel even if neither of
> them are PURE. [...]

And peculiarly, the new F95 definition of PURE makes this optimization
invalid even for PURE functions. Very strange indeed. The standard
should clean this up and only allow such reordering optimizations for
genuinely PURE functions. It was never a good idea, and was only
allowed because the committee wanted to both permit side effects *and*
permit optimizations which are only valid for PURE functions. Now that
the distinction can be made explicitly, the unsafe ambiguities should be
eliminated from the standard.


> Second, any problems with the parallelization of F due to an impure G
> are present even under the strictest possible definition of "PURE".

Only if the arguments themselves are imPUREly treated. If Y is a local
variable and F is really PURE (as opposed to the F95 definition) then
F(Y) is independent of any possible execution of G(Z).

> U = G(Z)
> V = F(Y)
>
> can be executed in parallel. However, if G is not PURE then the value of
> the argument Y may depend on side effects introduced by G and F cannot
> be executed in parallel with G under even the strictest definition of
> "PURE".

Only if Y is a global variable. If Y is local, such effects are not
only illegal, but completely impossible. This gives the user *explicit*
control over when the compiler is able to make assumptions about
parallelizing such references.

[...]


> This is not generally possible even with the "PURE"est of functions. To
> see why change the above to
>
> COMMON /PROBLEM/ X
> ...
> X = F(Y)
> .... lots of code not referencing X or defining Y ...
>
> Parallelization is defeated not only if the execution of F could be
> influenced by the side effects of subsequent code, but also if the
> results of other code depends on the execution of F, or rather on the
> side effect of the assignment to X that follows the execution of F(Y).

If "lots of code not referencing X or defining Y" contains a call to
some procedure that references the COMMON block, then it obviously
*isn't* "lots of code not referencing X or defining Y" is it? I refuse
to get into a discussion about exceptions to my examples which violate
the assumptions the examples are *explicitly* based upon.

[...]



> > Finally:
> >
> > X = F(Y)
> > .... lots of code not defining X or Y ....
> > Z = F(Y)
> >
> > With a traditional definition of PURE, if F is pure the second call to F
> > could be skipped entirely and be replaced with Z = X. This would be
> > possible even on conventional non-parallel implementations. It would
> > not be possible with Fortran 95's definition of PURE
>
> Only if it can be shown that the other code does not redefine X or Y.

Which is trivially easy if X and Y are local variables. An explicit
step I would take if I wanted my code to be optimizable in the above
way. The objective is not to disallow optimizations when the user can
explicitly code to allow them. At the same time, it must be allowed for
the user to explicitly violate the constraint (and not be optimized).
In either case, the optimizations should *never* occur when it's unsafe,
and a good implementation should *always* optimize when it is safe. And
the user should have explicit controls over the choice.

[...]

>
> X = F(Y,U)
> .... lots of code not defining X or Y ....
> Z = F(Y,U)
>
> If the code redefines U then Z cannot be parallelized.

Same example. My example of F(X) is completely general in that X may be
a compound data structure. Anything I said about X applies to any
additional arguments. equally.

[...]

>
> As near as I can the the distinction between the the current definition
> of PURE and what you want the definition is that you want the
> restriction "they can't use or define SAVEd or global variables" while
> the current definition has the looser restriction "they can't define
> SAVEd or global variables." I don't see any significant differences
> between the two definitions in terms of ease of learning.

I do. My first assumption when told that a function was PURE would be
than moving it's references around would have no effect on their
meaning. To find that such an assumption was false would come as a
jolt. Completely unexpected and *very* subtle errors can be expected
with this feature.

William Clodius

unread,
Feb 23, 1997, 3:00:00 AM2/23/97
to

James Giles wrote:
>
> <snip>

>
> And peculiarly, the new F95 definition of PURE makes this optimization
> invalid even for PURE functions. Very strange indeed. The standard
> should clean this up and only allow such reordering optimizations for
> genuinely PURE functions. It was never a good idea, and was only
> allowed because the committee wanted to both permit side effects *and*
> permit optimizations which are only valid for PURE functions. Now that
> the distinction can be made explicitly, the unsafe ambiguities should be
> eliminated from the standard.

Maybe you know the F95 standard better than me. Has the order of
evaluation been defined for the example of interest? Or should you
rephrase your comments to say something to the effect that letting this
order of evaluation be undefined, and hence allowing this optimization
while trusting to the judgement of the programmer, is error prone and
should not be allowed by the standard. I might agree with this argument,
but I still say that under F95's rules a compiler writer would find that
this optimization is valid.

I should note that there are essentially three stands on this language
design issue. Either do not allow impure functions at all, allow impure
functions but do not define an evaluation order withing a statement, or
allow impure functions and define a detailed evaluation order within a
statement.

Not allowing impure functions at all is the safest choice, however,
users, perhaps due to their training, generally complain about being
restricted to pure functions.

If the evaluation order is not specified (as in Fortran), users must
learn to avoid interacting expressions within a statement. This is error
prone, but the rule is simple.

If the evaluation order is specified, users can have interacting
expressions within a statement. However, the rules of evaluation order
can be subtle and error prone in their own right. It is not clear to me
that having the order of evaluation defined within a statement is a
significant gain in language design.

As to the statement "the committee wanted to both permit side effects
*and* permit optimizations which are only valid for PURE functions." I
would rephrase this as "the committee wanted to both permit side effects
*and* permit optimizations which are error prone except for PURE
functions." Unfortunately, I can think of functions with side effects
where such optimizations are valid, but I cannot think of a reasonably
straightforward rule that can be used by a compiler to distinguish such
functions from those functions with side effects where such
optimizations are invalid. Every rule I can think of either explicitly
assumes a solution to the halting problem, or requires such a complex
interprocedural analysis that I am almost certain that if it does not
implicitly assume a solution to the halting problem, is not solvable in
a time less than quadratic in code size, and probably requires more than
exponential time to solve. In the committee's defense, even by Fortran
66 there were a significant number of functions with side effects so
that outlawing side effects would have invalidated a significant
fraction of the heritage code, a significant Fortran's user community
has always been performance driven and any limitation in optimization is
strongly opposed by that community, defining an evaluation order will
fix the heritage code problem only for that fraction of the heritage
code that assumed the selected evaluation order, and the set of those
who can reliably use detailed rules of evaluation order may well be only
slightly larger relatively than the set of those who can use the current
ill defined rules.

>
> > Second, any problems with the parallelization of F due to an impure G
> > are present even under the strictest possible definition of "PURE".
>
> Only if the arguments themselves are imPUREly treated. If Y is a local
> variable and F is really PURE (as opposed to the F95 definition) then
> F(Y) is independent of any possible execution of G(Z).

But all you required was that F (and only F) not have direct access to
the global variables. If it is to have access to such variables through
its arguments, the natural thing to do is to leave them as global
variables, rather than cluttering up the argument lists of impure
procedures. To obtain the optimizations that you desire I would have to
modify any impure procedure that accessed Y so that it accessed it
through its arguments.

> <snip>

No time to address the other issues. Sorry.

James Giles

unread,
Feb 24, 1997, 3:00:00 AM2/24/97
to William Clodius

William Clodius wrote:
[...]
> ... Unfortunately, I can think of functions with side effects

> where such optimizations are valid, but I cannot think of a reasonably
> straightforward rule that can be used by a compiler to distinguish such
> functions from those functions with side effects where such
> optimizations are invalid. Every rule I can think of either explicitly
> assumes a solution to the halting problem, or requires such a complex
> interprocedural analysis that I am almost certain that if it does not
> implicitly assume a solution to the halting problem, is not solvable in
> a time less than quadratic in code size, and probably requires more than
> exponential time to solve. ...

There's a much simpler solution. Let the user declare that a function
is PURE and allow all optimizations for such functions. Assume that
functions not declared PURE aren't. No complicated interprocedural
analysis is needed or even to be considered.

No *existing* program can rely on behavior which would contradict the
above set of rules (if it did, it would be non-standard right now - not
to mention inconsistent and unreliable).

You could add the rule that no single expression may contain more than
one imPURE function (all the others - including the imPURE one may
execute safely in *any* order under this constraint). This may be going
too far, but it would completely eliminate any ambiguities deriving from
execution order of Functions.

Why is there such resistence to explicit user control over his/her
program's behavior? Is it the assumption of the committee that users
are not capable of exercising such control and that *ambiguous* rules
which deny the user such control are to be preferred?

[...]

> But all you required was that F (and only F) not have direct access to
> the global variables. If it is to have access to such variables through
> its arguments, the natural thing to do is to leave them as global
> variables, rather than cluttering up the argument lists of impure

> procedures. ...

If a function is genuinely pure, it can operate in any relative order to
any other function or procedure in the program no matter how impure
those other procedures might be. The only constraint (and only in some
implementations) is that its arguments must themselves be unmodified by
code running in parallel to the PURE function. That is trivially the
case for those MIMD parallel implementations which pass arguments as
copy-in/copy-out already (since the code is often running on separate
computers, this is most efficient anyway). Of course, a PURE function
would never copy-out.

Implemenations which do not use copy-in/copy-out parameter passing can
determine safe ranges for parallel operation using long established data
flow analysis which does not need to be particularly sophisticated: for
F(Y), if Y is not a local you simply assume that all imPure procedure
calls generate a dependency. Since the user has the explicit option to
make such variables local, such dependencies can be minimized at will.

Phillip Helbig

unread,
Feb 24, 1997, 3:00:00 AM2/24/97
to

In article <331080...@lanl.gov>, William Clodius <wclo...@lanl.gov> writes:

> Phillip Helbig wrote:
> >
> > In article <330F87...@lanl.gov>, William Clodius <wclo...@lanl.gov> writes:
> > What about a function which doesn't modify a global variable, but
> > whose result depends on a global variable? Ths should be deprecated as
> > well.
> >
>
> This is an area where I consider it impractical to impose restrictions.
> This is attempting to require, not that the function be "PURE", but
> rather that all arguments to the function be explicit. Global arguments
> for Fortran functions are in effect implicit arguments. Such a

What about a restriction saying that if the result of a PURE (or
whatever) function depends on global quantities, then these quantities
should be CONSTANTS an not variables? Still probably too restrictive,
but hints more at what I was thinking. I guess people would still need
to read in global quantities from a file, meaning they must be
variables.

A while back there was something like INTENT(OUT) for global variables
in a module being discussed, the idea being that if a routine can access
variables in a module, it might be a good idea to have the possibility
to make them READONLY. This would allow a sensible implementation of
the restriction discussed above, making side effects less dangerous and
allowing global variables.

Comments?

Ron Shepard

unread,
Feb 24, 1997, 3:00:00 AM2/24/97
to

In article <330F87...@lanl.gov>, William Clodius <wclo...@lanl.gov> wrote:

[...]


>1. SAVEd local variables: I have trouble thinking of any use for SAVEd
>local variables in FUNCTIONS where the user would not mind if the side
>effects associated with such variables are optimized away. I would

>prefer it if the standard deprecated their use in FUNCTIONs. [...]

I haven't followed all of this thread, but I can give an example of a
simple function in which side effects involving local saved variables are
crucial. I think someone else already mentioned random number functions.
This one is not random, but it is similar in spirit (written in f77
dialect):

integer function ntimes()
integer ncount
save ncount
data ncount /0/
ncount = ncount + 1
ntimes = ncount
return
end

Certainly the side effect cannot be optimized away. There are, of course,
other ways to accomplish such a counting operation. Would you prefer that
such functions be deprecated?

$.02 -Ron Shepard

Malcolm Cohen

unread,
Feb 25, 1997, 3:00:00 AM2/25/97
to

In article <330DDE...@cris.com>, James Giles <JGi...@cris.com> wrote:
>Malcolm Cohen wrote:
>[...]
>> That is not the rationale for PURE. A PURE function is parallel-safe.
>
>The concept of PURE functions existed long before parallel computation
>was even imagined. To say that the concept only has *ONE* rationale is
>just inaccurate.

I did not capitalise "PURE" just for the sake of exercising my Shift key - I
did it to indicate PURE functions in HPF 1.0, HPF 1.1 (and the coming HPF 2.0)
and in the almost official F95.

The HPF document says
"The freedom from side effects of a pure function allows the function to be
invoked concurrently in a FORALL without such undesirable consequences as
nondeterminism, and additionally assists the efficient implementation of
concurrent execution. Syntactic constraints (rather than semantic
constraints on behavior) are used to enable compiler checking."

[...]


>This may not have been the motivation behind
>the Fortran standard committee's version of PURE.

A lot of the motivation on WG5 (the international Fortran committee) for the
standardisation of PURE was the adoption of existing practice.

[...]


>It is unfortunate that the Fortran committee chose to define the concept
>of a PURE function in a manner which is considerably different than the
>rest of the computing industry. It will be seen as a foolish step.

"X3J3 can do no right"?

If we standardise existing practice we are told that existing practice is
foolish, if we diverge from existing practice [presumably because we think
existing practice is not good enough] we are castigated for doing language
design.

Anyway, as it has been pointed out, the authors of HPF included those who are
indeed familiar with the "rest of the computing industry" - in particular, with
the functional languages. The fact that Fortran has "PURE" functions that are
actually rather useful will not, IMO, be regarded by posterity as a mistake.
[IMO it is far less of a misnomer than "PARAMETER" or "const"].

>J. Giles
>Ricercar Software

William Clodius

unread,
Feb 25, 1997, 3:00:00 AM2/25/97
to

Ron Shepard wrote:
>
> <snip>

>
> I haven't followed all of this thread, but I can give an example of a
> simple function in which side effects involving local saved variables are
> crucial. I think someone else already mentioned random number functions.
> This one is not random, but it is similar in spirit (written in f77
> dialect):
>
> integer function ntimes()
> integer ncount
> save ncount
> data ncount /0/
> ncount = ncount + 1
> ntimes = ncount
> return
> end
>
> Certainly the side effect cannot be optimized away. There are, of course,
> other ways to accomplish such a counting operation. Would you prefer that
> such functions be deprecated?
>
> $.02 -Ron Shepard

The problem is that your statement above is incorrect. The standard
right now appears to allow function calls that invoke this sort of side
effect to be optimized away, and users that write such code are writing
code that is allowed, but not defined. To be more explicit most users
would expect such a code to dount the number of times the function is
(nominally) invoked, but at best it counts the number of times the
function is executed. The two semantics differ in the presence of
optimization. Note Richard Maines's earlier comment about random number
generators, "The one case where it most often arises in practice is with
things like random number generators coded as functions, and those are
so subject to trivial optimizations in common cases (like do i=1,1000;
a(i)=myrand(); end do) that I don't think you are going to get a ruling
that they are ok." which in this context implies that invocations of
random number generator functions, which rely on this sort of side
effect, are not ok, i.e., not well defined . The only well defined way
to do this sort of thing in Fortran is to use a subroutine call not a
function call.

If the standard does not define such code and a way could be provided to
identify and deprecate undefined code then I would prefer that the
standard deprecate it.

James Giles

unread,
Feb 25, 1997, 3:00:00 AM2/25/97
to

William Clodius wrote:

[...]



> If the standard does not define such code and a way could be provided to
> identify and deprecate undefined code then I would prefer that the
> standard deprecate it.

I too would prefer that the standard eliminate the feature if it can't
specify an unambiguous meaning. It is preferable that functions not be
allowed side effects at all if the standard is going to explicitly
permit ambiguous behavior with them.

However, the opportunity exists to remove the ambiguity and specify
clear meanings. That is: only allow optimizations which elide the
function call to be applied when the function is explicitly declared
PURE (and fix PURE so that it's really safe to elide calls to them when
the function value can be otherwise obtained).

For all other calls, enforce the standard requirement that the only way
to get the value of a function is to execute it. In section 2.4.3.3 of
the standard: "The value of a function result is determined by the
execution of the function." That's the *only* place in the standard
which states how to find the value of a function. That means that
execution is the *only* standard way to obtain the value of the
function.

This still leaves the question of what order functions are called when
several of them are present in the same statement. Officially, this is
not ambiguous since the standard already prohibits them from affecting
each other. (However, the commonly offered example of a random number
generator violates this constraint. Still, requiring all the calls to
actually be made probably satisfies the intent of most users in this
instance, regardless of order.)

James Giles

unread,
Feb 25, 1997, 3:00:00 AM2/25/97
to Malcolm Cohen

Malcolm Cohen wrote:

[...]



> Anyway, as it has been pointed out, the authors of HPF included those who are
> indeed familiar with the "rest of the computing industry" - in particular, with

> the functional languages. ...

Argument to authority is irrelevant, irrational, and is ignored by all
intelligent people. Especially since we can have no idea how those
particular cited experts actually felt about the feature in question.
They may have vehemently opposed the present definition of PURE when it
was debated in committee. Do you have independent commentary from these
people on this particular issue? (Their comments, rather than their
identities, might be quite interesting.)

> ... The fact that Fortran has "PURE" functions that are


> actually rather useful will not, IMO, be regarded by posterity as a mistake.

I already regard it as a mistake. PURE functions, as the proposed
standard presently defines them, strongly inhibit MIMD parallelism on a
distributed computing environments. Since they can depend upon
side-effects (even though they commit none) there are data dependencies
that make many common parallel optimizations unprofitable. The fact
that most of these dependencies are spurious (that is, they don't
matter, but there's no way for the compiler to tell that) makes it
worse. What do I call the genuinely PURE functions I really want now
that the PURE keyword officially refers to these semi-PURE
monstrocities?

I propose a test. Let's build a Fortran implementation which has both
genuinely PURE functions and the semi-PURE functions of the F95
proposal. Let's see how many people really use each one. I suspect
that few programmers really want or need the dependence on global
variables that the present F95 proposal allows. Until reading the
standard document, it would never have even occured to me that it was
possible! I would certainly never use such dependencies. I want a way
to tell the compiler that I didn't (so it can *really* parallelize my
code).

Craig Burley

unread,
Feb 25, 1997, 3:00:00 AM2/25/97
to

Craig Burley <bur...@gnu.ai.mit.edu> wrote

Sorry, I guess I was unaware that DEC Fortran ran on SPARC,
MIP, ix86, m68k, A29K, HP-PA, 1750a, ARM, i860, i960, m88k,
ns32k, powerpc, pyramid, romp, rs6000, and so on.

But, then, I certainly haven't been able to keep up with DEC's
progress in defining its Fortran language, and certainly expect
they've had to address some of the problems of creating a more
portable compiler than, say, just a VAX or PDP-11 compiler.

However, I'm unaware of the extent to which DEC has chosen to
ensure that all DEC Fortran compilers default to accepting a
_consistent_, portable, yet useful dialect that works on all
the machines to which they currently, or are likely to, port
their compilers.

I do know I'm trying to do this with g77, out of necessity, due
to the nature of free software in general and Project GNU in
particular.

(GNU strongly wants to avoid telling people that a given
construct means one thing on one kind of machine and another
on another, when it comes to defining its languages. There
are of course, certain unavoidable problems stemming from
poor design choices and compatibility with the past.)

Anyway, to me, a widely portable implementation of Fortran would
handle this code the same way on all systems:

DOUBLE COMPLEX Z
REAL R
Z = (3.3D0, 4.4D0)
R = Z
CALL DUMDUM(Z, R) ! Might not be necessary, but...
R = REAL(Z) - R
IF (R .NE. 0.) PRINT *, 'REAL() is Fortran 90'
IF (R .EQ. 0.) PRINT *, 'REAL() is not Fortran 90'
R = 4.4D0
CALL DUMDUM(Z, R) ! Might not be necessary, but...
R = AIMAG(Z) - R
IF (R .NE. 0.) PRINT *, 'AIMAG() is Fortran 90'
IF (R .EQ. 0.) PRINT *, 'AIMAG() is not Fortran 90'
END
SUBROUTINE DUMDUM(Z, R)
DOUBLE COMPLEX Z
REAL R
END

(Note that the question being asked by the above code is
_not_ "what is the precision of various intrinsics", though
that's how it answers the real question -- which is, "what
type is REAL(Z), where Z is DOUBLE COMPLEX, and AIMAG(Z)
as well?". Admittedly relying on precision means relying
on quality of implementation, but that doesn't change the
issue.)

And, in my little bitty test suite, I certainly have code
that assumes it is being compiled by a compiler that would
print "REAL() is not Fortran 90" above!!

This is just one, fairly minor, example of the sorts of problems
_real_ implementors run into trying to deal with _existing_ code
(which is technically non-standard according to F77, but in
practice quite portable) in combination with working towards
full support of F90, and most of them don't have to worry about
widespread portability. There are others, I'm sure, that I
haven't discovered yet, due to g77's relative youth and
inexperience.

In the above context, it is hard to see just how useful a
standardization process can be, especially when it can't
produce a specification of, e.g., whether functions can
have side effects _according to the existing language of
the standard_. (I know it's _useful_, because standards
themselves are, when properly produced, extremely important.
I'm questioning the _degree_ of usefulness of a particular
standards process, in this case, the Fortran ones that I
admittedly have little knowledge of, other than what I see
happening here and on f90-interp.)

William Clodius

unread,
Feb 25, 1997, 3:00:00 AM2/25/97
to

James:

I am getting very frustrated with this debate for several reasons:

1. Any discussion now is moot. The HPF definition of PURE was first publicized about 1993, the Fortran standardization committee
adopted it for Fortran 95 in 1994, and announced that in comp.lang.fortran as part of their summary of their meetings. The first draft
of Fortran 1995 was published in early 1995 with requests for comments announced in a number of forums including comp.lang.fortran. The
final draft was sent out for a vote by ISO in late 1996. While a little time remains till the end of the ISO ballot, I suspect that
most countries by now have posted their votes, and the remaining countries will not be influenced by what we say here. The general
public has officially had over a year and a half to comment on this topic. An observant follower of Fortran has really had about four
years to comment. The number of changes between F90 and F95 were small and easy to identify and comment on. What were you doing during
this time?

2. The discussion has developed in ways that I feel cannot be appropriately addressed in this forum. In particular I have the strong
impression that you have not read the current draft, the HPF standards, any of the HPF texts, or the recent Fortran 95 text by Metcalf
and Reid. There appears to be too much background that needs filling in, and it needs to be presented in a more orderly fashion than
this forum provides.

3. A lot of the discussion rests on matters of philosophy or opinion for which no substantive data exists to change our stands. SOme
examples,

A. I suspect that users problems with side effects most often due to several problems, i.e., failures to recognize that side effects
are important, failures to recognize what causes side effects, failures to document that a procedure causes side effects, and failures
to recognize that a procedure is influenced by a given side effect. The documentation provided by PURE, in any form, will at least
partially remind users of these points. I therefore regard PURE as an improvement. I do not regard defining the order of evaluation as
a significant improvement as it does not address the above problems. I do not know what you view as the most common sources of problems
with side effects.

B. As to whether users will be influenced by the name to make the unreasonable assumption that all arguments are explicit, you know as
well as I do what assume makes of you and me, they should learn to RTFM.

C. As to optimization, if I were an implementor and thought that there were any significant performance gains in restricting PURE to
mathematically pure functions, given Fortran's market I would have fought like **** for mathematically pure functions. There were a
significant number of implementors involved with HPF and F95 and remarkably little discord. They may have accepted the current version
of PURE because of its flexibility, and because the preferred Fortran 90 style is to rely on modules for global data, and the directed
acyclic graph nature of module useage in Fortran makes it much easier for the compiler to recognize the presence of data dependencies,
or the lack thereof, than for COMMON BLOCKs, or C includes. I view the problems with optimiztion as due to the presence of functions
with side effects, and as long as they are present it is not obvious to me that the amount of effort necessary to make code
parallelizable is significantly different for mathematically pure functions as opposed to Fortran's "PURE" procedures (has it been
mentioned here that subroutines can also be pure?). To achieve optimization for mathematically pure functions, you need to not only
ensure that all non-constant data is passed as an argument to the pure function, you have to ensure for all arguments that they are not
accessible as a modifiable global variables to any of the procedures in the block of code you want to parallelize, and are not passed
as an arguments with intent OUT, INOUT, or unspecified to any of the procedures in the block of code you want to parallelize. To
achieve optimization for F95's PURE functions, you have to ensure for all arguments (and global variables accessible to that function)
that the compiler can determine that they are not accessible as a modifiable global variables to any of the procedures in the block of
code you want to parallelize, and are not passed as arguments with intent OUT, INOUT, or unspecified to any of the procedures in the
block of code you want to parallelize. If I were writing an F95 or HPF compiler, in a *.mod file or other file I would keep all the
necessary information to do the analysis for Fortran's PURE procedures.

Malcolm Cohen

unread,
Feb 26, 1997, 3:00:00 AM2/26/97
to

In article <331337...@cris.com>, James Giles <JGi...@cris.com> wrote:
>only allow optimizations which elide the
>function call to be applied when the function is explicitly declared
>PURE

This is IMO far too restrictive.

>For all other calls, enforce the standard requirement that the only way
>to get the value of a function is to execute it. In section 2.4.3.3 of
>the standard: "The value of a function result is determined by the
>execution of the function." That's the *only* place in the standard
>which states how to find the value of a function. That means that
>execution is the *only* standard way to obtain the value of the
>function.

This is missing the point:
"7.1.7.1 Evaluation of operands

It is not necessary for a processor to evaluate all of the operands of an
expression, or to evaluate entirely each operand, if the value of the
expression can be determined otherwise."

Requiring all function references to be executed would slow down existing
programs. Not acceptable.

James Giles

unread,
Feb 26, 1997, 3:00:00 AM2/26/97
to Malcolm Cohen

Malcolm Cohen wrote:
>
> This is missing the point:
> "7.1.7.1 Evaluation of operands
>
> It is not necessary for a processor to evaluate all of the operands of an
> expression, or to evaluate entirely each operand, if the value of the
> expression can be determined otherwise."
>
> Requiring all function references to be executed would slow down existing
> programs. Not acceptable.

No it wouldn't. Any existing program which can tollerate it's functions
*not* being executed must either already have pure functions or is not
dependent on the impurities they have (and therefore can be recoded as
pure). In any case, if they are *not* already pure, the optimization of
eliminating thier references is unsafe *now*.

In any case, I did not ignore the standard quote you mention above. The
Fortran 77 standard did *not* contain the phrase "or to evaluate
entirely each operand," which exists explicitly to permit the kind of
optimizations that are *only* valid for PURE functions. The requirement
that the only way to acquire the value of a function is to execute it
was always there before F90, and is still the proper rule for any
function not declared PURE in F95. In the meantime, I would not buy a
compiler which did not execute functions that might have important
side-effects. It's unsafe.

James Giles

unread,
Feb 26, 1997, 3:00:00 AM2/26/97
to

To Clodius:

You are confusing two distinct issues and arguing them as one. I should
long ago have separated them, but as they both pertain to the use of the
PURE attribute in the language, I've left them together.

1) Fortran permits functions which are *not* pure. It also permits
optimizations to the way these functions are referenced which are *not*
safe for such impure functions. With the advent of the PURE attribute
(whatever its failings) this unsafe treatment of impure functions is no
longer justifiable. It should, therefore, be explicitly forbidden.

Now, I would also accept the constraint that impure functions are not
allowed at all - if you really think that the committee would go for it.
Indeed, any reasonable alternative that eliminates the present poor
design would be all right with me. My only concern here is that the
committee not (explicitly and unnecassarily) permit blatantly
undesirable behavior. This has nothing to do with the specific
definition of PURE or its effect on parallel computing, optimizations,
etc.. It is a distinct issue.

Are you really arguing in favor of continued standard-conforming status
of blatantly unsafe optimizations? Do you really think this is a case
of "user beware - because the design is perfect and any problems you
have are your own fault"?

2) The fact that PURE, as presently defined in HPF and F95, actually has
imPURE properties which inhibit parallel computation *and* lead to
possible users errors is not a controversial observaton. In fact, so
far there has never been a single person in this discussion who tried to
deny either property of this feature.

> 2. The discussion has developed in ways that I feel cannot be
> appropriately addressed in this forum. In particular I have the strong
> impression that you have not read the current draft, the HPF standards, any of the HPF texts, or the recent Fortran 95 text by Metcalf

> and Reid. ...

I have read the HPF standard (in draft form), and the F95 standard (the
"working document"). Both are full of obvious errors and omissions.
How is a reader of such things expected to tell that an obvious property
of PURE functions was being *deliberately* omitted.

In any case, whether I've read these discussions or not is *irrelevant*
(and your mentioning it is an obvious attempt to discredit without
bothering to address the actual *technical issues).

...


> A. I suspect that users problems with side effects most often due to
> several problems, i.e., failures to recognize that side effects
> are important, failures to recognize what causes side effects,
> failures to document that a procedure causes side effects, and failures
> to recognize that a procedure is influenced by a given side effect.
> The documentation provided by PURE, in any form, will at least
> partially remind users of these points. I therefore regard PURE as
> an improvement.

PURE is an improvement. But it's usefulness is severly impaired unless
it's used to clarify the implementation of imPURE functions (point one,
above) *and* it's possible to use them for practical optimizations and
parallelizations. In this latter, its an incomplete success. One which
may seem *very* incomplete if future programming paradigms shift toward
MIMD parallel computing.


> ... They may have accepted the current version


> of PURE because of its flexibility, and because the preferred Fortran
> 90 style is to rely on modules for global data, and the directed
> acyclic graph nature of module useage in Fortran makes it much easier

> for the compiler to recognize the presence of data dependencies,...

Nope. The use of modules is no different than the use of common blocks
with respect to data dependencies. If your compiling procedure X, which
calls F95-PURE function A and imPURE subroutine B, you have to assume A
depends on B - there is no possible compile-time information one way or
the other. This is true whether A depends on B through common or
modules - or not at all. The DAG property of module dependencies is
irrelevant to this issue.


> ... I view

> the problems with optimiztion as due to the presence of functions

> with side effects, ...

The problems are not due just to *functions* with side-effects. The
problems are due to *anything* with side-effects. Including an
assignment to a global in the current context introduces a possible
dependency (this is one that's more in direct control because it's
explicitly visible in the local context). Any assignment, anywhere, to
a global variable is a possible dependency for an F95-PURE function. So
an operation, anyhwere, that might contain such an assignment is a new
entry in the dependency graph for an F95-PURE function call.

> ... and as long as they are present it is not obvious

> to me that the amount of effort necessary to make code
> parallelizable is significantly different for mathematically pure
> functions as opposed to Fortran's "PURE" procedures (has it been

> mentioned here that subroutines can also be pure?). ...

As long as you're willing to restrict you idea of useful parallelization
to SIMD style parallel operations invoked by FORALL style directives,
there is not much difference. The F95 definition of PURE gives
sufficient guarantees for that kind of parallelism to operate safely.
As soon as you expand your horizons to more general forms of
parallelism, the F95 definition of PURE becomes very restrictive. It is
no longer possible to perform many important optimizations at all, and
most others are severely inhibited. Any operation that *might* cause a
side-effect (whether it really does or not) has to be regarded as a
dependency for all functions - including (so-called) PURE ones.

Craig Burley

unread,
Feb 27, 1997, 3:00:00 AM2/27/97
to

In article <5eujll$e...@red.nag.co.uk> mal...@sedi8.nag.co.uk (Malcolm Cohen) writes:

>It is unfortunate that the Fortran committee chose to define the concept
>of a PURE function in a manner which is considerably different than the
>rest of the computing industry. It will be seen as a foolish step.

"X3J3 can do no right"?

If we standardise existing practice we are told that existing practice is
foolish, if we diverge from existing practice [presumably because we think
existing practice is not good enough] we are castigated for doing language
design.

Yes, this is very frustrating.

The keyword "PURE" was basically (inadvertently, I think) hijacked
and now X3J3 has the choice of either blessing it (which is probably
best, and what it appears to be doing) or taking PURE back to mean
what Giles wants (which would render existing, working, code using
HPF PURE non-conforming, perhaps -- I don't know -- subtly buggy?).

GNU Fortran will solve the problem by also blessing HP's PURE and
introducing a new keyword to indicate what Giles wants:

VIRGINAL

;-)

James Giles

unread,
Feb 27, 1997, 3:00:00 AM2/27/97
to Craig Burley

Craig Burley wrote:

[...]

> The keyword "PURE" was basically (inadvertently, I think) hijacked
> and now X3J3 has the choice of either blessing it (which is probably
> best, and what it appears to be doing) or taking PURE back to mean
> what Giles wants (which would render existing, working, code using
> HPF PURE non-conforming, perhaps -- I don't know -- subtly buggy?).

No subtlety about it. Really PURE functions are easier for the compiler
to automatically enforce than the semi-PURE functions presently
proposed. So, it would not make any HPF functions buggy (subtly or
otherwise), but it might make some of them completely non-functional
(literally, well they're non-'functional' now if they use the allowed
impurities). I suspect that few *good* programmers use the impurities
allowed by the standard anyway. Any that do: well they'll have to
change their declarations to read SIMD rather than PURE (assuming you
still want to support semi-PURE at all). Big deal.

William Clodius

unread,
Feb 27, 1997, 3:00:00 AM2/27/97
to

James Giles wrote:
>
> <snip>

>
> 2) The fact that PURE, as presently defined in HPF and F95, actually has
> imPURE properties which inhibit parallel computation *and* lead to
> possible users errors is not a controversial observaton. In fact, so
> far there has never been a single person in this discussion who tried to
> deny either property of this feature.
>
> <snip.

To the best of my (albeit imperfect) memory no one else other than the
two of us has bothered to discuss the optimizability of PURE functions.
Has there been any one else that has confirmed either property of this
feature? I would prefer comments by someone who has worked on a compiler
that implements PURE as they have probably thought more about this
aspect than either of us. About the only F90+ compiler writer that has
commented on our thread recently has been Malcolm Cohen, and he has
commented on other issues not this one.

William Clodius

unread,
Feb 27, 1997, 3:00:00 AM2/27/97
to

James Giles wrote:
>
> <snip>

>
> 2) The fact that PURE, as presently defined in HPF and F95, actually has
> imPURE properties which inhibit parallel computation *and* lead to
> possible users errors is not a controversial observaton. In fact, so
> far there has never been a single person in this discussion who tried to
> deny either property of this feature.
> <snip>

I have not, to the best of my poor memory and communication skills,
disagreed that there are error prone aspects of Fortran and have tried
to state that the impurity of functions is one source of errors in
Fortran that I dislike. What I have tried to state is that I do not
believe that having PURE specify mathematically pure procedures and
having a well defined evaluation order is a cure all for these errors.
Unfortunately a proper evaluation of the relative effect on user errors
of mathematically pure procedures vs Fortran's PURE procedures requires
a full understanding of the pertinent user psychology for which I know
of no data. Such data would have to identify the causes of the errors,
i.e., are they do to an assumed order of evaluation or to a failure to
recognize that the order of evaluation is important, if due to an
assumed order of evaluation what are their assumptions and will their
assumptions change if the standard defines the order of evaluation,
which of Fortran's PURE or mathematically pure, if either, is more
likely to cause users to recognize the importance of side effects. The
data would also have to evaluate whether rewritten code is more error
prone than code written from scractch per line of code, an evaluation of
how much existing code would have to be rewritten to match the desired
style, what influence should heritage code have on the standard, etc..
Because I lack such data, I therefore rely on assumptions that I find
plausible, which suggest that a well defined evaluation order will have
essentially no effect on the error proneness of functions with side
effects, although having an evaluation order defined might be a
convenience for those who would not make errors involving side effects
in that they could then write more concise code. My assumptions also
suggest that there will be no significant difference between the two
concepts of pure in helping users recognize the importance of side
effects.

For me then, which form of "PURE" is preferable then becomes a question
not of error proneness, but of a tradeoff between coding convenience
vesus optimizability. There is no question to me that Fortran's "PURE"
is more convenient in coding as it avoids having to maintain additional
arguments. My programming style for functions has been essentially
identical to what is expected for PURE functions, so that to have highly
parallelized code between I/O and subroutine calls I simply have to add
the PURE attribute where appropriate in my function definitions. I have
to do significantly more work to convert my functions to become
mathematically pure or to make a significant number of my subroutine
calls PURE in Fortran's sense. The global modifications necessary to
make my functions mathematically pure will be error prone in and of
themselves. (Personally I find that I make more errors per line of
revised code than per line of original code.)

The relative benefits in terms of optimization I will leave ot others
for now.

Walter Spector

unread,
Feb 27, 1997, 3:00:00 AM2/27/97
to

William Clodius wrote:

>
> James Giles wrote:
> > 2) The fact that PURE, as presently defined in HPF and F95, actually has
> > imPURE properties which inhibit parallel computation *and* lead to
> > possible users errors is not a controversial observaton. In fact, so
> > far there has never been a single person in this discussion who tried to
> > deny either property of this feature.
> To the best of my (albeit imperfect) memory no one else other than the
> two of us has bothered to discuss the optimizability of PURE functions.

This is a topic which has been an interest of mine over
the years. I like to equate what PURE is attempting to do
with the Cray VFUNCTION capability. (This allows user-written
vector functions - no side effects allowed.)

One would like to allow a compiler to change, for example:

do, i=1,n
a(i) = pure_function (12.34)
end do

into:

temp = pure_function (12.34)
do, i=1,n
a(i) = temp
end do

In other words the pure function call, with a constant argument,
could literally be moved outside the loop! Obviously side effects
are to be avoided. (A PURE random number generator might have
unexpected problems to the uninitiated...)

There are times when one may want to use global variables.
Perhaps one wants to use a lookup table within the PURE routine
and some initialization of the common block took place at the
beginning of the program.

But a compiler should be able to change:

call init_tables ()
temp = pure_function (12.34)

to:

temp = pure_function (12.34) ! OOPS !
call init_tables ()

I don't know why. Perhaps the compiler thinks calling two
things at once in parallel tasks is a fun thing to do.
If the function is truly PURE, why not?

So what Mr. Giles says is correct - there should be NO
access to global variables if one wants truly robust code.
(A one-shot initialization capability would be nice though.
Isn't there a way to do this in ADA?)

Walt
--
Walt Spector
(w...@cray.com)
Mountain View, California
_._ _._ _.... _. ._.

James Giles

unread,
Feb 27, 1997, 3:00:00 AM2/27/97
to Walter Spector

Walter Spector wrote:
>
> One would like to allow a compiler to change, for example:
>
> do, i=1,n
> a(i) = pure_function (12.34)
> end do
>
> into:
>
> temp = pure_function (12.34)
> do, i=1,n
> a(i) = temp
> end do
>
> In other words the pure function call, with a constant argument,
> could literally be moved outside the loop! Obviously side effects
> are to be avoided. (A PURE random number generator might have
> unexpected problems to the uninitiated...)

Actually, unless the variable 'i' or the variable 'a' happens to be a
global variable, *both* my preferred definition of PURE and the
presently proposed one allow the above optimization. However, if 'i' or
'a' do happen to be globals, or if the loop were augmented with any
other definitions of globals (or calls to imPURE procedures), then the
present F95 proposal for PURE functions would not allow the above
optimization to take place. Really PURE functions (as per my
recommendation) would *always* allow the transformation.

James Giles

unread,
Feb 27, 1997, 3:00:00 AM2/27/97
to William Clodius

William Clodius wrote:
[...]

>
> I have not, to the best of my poor memory and communication skills,
> disagreed that there are error prone aspects of Fortran and have tried
> to state that the impurity of functions is one source of errors in
> Fortran that I dislike. What I have tried to state is that I do not
> believe that having PURE specify mathematically pure procedures and
> having a well defined evaluation order is a cure all for these errors.

If only pure functions were permitted, then specifying the evaluation
order of their references would indeed be a waste of time.

Nor, do I suppose that any of the changes I recommend will eliminate
*all* errors. Since I have not recommended that imPURE functions be
disallowed (though I would support such a resolution if the committee
were willing to consider it), there will always be subtle errors due to
mistakes made in the ordering of imPURE function references. However,
having truly PURE functions would make their order of evaluation
*clearly* irrelevant. No errors of order are even possible for such
functions.

The problem is that the present semi-PURE definition of PURE leaves
possibility of error in the standard for no useful reason. These errors
are, rather obviously, likely to be subtle and hard to identify. It
seems folly (to me) to unnecessarily leave the possibility of such
errors in the language.

[...]

> i.e., are they do to an assumed order of evaluation or to a failure to

> recognize that the order of evaluation is important, [...]

Most errors of this kind that I've seen coming from experienced users
were simply *mistakes*. People make them. The elimination of a whole
class of them that is unnecessary for the expressiveness or
functionality of the language seems to me to be a direct win.

[...]

> Because I lack such data, I therefore rely on assumptions that I find
> plausible, which suggest that a well defined evaluation order will have

> essentially no effect on the error proneness of functions with side
> effects, ...

One would think that the obvious course in the absence of data would be
the most conservative one. That is, assume that the ambiguous ordering
of imPURE functions *is* causing problems and, therefore, removing that
ambiguity.

However, you are making an assumption. I have never stated any support
for forcing Fortran to specify the order of evaluation of imPURE
functions. The present rule (that functions referenced from within the
same statement must not affect each-other's value) is *mostly*
sufficient. It already prohibits the most dangerous forms of mutual
dependence, and implies that functions referenced in *separate*
statements are ordered already (though perhaps this too is an incorrect
interpretation of the wording of the standard?).

What I've objected to is the vague phrase (and subsequent unclear
clarification) that function executions can be skipped (entirely or
partially) if their values "can be determined otherwise". Nowhere, not
even in the committee's supposed clarification, does it state what
optimizations this allows. Richard Maine has interpreted this to mean
that *any* side-effects in a function may be skipped. This would
certainly break a *lot* of existing code. In any case, this
interpretation contradicts examples given in the standard itself.

So, my proposal for imPURE functions has been, all along, that they
*must* be executed if their value is used in an expression. Stretching
further, it would be preferable that the mere presence of the function
reference should force its execution (even if its value is not
ultimately needed). Finally, if you really want to stretch, a
requirement that no single statement may refer to more than one imPURE
function (this eliminates the order problem entirely). I have always
stated this latter as an unlikely (and extremist) possibility.

To clarify the second point in the last paragraph:

If (COND1(X) .AND. COND2(Y)) ...

In a parallel environment, I'd prefer to evaluate both COND1() and
COND2() regardless of their final values because I can invoke them in
parallel (whereas, a short circuited .AND. operator would make me
serialize them). And, in the presence of side-effects, if COND1() or
COND2() are imPURE anyway, a rule requiring them both to be evaluated is
the only one that's not ambiguous.

[...]
. ... My assumptions also


> suggest that there will be no significant difference between the two
> concepts of pure in helping users recognize the importance of side
> effects.

Again, if you have no data, and are only assuming (you're the one that
pointed out last time the consequences of assuming), you should choose
the conservative approach. Since genuinely pure functions are
completely immune to order dependencies, they are automatically
completely safe (at least, with respect to the issue of evaluation
order).

William Clodius

unread,
Feb 27, 1997, 3:00:00 AM2/27/97
to

Walter Spector wrote:
>
> <snip>

>
> One would like to allow a compiler to change, for example:
>
> do, i=1,n
> a(i) = pure_function (12.34)
> end do
>
> into:
>
> temp = pure_function (12.34)
> do, i=1,n
> a(i) = temp
> end do
>
> In other words the pure function call, with a constant argument,
> could literally be moved outside the loop! Obviously side effects
> are to be avoided. (A PURE random number generator might have
> unexpected problems to the uninitiated...)

A few points.

First, as I understand Richard Maine's comments, it appears that the
Fortran standard currently allows such code motion across statements
even for impure functions, provided the compiler can show that the
function's behavior has not been changed by side effects, and the
compiler is free to make very liberal assumptions in showing that side
effects can not influence the function's behavior, i.e., that any
internal state in the function will not influence its response. Provided
a is not a global variable, Maine's comment indicates that the compiler
is free to perform the code motion. I do not like that aspect of the
standard. Although I do not care whether the order of evaluation is
defined within a statement, the implied ordering according to statement
sequence is so intuitive that I have great trouble justifying the lack
of such ordering in a language that allows side effects in functions.

Second, many useages of impure random number generator functions have
long been ill defined. The user is better off relying on random number
generator subroutines, which can be written to be PURE in F95's sense.
The random number generator subroutine of F90 is in fact PURE in terms
of the proposed standard's definition.

>
> There are times when one may want to use global variables.
> Perhaps one wants to use a lookup table within the PURE routine
> and some initialization of the common block took place at the
> beginning of the program.
>
> But a compiler should be able to change:
>
> call init_tables ()
> temp = pure_function (12.34)
>
> to:
>
> temp = pure_function (12.34) ! OOPS !
> call init_tables ()
>
> I don't know why. Perhaps the compiler thinks calling two
> things at once in parallel tasks is a fun thing to do.
> If the function is truly PURE, why not?
>

However in this example you gain nothing in terms of parallelization. In
order to convert pure_function to a mathematically pure function and
retain the optimization of the look up tables the look up tables will
have to be passed as an explicit argument. As a result you have the
sequence

call init_tables ()
temp = pure_function (12.34, look_up_tables)

where either look_up_tables is a global variable, or a local varible
returned by init_tables. As a result pure_function cannot be
interchanged with init_tables. However, just as the code

call init_tables ()
do, i=1,n
a(i) = pure_function (12.34, look_up_tables)
end do

can be safely transformed into

call init_tables ()
temp = pure_function (12.34, look_up_tables)


do, i=1,n
a(i) = temp
end do

if pure_function is mathematically pure provided a is not aliased with
look_up_tables. For fortran's PURE functions I believe the code


call init_tables ()


do, i=1,n
a(i) = pure_function (12.34)
end do


can be safely transformed into

call init_tables ()


temp = pure_function (12.34)
do, i=1,n
a(i) = temp
end do

provided the compiler can show that a is not global variable accessible
internally to pure_function, i.e., a is not aliased with look_up_tables.
Because Fortran 95 requires an explicit definition of pure_function's
semantics to know that it is pure we have three cases

1. pure_function was passed as an argument to the enclosing procedure,
unlikely for this example as otherwise what is the meaning of
init_tables. Then the compiler will be able to verify that a is not
aliased with look_up_tables only if a is not a global.

2. pure_function is globally accessible but the user has hand coded an
explicit interface. Then the compiler will be able to verify that a is
not aliased with look_up_tables only if a is not a global.

3. pure_function is globally accessible through USE of a module, and the
interface was generated by the compiler in processing the module. In
this case the compiler has the option of maintaining data about global
variables that can influence pure_function's behavior within its version
of the interface. The compiler will be able to verify that a is not
aliased with look_up_tables if either a is not a global, or, if a is a
global variable, the information within its database can be used to
verify that a is not a global varible that can influence its behavior.
In most code I suspect that the analysis necessary to verify this will
be simple, but if a is a pointer, or a target, or equivalenced with
another variable, the compiler might well punt on principle.

> So what Mr. Giles says is correct - there should be NO
> access to global variables if one wants truly robust code.
> (A one-shot initialization capability would be nice though.
> Isn't there a way to do this in ADA?)

> <snip>

I don't know about Ada, but Eiffel has ONCE procedures that are executed
for side effects once, say to read from a database, and afterwards
behave like constant arrays.

In thinking about this I wonder if James would accept having a MATH
attribute to the PURE attribute, where MATH would be a special case of a
PURE function that satisfies the mathematical definition of a pure
function, no direct access to global variables.

Van Snyder

unread,
Mar 4, 1997, 3:00:00 AM3/4/97
to

In <5eov...@rzsun02.rrz.uni-hamburg.de> Phillip Helbig wrote:
>| In <330F87...@lanl.gov>, William Clodius <wclo...@lanl.gov> writes:

>| There are essentially two well defined extremes for the semantics of
>| FUNCTION calls, side effects should be allowed and not optimized away,
>| or side effects should not be allowed. The committee has taken a less
>| well defined position that some side effects are allowed provided the
>| user does not mind if they are optimized aways.

>| Let us see the reaction is to a well defined (I hope) intermediate
>| semantics.

>| > 1. SAVEd local variables: I would


>|> prefer it if the standard deprecated their use in FUNCTIONs.

> Good idea.

Don't do this until initialization has adequate expressive power, and
exception handling is adequately done.

I use saved local variables in functions for things that depend on the
characteristics of the arithmetic. One example is the bounds of arguments
that don't cause exceptional behavior. Sometimes these are quite complicated
functions of the arithmetic characteristics.

Another is the degree of Chebyshev polynomial approximation that's appropriate
to a particular EPSILON. The easiest way to compute this from the coefficients
is by using a loop.

--
What fraction of Americans believe | Van Snyder
Wrestling is real and NASA is fake? | vsn...@math.jpl.nasa.gov

0 new messages