-
Notifications
You must be signed in to change notification settings - Fork 17
Allow setting default value for optional dummy argument #22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Yes, great idea. I usually do it like this: 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_
c_ = 0
if (present(c)) c_ = c
quadratic = a + b * x + c_ * x**2
end function quadratic Which is simpler than your original code, but it's still cumbersome to always introduce the local variable and do the Does Fortran support default non-optional arguments? My only suggestion would be to perhaps introduce default values for any arguments, not just optional. |
What is the meaning of default non-optional arguments? If an argument is required, where does its default value come into play? |
My bad, I think you are right. It only makes sense for optional arguments. |
The other suggestion I have is that in Fortran one specifies all argument things in the declaration, not in the argument list so perhaps something like this: 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, default :: c = 0
quadratic = a + b * x + c * x**2
end function quadratic Would be more consistent with Fortran conventions. The new integer, default :: n = 5 Perhaps one could use |
I like the Of course, I had to use the more verbose variant to better sell the proposal. :D |
The proposal can have both options, and the community and committee can discuss pros and cons of both, and then pick one.
I would suggest you change it to the shorter version, as we want to compare against the best current practice, not a longer variant. |
I'd be happy to prepare the first draft of the proposal, and you can take it from there. Does target for meeting #221 (February 2020) make sense? |
Yes! The October Committee Meeting ended, and what I would like to do differently than what the committee has been doing so far, is to prepare a very good proposal collaboratively on GitHub. Send a PR once you have a draft, let's work on it together and with others. And then once a good initial proposal is ready, I'll pitch it to the j3-members committee mailinglist, to get feedback from the committee, and that way we have a very solid proposal when the committee meets the next time and we'll take it from there. Finally, I would reiterate my invitation for you to join the committee. We need help. |
This should be implemented in the language. It's a simple change and it would go a long way. In the meantime, we will implement a helper function in stdlib: fortran-lang/stdlib#62, here is a preliminary implementation: fortran-lang/stdlib#73. The above example would look: 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_
c_ = optval(c, 0.)
quadratic = a + b * x + c_ * x**2
end function quadratic Which is not bad, but not as nice as having it in the language: 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, default :: c = 0
quadratic = a + b * x + c * x**2
end function quadratic Note: in this particular case, since we need 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
quadratic = a + b * x + optval(c, 0.) * x**2
end function quadratic but this does not work well if |
This proposal got some votes in the user survey for 202X, and it is one I personally like. It appeared on Data subgroup's wishlist at meeting 215 (see https://j3-fortran.org/doc/year/18/18-122r1.txt) https://j3-fortran.org/doc/year/18/18-136r1.txt with "use cases" was passed at meeting 216, but it didn't make it onto the "official" US request list - it's not clear to me if this was an oversight or the lack of a formal proposal. In either case, I would support this for 202Y. |
I too support this. |
Ok, let's write a good proposal for this and submit for the February meeting (#122) to get initial feedback from the committee. As indicated at #122, the following people listed this proposal in their top 5: @milancurcic would you be able to take a lead on writing a proposal for this? The others listed in this comment, if you could help Milan write it, that would be very helpful. (I'll try to help too, but there are other proposals that I want to help with also.) |
Yes I am in. While we are here, I have some questions & comments... Firstly, why should the DEFAULT keyword be needed? Seems to me that the mere presence of an equated value after the variable name will be sufficient, as in: real function quadratic(x, a, b, c)
! returns a + b * x + c * x2 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 * x2
end function quadratic
As far as I am aware, there is currently no way the language will allow an equated value for a variable declared as a dummy argument, so this seems sufficient to me. Also, providing a new keyword like DEFAULT might mislead folk into trying to put it on non-optional arguments and other (local variable) declarations. (If the intention is to distinguish this from the implicit save problem, then the name DEFAULT is not a good one, because it doesn't really mean "do not save". And I think we should keep this proposal well clear of any argument about implicit save.) Also also, when 2 or more arguments are declared in one statement, we would not wish to require them all to be given a default value, or none of them; this would be the implication of the presence of the DEFAULT keyword. Second: do we allow the default value to change a simple variable into an array? Eg:
Note that, absent the default, there is nothing in the declaration of B that makes it an array. IMO this should not be allowed, explicit shape information or the DIMENSION keyword should be required. Third: should the feature work for UDTs? Eg:
Fourth: should it work on an OPTIONAL statement? eg:
Fifth: what about allocatables? eg:
This creates a default allocated array with 2 elements. I can't, however, see an obvious way to get an unallocated array as a default. Maybe it could be done by combining with the next one... Sixth: should it allow initialization to a non-constant expression? eg:
...That's probably more than enough for the moment. |
Sure, I will submit an initial draft in a PR later this week and we can iterate on it together. |
@qolin1 thanks a lot for the feedback. Excellent ideas. Here are my comments.
real function quadratic(x, a, b, c)
! returns a + b * x + c * x2 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 * x2
end function quadratic
|
@qolin1 these are my thoughts.
Subroutine fred(a,b)
real, intent(in) :: a
real, intent(in), allocatable, optional :: b(:) = [1d0,2d0] should be allowed with
|
Regarding 1.: Note that, as I mention in #40, that "One can apparently use P.S. @milancurcic make sure you use this argument in the proposal. |
@certik I was looking for the fingers crossed emoji to be added next to the thumb up as a reaction to your post, but it's not available on GitHub... 😜 |
Any proposal should specify whether the default value may be a specification expression or must be a constant expression. The latter would be consistent with initialization, but the former would be more useful. Note that the rules for specification expressions already prohibit an object designator with Since a default value would be allowed only for optional arguments, that implies an explicit interface requirement. |
Um Steve, Please clarify for us what is meant by an "object designator". If we have an example default initialization such as:
My understanding is that b, c and d are object designators, but aa is not. Please correct me. |
Sorry, was quoting the standard regarding specification expressions. In your example, b, c and d are the object designators in question. The issue is what is allowed to appear in the default value expression. If it's a specification expression, it can reference things such as other dummy arguments and module variables. If it's a constant expression, it is restricted to only those things that can be evaluated at compile-time, which is the case for initialization. (Indeed these used to be called "initialization expressions".) The more details worked out in advance for a proposal, the better chance it has to move forward. One can even propose edits (ideally including complete new wording to make it easier to understand and read.) What I envision for this is that the caller could evaluate and pass the default values, since it already needs to know how to do this for some specification expressions. |
This proposal was discussed on Tuesday Feb 25 in the morning, I set the timer for 5 minutes, @zjibben gave an overview for about 3 minutes and then we discussed for about 20 minutes at the plenary. We took notes of the feedback and I summarized it below. Overall, we got excellent highly detailed feedback, and I would say the committee was generally positive about the idea. The approach suggested might not work directly, but the committee suggested an alternative approach that might work. Also lots of details must be figured out and fleshed out, and the proposal must be rewritten / improved accordingly. Here is the detailed feedback. I tried to summarize it first, but not all the comments are necessarily consistent, so I simply present the comments:
To summarize, the issue is that we do not want to change the behavior of present(). But then the issue is that it becomes really complicated if the user provides the value high in the call chain, then the nested subroutines might change how they behave, thus potentially creating performance bottlenecks. The best way forward seems to be not to tie the default value with optional arguments. Rather, to introduce “initial” (or “default”) attribute that can be applied to both local variables, as well as dummy variables. If applied to dummy variables, it would have no change in the subroutine itself --- the dummy argument would always be provided. However, at the call site, the compiler would put in the default value if the user does not provide it. That way there is no performance hit, and from the user perspective it achieves the same. |
Thanks for polishing the notes and posting @certik! |
@milancurcic indeed, it went really well. We have done another one (#1) today, and in general I think we will be doing this from now on. So this is a great avenue how to have a discussion with the wider community and how the community can propose ideas for the language. |
I have written a revision of the paper by @milancurcic, @jvdp1, and @zjibben that attempts to address most of the issues raised at the Feb. 25 meeting, and a few other issues that have come to my mind. The paper differs from the original primarily by:
|
This sounds great - when can we see the paper? The changes you propose are all those I support. |
I am a novice to GitHub. The introductory material on GitHub does a good job of telling me how to start my own project, but not how to participate in someone else's project. What steps do I need to take to upload the paper named revised_proposal.txt? |
William, great. If you navigate here: In the upper right corner of the UI there is an "Edit" button. If you click on it, it will take you to an editor which you can use to replace the existing paper with yours. Submitting the change will open a new Pull Request in which we will then discuss and edit the revised version. The above flow assumes that we edit existing proposal txt files. @certik @zjibben should we instead keep the old versions as separate files and add the new one in its own file (like J3 does)? |
FWIW I named my version revised_proposl.txt in the expectation that we would retain original, proposal.txt, for the records.
… On Jul 10, 2020, at 3:05 PM, Milan Curcic ***@***.***> wrote:
William, great. If you navigate here:
https://github.com/j3-fortran/fortran_proposals/blob/master/proposals/default_optional_arguments/proposal.txt <https://github.com/j3-fortran/fortran_proposals/blob/master/proposals/default_optional_arguments/proposal.txt>
In the upper right corner of the UI there is an "Edit" button. If you click on it, it will take you to an editor which you can use to replace the existing paper with yours. Submitting the change will open a new Pull Request in which we will then discuss and edit the revised version.
The above flow assumes that we edit existing proposal txt files. @certik <https://github.com/certik> @zjibben <https://github.com/zjibben> should we instead keep the old versions as separate files and add the new one in its own file (like J3 does)?
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub <#22 (comment)>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/APTQDORHHFYI4SZXSHT3AGTR257CRANCNFSM4JCMOXGQ>.
|
FWIW trying to add a file at https://github.com/j3-fortran/fortran_proposals/tree/master/proposals/default_optional_arguments |
I am thinking of preserving the original paper, as the discussion is based on that. But let's have a dedicated directory for all the papers and documents related to this feature.
…On Fri, Jul 10, 2020, at 3:05 PM, Milan Curcic wrote:
William, great. If you navigate here:
https://github.com/j3-fortran/fortran_proposals/blob/master/proposals/default_optional_arguments/proposal.txt
In the upper right corner of the UI there is an "Edit" button. If you
click on it, it will take you to an editor which you can use to replace
the existing paper with yours. Submitting the change will open a new
Pull Request in which we will then discuss and edit the revised version.
The above flow assumes that we edit existing proposal txt files.
@certik <https://github.com/certik> @zjibben
<https://github.com/zjibben> should we instead keep the old versions as
separate files and add the new one in its own file (like J3 does)?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#22 (comment)>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/AAAFAWFMV63Z2B7JFOA3NO3R257CPANCNFSM4JCMOXGQ>.
|
You already have such a directory |
William, here's what you need to do:
|
The revised_proposal.txt has been checked into the repository. |
Just a comment. Keeping old files around with a naming convention for revisions kind of misses the point of having a version control system (the old versions can still be accessed if desired). But since not everyone interested in the project will be familiar with git, it's probably best to do it anyway. |
@everythingfunctional I think we should work on just one file and add changes to it (e.g. in #175), but once we submit it to the J3 meeting for consideration, I think it's good to keep a copy around, so that you don't have to go into git history all the time to see what the original was that the J3 discussion is based upon. |
FWIW I have changed how the proposal discusses arguments with no intent. Instead of assuming that such arguments will be treated the same as INTENT(IN) or VALUE, I call no intent out separately and note how the wording in the standard should be phrased to either treat them the same as INTENT(IN) or VALUE or treat them like INTENT(INOUT) or INTENT(OUT). |
My revised proposal was that default values are relevant for dummy arguments with INTENT(IN) or VALUE explicity specified. It originally had no intent as an option, but consistently repeating “INTENT(IN), or, VALUE, or no intent” was awkward, and there was the problem that many people assume the no intent was,ery similar to INtENT(INOUT). It did not propose to “automatically” give some different attributes to no intent arguments. Users would have to explicitly give the DEFAULT attribute, and write out the DEFAULT assignment.
… On Jul 25, 2020, at 9:12 PM, septcolor ***@***.***> wrote:
I was assuming that the default value is relevant only for dummy arguments with INTENT(IN) explicitly specified. On the other hand, if the dummy arguments with no intent are automatically given some different attributes in future (say, intent(OUT) or VALUE), doesn't it mean that it silently breaks existing codes? (For example, INTENT(OUT) deallocates allocatable dummy arrays automatically, while VALUE changes the semantics from pass-by-reference to pass-by-value (in practice), which is crucial for foreign language interface.
I guess it would already be extremely useful to be able to specify the default value for only the case where INTENT(IN), VALUE is explicitly specified, while no default value is allowed otherwise. At the moment, I need to use PRESENT() and declare additional local variables to set the default value, which is very inconvenient (as compared to other languages).
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub <#22 (comment)>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/APTQDOVDYPMAWERFPLL6Q3TR5ONLBANCNFSM4JCMOXGQ>.
|
Just to remember that a revised version of the proposal is in the MR: #175 . It doesn't appear clearly in this thread... |
During the development of Fortran 90, back when it was still called Fortran 8x, there was a proposal for an AUTOMATIC attribute. When RECURSIVE was added, the argument was that every variable that isn't SAVE is automatic. The standard's definition of "automatic variable" was needlessly narrowed to one that used a variable to specify a bound or length parameter. I no longer have records from that time; I sent them to the Computer History Museum. Loren Meissner is the curator for them. Had the AUTOMATIC attribute been retained, an obvious definition of AUTOMATIC with initialization would have meant automatic initialization, not SAVE. Specifying the AUTOMATIC attribute without initialization would simply confirm what we already say now. Explicitly specifying AUTOMATIC and SAVE for a particular variable should be prohibited. Explicitly specified AUTOMATIC would override default SAVE. AUTOMATIC would naturally be prohibited for storage-associated variables. Once AUTOMATIC is defined that way, all that is necessary to provide default values for absent optional arguments is to define an absent OPTIONAL argument that has initialization to be a local automatic variable. The PRESENT intrinsic should still be available to inquire whether there was an associated actual argument. Even if there's a value for a local variable acting in lieu of the absent dummy argument, it's still useful to know whether the actual argument was present. |
"Same syntax as Python keyword args, so it would be intuitive to Python developers learning Fortran;" "keyword argument" is only relevant when you call a function. |
C++17 has introduced a class template An example of passing an optional value to a subroutine looks as follows: #include <iostream>
#include <optional>
void display(std::optional<int> opt_a) {
if (opt_a) {
std::cout << "a is present with value " << *opt_a << '\n';
} else {
std::cout << "a is not present" << '\n';
}
}
int main() {
auto a = std::make_optional<int>(42);
display(a);
display({});
return 0;
} To supply a default non-null value you could modify the prototype: void display(std::optional<int> a = {42}) The class template also has an accessor method int a = opt_a.value_or(42) This is kind of similar to the the Fortran-lang stdlib function integer :: c_
c_ = c .presentOr. 42. ! present(c) ? c : 42 fails since the argument of an operator interface cannot be optional. |
variants:
|
Problem
Currently, Fortran doesn't allow setting a default value for optional dummy arguments. If declaring a dummy argument as
optional
, the user must:Example
Example of a quadratic function that optionally allows evaluation of its linear form:
Checking for presence of the optional argument and using a temporary variable is cumbersome and error-prone, and it is even more so if the same needs to be done for many optional arguments.
Solution
Allow setting the default value of the optional argument in the
function
(orsubroutine
) statement by using the assignment operator=
:This is similar to how Python keyword arguments are defined.
Comments
The text was updated successfully, but these errors were encountered: