From jwagener@amoco.com Tue May  9 12:06:18 1995
Received: from interlock.amoco.com by dkuug.dk with SMTP id AA19354
  (5.65c8/IDA-1.4.4j for <sc22wg5@dkuug.dk>); Wed, 10 May 1995 00:07:01 +0200
Received: by interlock.amoco.com id AA06858
  (InterLock SMTP Gateway 3.0 for sc22wg5@dkuug.dk);
  Tue, 9 May 1995 17:06:42 -0500
Message-Id: <199505092206.AA06858@interlock.amoco.com>
Received: by interlock.amoco.com (Protected-side Proxy Mail Agent-3);
  Tue, 9 May 1995 17:06:42 -0500
Received: by interlock.amoco.com (Protected-side Proxy Mail Agent-2);
  Tue, 9 May 1995 17:06:42 -0500
Received: by interlock.amoco.com (Protected-side Proxy Mail Agent-1);
  Tue, 9 May 1995 17:06:42 -0500
From: jwagener@amoco.com
X-Openmail-Hops: 1
Date: Tue, 9 May 95 17:06:18 -0500
Subject: Fortran 95 thoughts
To: vsnyder@math.jpl.nasa.gov
Cc: sc22wg5@dkuug.dk
X-Charset: ASCII
X-Char-Esc: 29

Item Subject: Message text
Van,
thanks for your note of April 17.  As it happens, that was the date of the start
of the WG5 meeting in Tokyo, at which the final content of F95 was established
(subject to changes motivated by public review).  Following that week, X3J3 met
April 24-28 to make the final document changes, and the week after that I spent
in offsite/vacation mode; thus this is the first I've had to look at your recent
suggestions for F95.

The current plan is to do a "CD ballot" on the F95 draft (that's ISO-speak for
the first round of comments) during approximately June, July, and August, with
the US public review period being the first two months of that three-month
ballot.  I suggest that you submit your suggestions as public review comments
during the public review period to Kate McMillan (kmcmillan@itic.nw.dc.us) and
also to the X3J3 premeeting distribution for the August X3J3 meeting (by
mid-July to north@rosevc.rose-hulman.edu).  The current draft is document
95-007, available by anonymous ftp from ftp.ncsa.uiuc.edu:x3j3, and the updated
draft, 95-007r1, that will be the basis of the ballot will be available later
this month from the same source.

Following is my top-of-the-head quickie comments on your various suggestions,
which I have numbered and summarized; I hope you don't mind that I am copying
this note, including your note to me, to the WG5/X3J3 email list.

(1)  destructors                  this has been discussed a fair amount;
                                  it's probably too late for F95, but the
                                  specific functionality you want it for 
                                  is, I believe, provided by the automatic
                                  deallocation feature recently added

(2)  constructors                 similar to (1), but note that default
                                  initialization of structured objects is
                                  now provided (though not via constructors)

(3)  certain vars and args        if this is not clear, you're right it needs 
                                  to be, but I think you have to declare them

(4)  initialized pointers         this can now be done, with the NULL intrinsic

(5)  real CASE ranges             a controversial subject to be sure - 
                                  I enjoyed your "message to troglodytes"

(6)  INCLUDE change               couldn't hurt to resend the details

(7)  removing irregularities      I've always wanted to simplify by removing
                                  unecessary restrictions and irregularities;
                                  for some reason this is a tough sell, however;
                                  a comprehensive list might help

(8)  expanded use of EXIT         probably too late for F95, but ....  (PTLFB)

(9)  more local declarations      PTLFB

(10) enumerated types             PTLFB; but extending the TYPE statement is
                                  the way to go (I made that suggestion years 
                                  ago  :-)

(11) IOS intrinsic                PTLFB

(12) new "triplett" type          PTLFB

(13) deprecate CONTAINS           PTLFB (good idea - I thought CONTAINS should 
                                  never have been put in F90 in the first place)

(14) relax use of "::"            PTLFB

(15) / conditional compilation    an interesting topic and suggestion; this was
                                  a hot topic at the WG5 meeting, and the
decision
                                  there was to not make this functionality part 
                                  of the Fortran language (keep it separate);
but
                                  your suggestion is an intriguing alternative

So much for my summary of your comments and my quick responses, which are
subject to correction from those more in touch with the details of specific
topics.  You might also make use of our "journal of requirements" (JOR) process,
and submit suggestions for requirements for future Fortran revisions.  Getting
suggestions in the JOR guarantees that they will be seriously considered, but of
course will not guarantee that they will be accepted by either X3J3 or WG5.  The
JOR editor is Stan Whitlock, whitlock@tle.enet.dec.com .

Thanks again for your note, and I hope that this response gives you a bit of an
up date as to where we are and outlines the best routes for followup on those
items that you want (a) added to F95 at the last minute and (b) seriously
considered early on for F2000.

Jerry


               

.......................................................................

FROM: vsnyder/unix_in////////HPMEXT1/vsnyder#a#math#f#Jpl#f#Nasa#f#Gov@houeosm4
TO: jwagener@hou.amoco.com

.......................................................................

Received: from hou.amoco.com (earth) by houeosm4 with SMTP
	(1.37.109.16/16.2) id AA098216085; Mon, 17 Apr 1995 19:48:05 -0500
Received: from netserv2 by hou.amoco.com (4.1/SMI-4.1)
	id AA26235; Mon, 17 Apr 95 19:47:41 CDT
Received: from interlock.amoco.com (portal.trc.amoco.com) by netserv2 (4.1/SMI-4.0)
	id AA00227; Mon, 17 Apr 95 19:48:29 CDT
Received: from nemesis.jpl.nasa.gov (nemesis-fddi.jpl.nasa.gov) by portal.amoco.com with SMTP id AA10139
  (InterLock SMTP Gateway 3.0 for <jwagener@amoco.com>);
  Mon, 17 Apr 1995 19:47:50 -0500
Received: from math.Jpl.Nasa.Gov (math.jpl.nasa.gov [137.79.50.106]) by nemesis.jpl.nasa.gov (8.6.10/8.6.6) with ESMTP id RAA20068 for <jwagener@amoco.com>; Mon, 17 Apr 1995 17:43:20 -0700
Message-Id: <199504180043.RAA20068@nemesis.jpl.nasa.gov>
Received: by math.Jpl.Nasa.Gov
	(1.37.109.15/16.2) id AA029325989; Mon, 17 Apr 1995 17:46:29 -0700
From: Van Snyder <vsnyder@math.Jpl.Nasa.Gov>
Subject: Fortran 95 thoughts
To: jwagener@hou.amoco.com
Date: Mon, 17 Apr 1995 17:46:28 PDT
X-Mailer: Elm [revision: 109.14]

.......................................................................

Dear Jerry:

I have several thoughts concerning Fortran 95 on my alleged mind.
I display them below, for your study.

I've divided them into groups.  I think of the first as correcting serious
errors or oversights in the Fortran 90 standard (there's some follow-on to
the first suggestion, however, that's related to it, but doesn't correct a
serious defect).  The second group is just stuff I think would make life
simpler for programmers, and usually for compiler writers too.

Except where indicated, the proposals are pretty much independent from
one another.  Whatever dependencies are present are acyclically related.

I don't propose anything that would immediately invalidate F77 or F90
programs, but I do propose a few deprecations.

By and large, I think the proposals simultaneously simplify the language,
simplify life for implementors and programmers, increase its expressive
power, and increase its type safety.

If you think there's a chance for any of them, I'd be happy to write chapter
and verse for concrete proposals.

A general thought is that every explicit constraint, and every implied
constraint, ought to be examined.  If a case can't be made for having it,
it ought to be deleted.  Not having kept a running rationale during the
development process, I'm certain people lost track of the reasons for
some constraints, which eventually became unnecessary but weren't deleted.

It would similarly be useful to keep a rationale, at least to the extent
of keeping an explanation of every explicit and implied constraint.

Best regards,
Van Snyder
--------------------------------------------------------------------------------
In the course of a class on Fortran 90, given (actually _sold_) by Walt
Brainerd, he and I agreed there's a serious oversight in Fortran 90 that
compromises the usability of derived data types, when taken together
with the facility to create user-defined operators, or overload
intrinsic operators to manipulate them.

Suppose one has

  type t
    ! blah, blah, blah
    type(u), pointer :: p
  end type t

and an operator "+" defined to take type(t) for both operands and yield
a type(t) result.  Assume the function that implements "+" allocates the
"p" field of the result.

Suppose one writes

 subroutine x
  use type_t_stuff
  type(t) :: a, b
  ! provide values for a and b (which allocates a%p and b%p).
  call s (a+b) ! s is defined in type_t_stuff to take a type(t) argument.
  return
 end subroutine x

The compiler will produce space for the temporary result "a+b",
which space will be filled-in by the user-provided overloading of
"+" that applies to two "type(t)" operands and produces another.

When control returns from "x", the temporary result "a+b" goes out of
scope.  At that time, the "p" field of "a+b" ought to be deallocated.
The original philosophy of Fortran 90 appears to have been that the user
ought to, and always can, take responsibility to deallocate everything
that's allocated.  In this case, however, the programmer can't discharge
that responsibility because the temporary result "a+b" has no name, and
therefore, its "p" field can't be referenced.  This produces a "memory
leak" that would eventually stop the program (unless the vendor implements
a garbage collector -- but the user could subvert that by using the
"sequence" attribute for type t).

The user _could_have_ written

  temp = a+b
  call s (temp)
  ...
  deallocate (temp%p)

but that's _not_ what was in mind when Fortran 90 was developed!  If so, the
language wouldn't have allowed user-defined operators.  We'd still have to
write "call t_add (a, b, temp)"!

To solve this problem, I propose stealing one trick from C++ (unpalatable
as that may be):  In C++ each class may define a destructor procedure that
is automatically invoked when an object of the class is to be destroyed,
either by going out of scope, or by being deallocated.  I propose allowing
one to write a destructor procedure for a derived type.  The default would
be an empty destructor.

The syntax I like best is to declare and define the destructor process
_inside_ the derived type declaration, after the fields.  For example:

  type t
    type(u), pointer :: p
    destructor
      deallocate (t%p)
      ! return is automatic here, but could be specified explicitly
      ! here or anywhere else in the destructor.
  end type t

Notice three things about this example.  First, "destructor" becomes
a keyword, having meaning only in this context.  Second, I've used
"t%p" to mean "the p field of the object of type t for which the
destructor is invoked" (as opposed "this->p" in C++).  (The "t%"
could be optional.)  Third, there's no "end destructor" statement --
the destructor is ended by the "end type" statement -- so "destructor"
must be at the end of the type declaration.

I'm not religiously committed to any of these three features, but I
believe strongly that the functionality ought to be provided, and that
the best place to allow the user to define what happens is _inside_ the
derived type definition.  Failing the latter, one might be allowed to
write

  destructor :: y

or

  type t, destructor(y)

or something else, where y must be a subroutine with explicit interface,
that takes one argument of type(t).  I think these are less elegant; they
doesn't gain anything as compared to the former suggestion -- they still
require a new keyword, for example.
--------------------------------------------------------------------------------
This isn't nearly as serious a problem as the above, but for symmetry,
it would be useful to allow constructors, too (they'd be pretty silly
without destructors).  Constructors are functions that produce a value
of the type to which they're associated.  They would be executed
automatically when a variable comes into existence, either by the program
unit containing it being called, or the object being allocated.  For example:

  type t
    type(u), pointer :: p
    constructor ! think of this as "type(t) function t ()"
      nullify (t%p)
      return ! would be automatic here, and could also be specified
      ! explicitly anywhere else in the constructor.
    destructor
      deallocate (t%p)
  end type t

It's also desirable to allow constructors to have arguments:

  type t
    type(u), pointer :: p
    constructor (p)
      type(u), intent(in) :: p ! Intent(IN) should be REQUIRED
      t%p = p ! notice that t%p and p are different here!
    constructor
      nullify (p) ! Did you notice I didn't write t%p ?
  ... (as above ) ...

which one might use like

  type(t(z)) :: r

where z is of a type for which a constructor for t has an argument
(z could be a parameter, a dummy, a previously declared variable having
an initial value, a variable accessed by host or use association, or a
variable in common).

Notice that each constructor is terminated by the beginning of the next
one, or by the destructor.  The destructor could come _immediately_before_
or _between_ the constructors, too, but _not_earlier_.

A benefit of allowing constructors with arguments is that the compiler
could invoke them to produce a value of the appropriate type when needed,
say when a function needs a type(t) argument, but the user gives it a
type(u) argument, or when doing assignments that would otherwise be
impossible.  Suppose r is of type(t) and z is of type(u), and there's
no overloading of "=" that takes the appropriate types.  Ordinarily,

  r = z

would be impossible, but if type(t) has a constructor that takes a type(u)
argument, the above means

  r = t(z)

The user could write this too.  [It's like having INT at your disposal to
do explicit or automatic conversions.]

Constructors with more than one argument are useful, too, and should be
allowed in variable declarations, but they can't be used for automatic
conversions.  [It's like having CMPLX to do explicit conversions.]

This _isn't_ a new idea, even in Fortran 90!  Fortran 90 already has two
automatically generated constructors:  One with no arguments, that does
nothing (type(t) :: foo), and one with the same number and order of
arguments as the members of the type, that assigns the arguments to the
members (r = t(a,b,c...)).

If vendors object that the form I suggested above for constructors would
require "name-mangling", one might instead use

  type t
    ! blah blah blah
    constructors ! you could spell it "interface t" but t is redundant
      type(t) function foo_1
        ...
      end function foo_1
      type(t) function foo_2 (p)
	type(u) :: p
	...
      end function foo_2
      ...
    destructor
      ...
  end type t

so t() accesses foo_1, t(z) accesses foo_2(z) (providing z is of type(u)) ...
All of the functions in the constructors block _must_ return type(t)!

I prefer that the bodies of the constructors be declared _within_ the type
definition, but this syntax could be constrained to be just like an interface
block (and maybe even spelled the same way).
--------------------------------------------------------------------------------
Even better, and simpler, and more general than specifically adding
"constructors", allow

  type t
    ! blah blah blah
    type(u), recursive, function :: f (g, ...) result (r)
      ... t%foo ...
    end function f
    destructor
      ...
  end type t

or

  type t
    interface f
      type(u), recursive, function :: ff (g, ...) result (r)
      ...
    end interface f
    interface t
      ...
    end interface t
    ...
  end type t
  ...
  type(t) :: a
  type(u) :: c
  ...
  c = ... a%f(...) ...
  ...

in which, again, "t%" is bound to the "object of type t on which the
function f was invoked" ("a" in the example).

Notice the "::" syntax in the function declaration.  If one _requires_ it
in this context, "contains" isn't needed.  [Aside: "contains" ought to be
deprecated in favor of allowing the "::" syntax for functions, and
_requiring_ its use for internal functions.  If you think there's a
chance of _that_ I'll draft it too.]

An interface or function having the same name as the type must have a
return value of the same type, and is a "constructor" as described above.
This doesn't need extra syntax like the "constructor" proposal above.

I think it's important to declare procedure bodies _inside_ the type
declaration -- it simplifies the compiler writers job.

This goes a long way to providing OOF.  Whatever you do, however, _don't_
provide multiple inheritance!  There's some controversy whether OOP
needs inheritance at all -- take a look at Ada 95, for example (docs free
from Robin Keeney, keeneyr@grumpy.plk.af.mil).  It uses extension instead.
Modula-3 gets along fine with single inheritance.
--------------------------------------------------------------------------------
The standard needs to pin down whether names used only for induction
variables in an implied DO, or dummy arguments for statement functions,
need to be declared if IMPLICIT NONE is in effect.  (Maybe it is pinned
down, but I couldn't find it.)
--------------------------------------------------------------------------------
I understand there's agreement one ought to be able to declare an initially
nullified pointer.  I also understand there's some sentiment to define an
intrinsic function for that purpose.  I think the former is necessary, and
the latter is not.  Simply allow pointer assignment syntax for an
initializer in a declaration.  For example:

  real, target :: a
  real, pointer :: c => , b => a

declares b to be a pointer to a real, and its initial state is "associated
to a".  Similarly, c is a pointer to a real, and its initial state is
nullified.  One might want to prohibit the initial value from being a
pointer, or allow it only to refer to something previously defined, lest
one require the compiler to detect cyclic dependencies that produce no
initial value:

  real, pointer :: a => b, b => a

[Several remarks: "real, parameter, pointer :: b => a" would be the same
as "equivalence (a,b)".  Should "parameter" and "pointer" be allowed
simultaneously?

If constructors are introduced, it isn't nearly so important to provide
a syntax to denote a nullified initial state for a pointer.

Extending the pointer assignment statement syntax to allow writing "a =>"
(pronounced "a points nowhere") would allow deprecating the NULLIFY statement.]
--------------------------------------------------------------------------------
Don't deprecate/obsolete Arithmetic IF until CASE is extended to REALs,
or at least REAL ranges (which must be allowed to be open or closed at
either end, independently).  Better yet, extend CASE to all ordered types,
which includes derived types for which the user has provided accessible
definitions for "<", "<=" and "=".  If enumeration types are ever added,
extend CASE to them too.

All earlier arguments I have seen against this proposal have been spurious.
Please enlighten the troglodytes under your care:

SELECT CASE is _nothing_more_ than an alternative notation for IF ...
ELSE IF ... ELSE IF ...  ELSE ... ENDIF that gives the compiler better
optimization clues and reduces writers cramp.  A clever compiler
_might_ discover that it can use computed GOTO but the stamdard _does_not_
require it!  It is _not_necessary_ simultaneously to extend computed
GOTO to REALs!

If somebody uses a CHARACTER(17) for a discriminant, the compiler almost
certainly _won't_ use a computed GOTO.  So the code generation strategy
needed for REAL ranges is _nothing_new_.  Real ranges for CASE thus has
_nothing_ in common with computed GOTO.

Real range for CASE has _nothing_ in common with REAL induction variables
for DO (I'd be happy to see the latter deprecated/obsoleted).

Allowing REAL relationals is _absolutely_essential_ for numerical
analysis -- it _wasn't_ a mistake!  (Even testing for equality between
reals is useful, and can be portable if one uses care.)

[End of troglodyte enlightenment.]

Consider

  IF (F(X)) 10, 20, 30

and

  IF (F(X) < 0.0) then
  ...
  ELSE IF (F(X) == 0.0) then
  ...
  ELSE
  ...
  ENDIF

Notice that the latter executes F twice.  That's more expensive than with
the Arithmetic IF, and if F has side effects, the answers might be different.
Now consider:

  SELECT CASE (F(X))
  CASE (* < 0.0)
  ...
  CASE (0.0)
  ...
  CASE DEFAULT
  ...
  END SELECT

Notice that F(X) is executed _once_ like in the Arithmetic IF case.
The compiler will re-write this:

  _temp_ = F(X)
  IF (_temp_ < 0.0) then
  ...
  ELSE IF (_temp_ == 0.0) then
  ...
  ELSE
  ...
  ENDIF

but it _won't_ re-write the first one above (immediately after the
Arithmetic IF) because it doesn't know whether F has side effects.

Prohibiting REALs in CASE is a restriction that makes life harder for
compiler writers and programmers alike.

You may remember that I've proposed this before.  If you think there's a
chance, I'll dust off what I've written previously, put in appropriate
line numbers (if I can find a draft standard), and mail it to you.

The idea was pretty simple:  In a CASE statement, "*" stands for the
value of the expression in the SELECT CASE statement, and it could be
compared to constants to produce a range or a point , e.g.:

  CASE ( 10.5 <= * .lt. 11.0, * == 13.0, * .eq. 14.0 )

Of course, this syntax works equally well for integers and characters
(but not logicals).  The present "A:B" would be equivalent to "A<=*<=B",
"A:" would be equivalent to "A<=*", ":A" would be equivalent to "*<=A",
and "C" alone would be equivalent to "*==C" or "C==*".
--------------------------------------------------------------------------------
Change the interpretation of the character string on an INCLUDE line from
"system defined" to "user defined".  As it stands it's a significant barrier
to portability.  I explained once before how to do this (we did it in a
preprocessor that runs on Univac 1100, DOS, VMS and Unix).  The explanation
was intended to appear in Appendix C.  I could mail it to you again.
================================================================================
The following is less critical than the foregoing.
Most, however, is very easy for developers.
================================================================================
Have you considered simplifying the language, and the compiler writer's
job, by getting rid of gratuitous constraints and irregularities?

For example, the constraint that one can't use list-directed or namelist
I/O for internal files is easier to allow than prohibit.  At least, so
I've been told by two compiler manufacturers.

If you allow it, you'll probably also need to regularize the "end=" clause
in the control part by allowing it in "write" statements too.  I suggest
it generally means "you've run out of space", with a system dependent
meaning to that phrase when applied to an external file, and a standard
meaning ("You've run out of space!") when applied to an internal file.
--------------------------------------------------------------------------------
To discourage further the usage of GOTO, consider allowing EXIT to apply
to any construct, and introduce a BLOCK construct that has only two
purposes: 1) a point to which one attaches a label so interior EXIT's
can refer to it, and 2) a barrier into which GOTO's cannot penetrate.

To see the value, try to re-write the following without a redundant test
or a GOTO:

  b: BLOCK
       DO I = 1, SIZE(K)
	 IF (J == K(I)) EXIT b
       END DO
       PRINT *, J, " is not in the set."
  END BLOCK b
--------------------------------------------------------------------------------
Allow declarations (except for common and internal procedures) inside
constructs.  It ought to work just like host association.  Thus, compilers
already have the machinery in place -- not much new is needed.  By the way,
this gives a third use for a BLOCK construct: encapsulating names.  If a
variable having the same name as the induction variable of a DO is declared
inside the block, it's the induction variable, and it's inaccessible outside
the block.
--------------------------------------------------------------------------------
So that programmers won't need to use goofy "first time flags" for
initialization, it would be useful to allow executable statements in
modules, before the CONTAINS statement that introduces module procedures
(or before the END MODULE statement if there are none).  The free-standing
executable statements in modules should be executed in an order that is
the reverse of a topological ordering of the USE relation.  For example, if
module A contains "USE B", the initialization code in module B is executed
before the initialization code in module A.  The main program, if any, is
executed after all module initializations.  This is the way Modula and Ada
work.  Since they allow circular use dependencies, they have a headache
Fortran wouldn't.  If the initialization code in module A calls an
external procedure that USEs module B, but there's no path of USE dependency
between A and B, the standard should leave the relative order of execution
explicitly undefined, or call it an error.
--------------------------------------------------------------------------------
Allow users to define enumeration types.  One could extend derived type
syntax in several ways without introducing new keywords:

  type colors :: red, blue, green

I don't think it's _necessary_, but this could be extended by
allowing the specification of values:

  type colors :: red=0, blue=1, green=2

If users do not (or are not allowed to) specify values, enumerations should
explicitly be represented by default integers, with the first member
represented by zero, the second by one, etc. Thus "blue > red" makes sense,
and one can couple a Fortran 9x program to something non-Fortran 9x.  (I
had this problem when coupling the input to a parser generator to a compiler.
Fortunately, the compiler was written in Modula-2, for which the representation
of literals of enumeration types is specified, as above.)

This is _more_ than just automatic assignment of values to parameters!  The
_type_ is important too, just like derived types that are structures are
important in their own right (as opposed to just their components.)  The
types can be used for selecting functions from a generic set, e.g.

One could argue for an alternative syntax

  type colors(red,blue,green)

but I would rather use it for a "typedef" syntax:

  type matrix(real,dimension(:,:))
--------------------------------------------------------------------------------
I've tried to write programs that respond gracefully to I/O errors.  One
usually starts by writing IOSTAT=IOS or some such thing in the control part
of an I/O statement.  But what then?  After doing whatever graceful thing
one had in mind, all that one's left able to do is print IOS!  I'd like to
see a standard intrinsic subroutine that takes IOS, and prints whatever
the I/O library would have printed if the user hadn't specified IOSTAT=
or ERR= in the control part of the I/O statement.  It wouldn't hurt to
allow the unit number and/or file name (depending on what one was doing --
say INQUIRE or OPEN) as optional arguments, to improve the quality of the
messages if they're supplied.  One compiler vendor has done this already.
================================================================================
The following may be too exotic for the committee, but I don't think a
compiler writer would have much trouble with it:
================================================================================
Make the triplet that's used to specify a cross section of an array into
an intrinsic type.  The constructor would be as it is now, i.e.
"start:end:stride", and one would need intrinsic functions, say
"triplet_start" etc to extract the pieces.  One might declare them, e.g.

  triplet, parameter :: foo = 1:35:2

They could then be used for subscripts (denoting a section), passed around
as arguments, in assigment statements, and in I/O statements.  One could
also regularize DO syntax by allowing "DO I = FOO" where FOO is a triplet
variable, or by allowing a triplet constructor (DO I = 1:35:2).
--------------------------------------------------------------------------------
Make variable and function declarations more symmetric by allowing "::"
immediately before the function name in a function header, in which
case the "result (foo)" clause must come somewhere _before_ the "::".
Allow one to elide the CONTAINS statement if the first internal procedure
is a function using "::" syntax for its header.  Allow one to elide the
CONTAINS statement if the first internal procedure is a subroutine.
Eventually deprecate the CONTAINS statement.  When the rules are extended
to allow internal procedures to contain internal procedures, the nesting
is indicated by the relative positioning of END FUNCTION or END SUBROUTINE
statements, not by CONTAINS statements.
--------------------------------------------------------------------------------
Make declarations more symmetric by allowing attributes and types to come
in any order before the "::".  Reduce the verbiage in the affected section
of the standard by about half, by removing duplicate discussion of attributes
in "type" statements, and in "attribute" statements.  (Call them all
"object declaration" statements or some such thing.)
--------------------------------------------------------------------------------
Allow one to write a procedure, body and all, not just a header, in an
interface block.  Specify in the standard that this is a clue from the
programmer to the compiler that the procedure ought to be inlined.
--------------------------------------------------------------------------------
_Don't_ define a Fortran preprocessor just to support conditional compilation!
Instead, allow declarations inside constructs, as outlined above, and add
just one keyword, say EXPORT, to indicate things declared inside IF or CASE
constructs, including labels, are visible outside.  One writes EXPORT after
THEN or after the expression on a SELECT CASE statement.  If EXPORT is
present, the predicate of IF or discriminant of SELECT CASE must be constant
(otherwise duplicate declarations and/or labels may result).

Extend the CASE statement by allowing

  CASE ( range_list ) .AND. logical_expression

which is interpreted to mean the value of the discriminant expression (in the
SELECT CASE statement) is in the range_list, AND logical_expression is true.
If there is no CASE statement such that the discriminant is in range_list,
or there is one but the associated logical_expression is false, the CASE
DEFAULT block is executed.  If logical_expression is false, and constant,
the compiler should delete the CASE statement and its associated block.  If
logical_expression is constant and true, the compiler should delete it.  If
logical_expression isn't constant, the compiler should re-write the above as

  [ELSE] IF (discriminant is in range_list) THEN
    IF (.NOT. logical_expression) GOTO CASE_DEFAULT_stuff

which a programmer couldn't because it would violate reasonable rules.
Thus range_lists can overlap so long as all CASE statements with overlapping
ranges have constant logical_expressions, and no more than one has an
associated true logical_expression.  After rewriting, a compiler's analysis of
a CASE construct is as in Fortran 90 -- except for the GOTO CASE_DEFAULT_stuff
part.

Writing "CASE (range_list) .AND. logical_expression" is different from writing

  CASE ( range_list )
    IF ( logical_expression ) THEN
    ...
    END IF
  CASE ... or END SELECT

when logical_expression is false.  In the latter case, if the value of the
discriminant expression is in range_list, nothing is done.  In the former
case, if the value of the discriminant expression is in range_list, the
block associated to the CASE DEFAULT statement is executed.  When
logical_expression is true, the two forms are equivalent.

With these two simple changes, one can use IF with constant predicates, and
CASE with constant discriminant expressions and/or constant logical_expressions,
for conditional compilation.  No clanking cpp-like machinery is necessary.
