-
Notifications
You must be signed in to change notification settings - Fork 16
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
Conditional Expressions #183
Comments
Which of these do people find the most readable and least readable? My own feeling (from most readable to least readable):
We should also consider more use cases. I also asked at https://fortran-lang.discourse.group/t/202x-feature-conditional-expressions/329 to get more feedback on this feature. |
To be honest I find both variants suboptimal (the first one would make an if/else/endif block return a value when being used inline, and simply run code as usual when not, but would be easier to read) and I wonder whether there is really a need for general conditional expressions or whether a ternary operator would be sufficient. In the latter case I would suggest the Python-based syntax (which one could even nest): CALL SUB(A, B, C, D IF PRESENT(D) ELSE (EPSILON(X) IF (X < 1) ELSE SPACING(X))) |
@dev-zero in your opinion, can you rate by readability / preference the three options above plus your proposed Python-based syntax? The ternary operator (that you presumably like) is the "? syntax" which is one of the proposed ideas, but you also say they are "suboptimal", so I am confused. |
From most to least readable:
Caveat: I use 2 in Python a lot so I'm used to it, thus my preference. |
FWIW my preference from most to least readable is:
Aside the reduced readability, IMHO I cannot see any practical advantage in the new proposed expressions. |
My preference in terms of readability:
As for why I dislike the A general start and end marker CALL SUB(A, B, C, ? (PRESENT(D)) D : ? (X < 1) EPSILON(X) : SPACING(X) ? ?) correct? With the second to last |
I think the readability question might slightly be missing the point. The question isn't about how easy is it to see what the code is doing, but how easy is it to see the code's intent. To me, an To use the example from above, if I see
It's not immediately obvious (especially in codes that are more complicated than this example) that we are definitely going to be calling
that intent is put front and center without having to compare each branch in an entire So, my preferences would be for
|
If the idea is to call IF (PRESENT(D)) THEN
D2 = D
ELSE IF (X<1) THEN
D2 = EPSILON(X)
ELSE
D2 = SPACING(X)
END IF
CALL SUB(A,B,C,D2) Which I personally find more readable than: CALL SUB(A, B, C, IF (PRESENT(D) THEN D ELSE IF (X < 1) THEN EPSILON(X) ELSE SPACING(X) END IF) But it is true you have to declare an extra variable (although the performance of the code should be identical with a good compiler). |
Is a subset of this a good candidate for a stdlib function |
It would be, but |
@certik , while you've removed the difficulty of seeing "do we always call |
My understanding is that this feature was approved by WG5 for inclusion into 202X based on a survey, where people expressed a wish to have conditional expressions in Fortran, but I don't think there were concrete proposals how it would look like, just that the feature would be nice to have. @sblionel is that an accurate statement? @everythingfunctional explained well the main argument for conditional expressions is that they enforce a single logical operation to be assigned somewhere (as the result of the conditional expression). I agree that at this level of "requirements" it seems like a good idea and I am not against that (I use it sometimes in Python, although rarely; I never use the A prior compiler implementation of this would be very helpful, so that we can play with it more, before putting it into the language. |
Adopting |
I forgot: why aren't we just fixing |
@klausler the arguments that have been put forth against fixing
I don't personally understand the arguments, but I think @everythingfunctional does? Brad, can you summarize here why we cannot extend |
Yes. The way we work is that WG5 outlines the general idea and J3 develops that into a specific proposal. |
|
@klausler , I actually proposed just that on the J3 discussion board, but Malcolm was able to give me convincing reasons against, along the lines of my previous comment. |
@everythingfunctional cannot the shape in |
But |
Rank, yes, apart from assumed-rank dummy arguments, but not shape. |
I suppose that's true. In fact, one could go a small step further and state that the "unused" argument is evaluated only if necessary to determine the resultant shape. I.e., if the array argument is selected, the scalar argument wouldn't need to be evaluated. However, that still wouldn't satisfy one of the use cases (although it's not one I find particularly compelling); conditionally supplying an optional argument. I think the proposed conditional expressions provide a clean way to provide that, with a convenient place to put the desired deferred evaluation functionality. |
I'd prefer to see something like select
case (present(d))
call sub(a,b,c,d)
case (i<n) then case (a(i)==0)
call sub(a,b,c,epsilon(x))
case default
call sub(a,b,c,spacing(x))
end select Or possibly use |
#22 is about providing a default value if an argument is not provided; it is about the callee. This proposal is about how an argument could be provided or not; it is about the caller. For example, say if
|
@everythingfunctional how would that be written using conditional expressions? I don't think that's possible either, or am I missing something? |
The proposal specifically allows for the
|
I see, I didn't realize that. Btw, I think the syntax is: call sub(a, if (x >= 0.1) then (x) end if) or call sub(a, if (x >= 0.1) then x end if) But adding parentheses around Well, we can have some kind of syntax or keyword for merge, such as |
So you are saying that the fortran tokenizer get rid of all whitespace... hmpf that would be a problem indeed :/ |
In my opinion, with fixed-form source having been made obsolescent, at some point we should be allowed to stop worrying about how new features can expressed without significant whitespace. And so, |
It was discussed at the October 2020 Fortran call that another good use case for this feature would be for array initializers. In Python you can do (I corrected the syntax, thanks to @ivan-pi's comment below): [x+1 if x >= 45 else x+5 for x in range(1, N+1)] So in Fortran you could do: [ (if (x >= 45) then x+1 else x+5 endif, x = 1, N) ] Since conditional expressions are just expressions, this should just work. |
This triggers a SyntaxError in Python. The correct way would be: [x + 1 if x > 45 else x+5 for x in range(1,N+1)] So the syntax is expression_if_true if condition else expression_if_false Edit: following Python if expression syntax, the example from above would be: CALL SUB(A, B, C, D IF PRESENT(D) ELSE (EPSILON(X) IF X < 1 ELSE SPACING(X))) It seems fairly nice, you just need to remember the condition is in the middle. Edit2: Oops, I just noticed the posts from @14NGiestas above. |
New syntax paper being proposed at the June 2021 J3 Committee meeting: |
What a mess! Tell me again how this is supposed to be better than a |
@klausler thanks for the feedback. I personally think we should not do this feature at all (i.e. NO on the 21-157 proposal), as the two syntaxes (keyword and ?) seem worse than not doing this (based on the feedback both online above, as well as in private that I got). We can pursue the |
I'm sure we've talked about this. In short, strengthen |
The idea of modifying MERGE was discussed several meetings ago (I can't find which one). I liked the idea, but there were complaints that it would slow down MERGE for everyone and it failed. |
That's astonishing and credible at the same time. |
I use MERGE but hate the name, so how about "CHOOSE", and it would short-circuit and allow character variables of different lengths as arguments. I think the short-circuit would be enough, but I would definitely want it to be able to work with optional parameters so it could be used with subroutine a(opt)
character(len=*),intent(in),optional :: opt
character(len=:),allocatable :: opt_local
opt_local=choose(lower(opt_local),'default',present(opt))
...
so "lower" would not be called on an undefined value. |
I really do mean it to be different than MERGE; it would return the first or second argument, which could be of different size, not elementally using a mask, but just based on whether the third argument is T or F. |
And I wish MERGE had been called PICK, given that SELECT is taken. |
Whoops, I accidentally closed this issue by hitting the wrong button. Fixing now. |
Here is the discussion of a new intrinsic, say, |
Finally, here is another paper that we will vote on next Monday: This is essentially the "arrow form": #183 (comment) What is your opinion on that one? |
It still changes the syntax of expressions, so it would affect parsing, AST definitions, &c.; this would be needless work in every implementation over and above the straightforward semantic analysis that a new intrinsic function would cost. Tokenizing the new "->' symbol correctly would impose a look-ahead requirement on tokenization of the current "-". And it implies but does not specify the operator precedence of a "cond-expr" -- does it replace a current parenthesized expression in the syntax? Can it be used as a variable? I hate it less than the first two ("IF" and the "?") but not by much. I don't understand the aversion to using an intrinsic function; they're easy to parse, they nest in obvious ways, and they're more likely to be understood than new operator syntax. |
The only arguments I've heard against an intrinsic function are summarized here: #183 (comment) (harder to do chaining, possibly more confusing due to some arguments not being evaluated). |
Would this proposal be limited to use in function calls? And to one-line I'd like to suggest a slight frame-change, and propose allowing
If this syntax was included, then it gives you the power of "conditional arguments", and more besides. Like a lot of these proposals, this doesn't give you something you couldn't already do, but I feel like this is both clearer and more powerful than the existing alternatives, and as previously mentioned can be chained with other things like array initialisation. I don't believe this would conflict with existing syntax, in that I don't believe you can currently have statements with return values on their own (e.g. the line I guess you'd need to decide what to do if when there was no return value because there was no I suppose also if For language consistency reasons, I'm against the other syntax options (as much as I personally like the pythonic syntax in python). I can see the arguments for dropping the For clarity, my ranking of the proposed syntaxes is:
|
So you want Fortran parsers to be able to handle statements as parts of expressions, and add a new kind of expression-only statement. What happens if one of your "then" or "else" parts is "returning" the value of a variable named ELSE? or END? |
I think all the proposed syntaxes are OK (just OK) for simple conditional expressions, but that they become very hard to read for compound conditional expressions. I can only speak for myself, but every example I read with compound conditional expressions I have to mentally step through branch-by-branch to make sure I understand what it's doing. Basically, in reading
I mentally reconstruct
If I'm reading someone else's code, I'd much rather see the second form than the first (or any of it's proposed variations). Yes, it's verbose, but it's also obvious. I'm more partial to a function-like syntax, i.e., |
We started a poll to collect feedback on this feature. We’ll present the results of the poll to the Committee on Monday when the proposals for this feature are due for discussion and a vote. |
Ideally, yes. I think it would add benefits to the language.
I'm surprised these are not already reserved words. I guess a possible solution would be to require brackets or similar, so that the syntax would be
and with an interesting choice of variable names,
I guess this would also help with syntax parsing. |
I support the function syntax. My suggested name for |
This is not the same thing as lazy evaluation as the term is commonly understood in programming language theory. |
It is a lot easier to do in an interpreted language, and some might not like the reuse of IF but in a little scripting language I have where everything is a function IF acts like a function if given more than one parameter and only evaluates one of the following expressions depending on the results of the conditional, in the form if(expression,eval_if_true, eval_if_false). I have used it so long it seems natural to me. Trying to put that into a Fortran context it might look like program testit
contains
call passto(20)
call passto()
subroutine passto(a)
integer,optional :: a
integer :: b
b=if(present(a),a,10)
write(*,*)b
end subroutine passto
end program testit I can think of some reasons that might be disliked, but I have seen a lot of comments about the complexity of some of the solutions and I have used that for a long time and it is pretty easy to type even interactively. The language also lets logicals return an integer and a lot of other un-Fortranish things so things like like doing a sum() of a bunch of expressions and being able to do something if 2 out of 3 are true is easy, or doing a max() or min() on a list of logical expressions makes sense; but now that there is ANY() and ALL() Fortran can do something similiar now. Of course only evaluating one of the expressions is easy in a scripting language and very much against standard Fortran behavior. |
Relevant papers:
Taking the second example from the
18-274
paper:One proposed syntax is "keyword syntax":
The second proposed syntax is "? syntax":
The text was updated successfully, but these errors were encountered: