[J3] Default values for optional arguments
Kurt W Hirchert
kurthirchert at gmail.com
Thu Jan 21 03:14:00 UTC 2021
Not so brief historical flashback:
During Fortran 8X development (i.e., development of what became Fortran
90), J3 considered argument defaulting, primarily as an alternative to
explicitly optional arguments. Because of complaints that J3 was adding
too much, they did not want to do both. Ultimately, J3 chose optional
arguments, because defaulting could be implemented using optional
arguments, but defaulting could not support everything that could be
done with a general optional argument facility.
During those discussions, J3 considered various paradigms for
implementing default arguments using optional arguments. In addition to
those already discussed in this thread, there was another you might
consider:
subroutine foo(a, b)
integer, intent(in), optional :: b
...
if (present(b)) then; call inner (b = b)
else; call inner (b = 42); end if
contains
subroutine inner(b)
integer :: b
! use a (via host association) and b (via argument association)
to do whatever foo does
end subroutine inner
end subroutine foo
This approach avoids the b vs. bval name confusion Thomas mentioned. It
doesn't move the default into the procedure interface, but the body of
foo essentially does little more than the defaulting, so it is still
fairly visible. (If the processor can be convinced to expand foo in-line
without expanding inner, you could get the effect of implementing
defaulting in the caller.)
You can't reference present(b) in inner, but most procedures are
unlikely to need the value of present(b) once the value of b has been
established. If you have one of the rare exceptions, you can add
explicit code to capture the value of present(b) in foo and make it
available in inner using another name (e.g., present_b).
An advantage of using argument association rather than assignment is
that it works for intent(out) and intent(inout) arguments as well as
intent(in). Many library routines of that era had arguments that were
nothing but workspace to be used by the routine. This approach could be
used to create an interface that would work with existing programs but
would allow the workspace argument(s) to be omitted in new programs.
Optional return of intermediate computations could be implemented
similarly. Optional dummy procedures are another possibility.
One could use pointer association rather than argument association to
avoid the extra calls, but pointer association often has more negative
effects on optimization than argument association.
If one needs to default a second argument c, the main logic of foo could
be moved from inner to a second internal procedure inner2, and inner
could become a defaulting "wrapper" similar to foo itself. In Fortran
90, inner2 could not be nested inside inner, so b would have to be
explicitly passed to inner2 as well as c. This may be desirable even if
you can nest inner2 inside inner, as there may be added cost to
multi-level host association.
A possible hybrid approach to defaulting multiple arguments might be to
use assignment and pointer assignment to collect the defaulting in
different names and then pass those different names to something like
inner to get back to the original argument names.
If you add conditional expression evaluation like the C ?: ternary
operator to Fortran, the body of foo in the intent(in) case could be
simplified to something like
call inner(b = present(b) ? b : 42)
greatly reducing its verbosity.
--
After writing the above, it occurs to me that we might be able to have
much of the power of the above approach in a much simpler form. Imagine
an assignment-like statement something like
optional-dummy-argument ?=> argument
with the semantics that if no actual argument is associated with that
optional argument on the LHS, the RHS is evaluated/identified and
becomes the actual argument associated with the dummy argument. This
would reduce Thomas' example to
b ?=> 42
For the most common case, we might want to allow this ?=> operation to
be specified on the declaration of an optional dummy argument, but
having the stand-alone statement form allows more complex logic to be
used in the determination of what actual argument to associate when such
complexity is needed. (It would also allow the defaulting to be
deferred until the value of present can be captured, in programs that
need that.) [The existing rules that prevent an actual argument from
"going away" while associated with a dummy argument might require some
reinforcing to cover additional cases that could occur with this operation.]
-Kurt
P.S. To those who might have preferred that I put this on github, I
apologize, but that is one new trick this old dog hasn't gotten around
to learning.
On 1/20/2021 1:32 AM, Thomas König via J3 wrote:
>
> A somewhat common thing to do for optional arguments is to set them
> to a default value if the caller did not specify anything. One idiom
> to do this would be
>
> subroutine foo(a, b)
> integer, intent(in), optional :: b
> integer :: b_val
> ...
> if (present(b)) then
> b_val = b
> else
> b_val = 42
> end if
>
> and then use b_val and c_val. This idiom is wasteful of lines (as
> written, it takes five lines of code for a single default value). More
> importantly, it is also error-prone because it is necessary to remember
> to use b_val instead of b.
>
> The first drawback could be mitigated by doing something like
>
> if (present(b)) b_val = b
> if (.not.present(b)) b_val = 42
>
> (not very clear, in my opinion) or by using yet-to-be-added conditional
> expressions, but people would still have to use a different variable
> name for essentially the same thing.
>
> What could help is to add some way to express default initialization for
> optional arguments, something along the lines of
>
> integer, intent(in), optional :: b = 42
>
> or
>
> integer, intent(in), optional, default (42) :: b
>
> Opinions?
>
> Best regards
>
> Thomas
More information about the J3
mailing list