-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy pathproposal.txt
251 lines (198 loc) · 9.75 KB
/
proposal.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
To: J3 J3/XX-XXX
From: Milan Curcic, Jeremie Vandenplas, Zach Jibben
Subject: Default value for optional arguments
Date: 2020-January-10
Reference: 18-136r1, 18-122r1
1. Introduction
This paper contains a proposal for Fortran 202y, to allow a programmer
to specify a default value for optional dummy arguments. This would
allow the programmer to then safely reference such variables in
expressions regardless of whether the actual argument is present or
not.
2. Problem
Currently, standard Fortran does not allow setting a default value for
optional arguments. Default value of an optional argument is the value
that the dummy argument would take if the corresponding actual argument
is not present. If declaring a dummy argument as optional, the user
must:
* Explicitly test for the presence of the actual argument using the
intrinsic function present();
* Use a separate variable inside the procedure to assign the value
because the optional dummy argument that is not present must not be
referenced in expressions other than as actual argument to the
intrinsic function present().
This example function illustrates the problem:
real function quadratic(x, a, b, c)
! returns a + b * x + c * x**2 if c is present
! and a + b * x otherwise
real, intent(in) :: x, a, b
real, intent(in), optional :: c
real :: c_tmp ! use another var. to reference the missing arg
c_tmp = 0 ! default value if c is not present
if (present(c)) c_tmp = c
quadratic = a + b * x + c_tmp * x**2
end function quadratic
For any dummy argument with the optional attribute, the programmer must
use the intrinsic function present() to check for the presence of the
argument. Furthermore, if the optional dummy argument is meant to be
used in multiple places in the procedure, the programmer is likely to
use the pattern from the example above, where a "temporary" variable is
declared and used in place of the dummy argument, which disconnects the
implementation from the user interface. Furthermore, this requires at
least 3 lines of code (declaration of c_tmp, initialization of c_tmp,
and testing for the presence of c) only to handle the scenario of a
missing optional argument.
This proposal seeks to address the issue that explicitly checking for
presence of the optional dummy argument and using a helper variable is
cumbersome and error-prone. The primary benefit of this feature is the
reduction in source code needed to handle optional arguments. This
benefit is even greater in scenarios where the optional argument is
used in many places in the procedure, and a helper variable is used for
its value instead. Reduction in needed source code would result in more
readable and more correct programs. The secondary benefit of this is
programmer happiness, as working with optional arguments would require
less typing.
3. Proposed solution
As suggested by Van Snyder in 18-136r1, the problem could be solved by
allowing an optional argument to be initialized using a constant
expression. The optional argument would then only be initialized if the
corresponding actual argument is not provided by the caller. Example:
real function quadratic(x, a, b, c)
! returns a + b * x + c * x**2 if c is present
! and a + b * x otherwise
real, intent(in) :: x, a, b
real, intent(in), optional :: c = 0
quadratic = a + b * x + c * x**2
end function quadratic
In this snippet, we use the assignment operator (=) to specify the
default value of the optional dummy argument.
Like initializer, the optional argument can be assigned any constant
expression, as defined in Section 10.1.12 of 18-007r1.
While there may be concerns that the same syntax is used to implicitly
set the save attribute for variables in procedures, there is no
conflict because the language prohibits dummy arguments from having an
initializer, or the save attribute. The change to the standard to allow
this feature would thus be to allow an optional dummy argument to have
an initializer, which would be triggered only when corresponding actual
argument is not passed by the caller.
This improvement has already been suggested by Van Snyder in 18-136r1,
has received votes in the user survey for 202X (see
isotc.iso.org/livelink/livelink?func=ll&objId=19530634&objAction=Open),
and appeared on Data subgroup's wishlist at meeting 215 (see 18-122r1).
4. Backward compatibility
This addition to the language would not break any existing standard
conforming Fortran program, and thus preserves Fortran's backward
compatibility.
5. Related behavior
5.1. present()
There are two possible behaviors of the function present() when an
initializer is provided.
The first behavior would be that the function present() always returns
.true. when the optional dummy argument has an initializer, and
independently of the fact that an actual argument is passed or not
by the caller. Therefore, with such a behavior, the function present()
becomes useless for optional arguments with a default value.
Example:
real function foo(a, b)
real, intent(in), optional :: a
real, intent(in), optional :: b = 0
print*, present(a) !.true. if an actual argument is provided
!by the caller, .false. otherwise
print*, present(b) !always .true. due to the initializer
end function foo
The second behavior would be that the function present() returns
.true. or .false. when an actual argument is passed or not by the
caller, and independently of the fact that the optional dummy
argument has, or hasn't an initializer.
A use case of this behavior of the function present() could be:
real function foo(a, b)
real, intent(in) :: a
real, intent(in), optional :: b = 1.234
if (present(b)) then
... !expensive input validation here
end if
...
end function foo
5.2. intent(out)
There are several different possible behaviors for intent(out)
variables. One option is to disallow default values on optional
intent(out) variables. However, there are use cases where it is
desirable to return the value of a variable if a caller requests it,
without requiring the added logic of present() and "temporary"
variables described above.
Alternatively, we might treat intent(in) and intent(out) variables
identically, so that the following would be valid:
real function foo(a)
real, intent(out), optional :: a = 0
end function foo
The benefit is that, just like proposed for intent(in) variables,
intent(out) variables would be usable regardless of whether the value
is actually passed back to the caller, removing the need for present()
and "temporary" variables. The complication is that a present
intent(out) variable is initially undefined, meaning the logic of
present() might still be necessary in this case. Furthermore, the
behavior suggested above where present() = .true. always for variables
with a specified default would not be desired for intent(out)
variables. A possible use case for this behavior could be:
integer function open(filename, mode, iostat) result(u)
character(*), intent(in) :: filename
character(*), intent(in), optional :: mode
integer, intent(out), optional :: iostat = 0
character(3) :: mode_
character(:),allocatable :: action_, position_, status_, access_
character(:),allocatable :: form_
!some code
!....
open(newunit=u, file=filename, &
action = action_, position = position_, status = status_, &
access = access_, form = form_, &
iostat = iostat)
end function
A third option is that optional intent(out) variables with a specified
default are always initialized to that default, even when present,
rather than leaving them undefined.
5.3 intent(inout)
Dummy arguments with intent(inout) or no specified intent follow from
the behavior for intent(in) and intent(out). If an optional
intent(inout) variable is present, the existing behavior is
followed. The dummy variable has the value of the actual argument on
invocation of the procedure, its data may be used or manipulated during
the scope of the procedure, then it is returned to the scoping unit. If
an actual argument is not provided for an optional dummy argument with
a specified default and intent(inout), the variable recieves the
default value and may be used or modified throughout the procedure. The
data is then not returned to the scoping unit, since no actual argument
was provided.
This would allow the following routine:
subroutine foo(x)
integer, intent(inout), optional :: x = 0
! do stuff that could use or assign to x
end subroutine foo
This would be equivalent to:
subroutine foo(x)
integer, intent(inout), optional :: x
integer :: x_
if (present(x)) then
x_ = x
else
x_ = 0
end if
! do stuff that could use or assign to x_
if (present(x)) x = x_
end subroutine foo
5.4 Arrays
Default values for array dummy arguments present a difficulty,
particularly expressions involving a scalar. Some decision must be made
for the behavior in the following instances:
real, intent(in), optional :: a(:) = 0
real, intent(inout), optional :: b(:) = 0
real, intent(inout), allocatable, optional :: c(:) = 0
In each case, the size of the array is not determined if an actual
argument is not provided. One option is of course to forbid defaults
for arrays. This isn't preferable, but allowing defaults for scalars
only may still address the majority of use cases. Another option is to
forbid array broadcasting as a default specifier. That is, allow only
defaults such as [0,0] for arrays, where the length is clear.
6. Further discussion
Online discussion that led to this proposal can be found at
https://github.com/j3-fortran/fortran_proposals/issue/22.