[J3] [EXTERNAL] Re: Why is += missing?
Clune, Thomas L. (GSFC-6101)
thomas.l.clune at nasa.gov
Thu Sep 2 16:45:19 UTC 2021
If we decide to go down this path, then I would very firmly want “+=” to be a distinct assignment operator and allow user defined operations using ASSIGNMENT(+=). (Yet one more wrinkle is that ASSIGNMENT(+=) would need to require that the LHS argument be INTENT(INOUT) whereas ASSIGNMENT(=) is allowed to be INTENT(OUT) or INTENT(INOUT).)
Regarding the slippery slope – everyone will have their own line. Mine is firmly _before_ .DefinedBinoryOp.= And probably well before most of the options in your list of possibilities. I might be open to a single new assignment-like operator that would not be “burdened” with the implied meanings of the existing ones. Something like “^=”
I’m not terribly motivated by the “cousins” you mentioned. If I really had a lot of that in code and ASSOCIATE seemed too intrusive, I’d define my own wrapper procedures such as INPLACE_MAX:
call INPLACE_MAX(a,b) ! equivalent to a = max(a,b)
Sure the same argument could be made for any of the above, but … the += case feels like it arises far more frequently and also seems to provide more immediate code improvement.
From: J3 <j3-bounces at mailman.j3-fortran.org> on behalf of j3 <j3 at mailman.j3-fortran.org>
Reply-To: j3 <j3 at mailman.j3-fortran.org>
Date: Thursday, September 2, 2021 at 11:18 AM
To: j3 <j3 at mailman.j3-fortran.org>
Cc: Kurt W Hirchert <hirchert at uiuc.edu>
Subject: [EXTERNAL] Re: [J3] Why is += missing?
Resent-From: Kurt W Hirchert <kurthirchert at gmail.com>
Resent-To: j3 <j3 at mailman.j3-fortran.org>
Resent-Date: Thursday, September 2, 2021 at 11:15 AM
As often happens, this thread seems to have died out before I had my thoughts organized. Since a couple of my ideas seem to be ones no one else has expressed, I will go ahead and post this message. Read it or skip it as you feel appropriate.
Random observations that may not be terribly significant
If there were any chance of doing only += without the other op= variants, it might be worth noting that A-=B can be simulated by A+=-B.
The discussion about /= may be a bit of a tempest in a teapot. As far as I can tell, there is no ambiguity for compilers, and the confusion for human beings may not be that great. In the existing language, :, =, =>, /, and * all have varying meanings in different places, and that doesn’t seem to confuse people. Furthermore, in an expression, /= is a two-character representation of a single mathematical symbol, but the assignment version is likely to be perceived as just a specific example of a representation of an op followed by =, so the mental parsing may be quite different.
If instead of writing
a_long_and_or_complicated_identifier = a_long_and_or_complicated_identifier + more_stuff
you write
a_long_and_or_complicated_identifier = &
a_long_and_or_complicated_identifier + more_stuff
(lining up the repeated part on successive lines), you can minimize many of the problems that were offered as motivation for +=. Of course, this doesn’t mitigate the tedium of typing all that twice or the general verbosity.
Is += one symbol or two successive symbols? The answer has the immediate impact of saying whether a blank is allowed between the two characters, but there can also be deeper semantic issues.
If a program defines OPERATOR(+) (and possibly ASSIGNMENT(=)) for a derived type, can += be used on that type, or is += defined only for intrinsic numeric types? If so, can this intrinsic interpretation be overridden (with an ASSIGNMENT (+=) interface)? (The question of one symbol or two may have a bearing on this question.)
When we were working on the array computation features in Fortran 90, I developed the habit of looking at computations in loops and asking myself whether they could be converted to array computations. During this discussion, I found myself asking how many potential uses of += with a long and/or complicated left-hand side are computing sums that could be done with some form of SUM. This probably isn’t a relevant question as long as there are enough that can’t reasonably be done with SUM.
Two observations that seem more significant to me
My first significant concern about += is the “slippery slope”. As soon as += was mentioned, -=, *=, and /= were expected. Since that accounts for four of the five numeric operators, it was not long before **= was mentioned. Other types data were not discussed here, but Perl, example, has the equivalents of //=, .AND.=, and .OR.=. In the extreme, this might lead to .DefinedBinaryOp.= so that there would be no necessity to remember which binary operators can precede the =. (I note in passing that if one likes the +.= solution to the /= issue, .OR..= looks a bit funky to me.)
My second significant concern is that even if all these += cousins are added to the language, this will still address only “the tip of the iceberg”. Consider the following examples: (In the interest reducing my typing, I will use ALAOCI in place of a_long_and_or_complicated_identifier.)
ALAOCI = MAX(ALAOCI,something_else) ! or MIN
ALAOCintegerI = MOD(ALAOCintegerI+1,n)
ALAOCmatrixI = MATMUL(ALAOCmatixI,another_matrix)
ALAOCintegerI = IOR(ALAOCintegerI,2**k) ! set bit k
ALAOCintegerI = IAND(ALAOCintegerI,NOT(2**k) ! clear bit k
ALAOCcharI = chars//ALAOCcharI
! prepend chars (cf. Using //= to append chars)
ALAOCcharI = TRIM(ALAOCcharI)//chars
! better append for non-ALLOCATABLE char variables
ALAOCcharI = ‘[‘//TRIM(ALAOCcharI)//’]’ ! add brackets
ALAOCcharI = ALAOCcharI(2:INDEX(ALAOCcharI,’]’)-1
! remove brackets
ALAOClogicalI = .NOT.ALAOClogicalI ! toggle variable
ALAOCnumericI = -ALAOCnumericI ! negate variable
ALAOCrealI = LOG(ALAOCrealI) ! convert to logarithmic data
ALAOCrealI = EXP(ALAOCrealI) ! convert back
ALAOCpointerI => ALAOCpointerI%next ! remove from linked list
Solutions?
Michael Klemm pointed out a proposed feature in Ada, using @ in the right-hand side of an assignment as a stand-in for the left-hand side of that assignment. Thus, instead of simplifying
ALAOCI = ALAOCI + whatever
to
ALAOCI += whatever
you could write
ALAOCI = @+whatever
If I correctly managed the switch to a monofont, it should be apparent that this is only one character longer than the += version and still much shorter than the original version. (If the /= issue leads to the use of +.=, the Ada-like version would be the same length.) Unlike +=, it applies equally well to “the entire iceberg”. It appears to avoid all the syntactic and semantic issues that came up with +=. Unless one is hellbent on copying C, I would consider += to be an inferior solution to the problem and encourage that it be abandoned in favor of pursuing an Ada-like solution (or something better if someone can come up with it).
Although I like the basic idea of this feature, I will offer a couple of quibbles: I am not overly fond of the character @ for this purpose, because it “looks funny” to me and because I generally prefer using characters in use in the language over using new characters. My first thought for a more Fortran-like stand-in was .LHS. (or some other .WORD. if you don’t like LHS). This opens the possibility of compatibility issues in programs that already use .LHS. as a defined operator. I believe all these issues can be resolved, but people may not like my resolution alternatives. Since underscore is not allowed as the first character in a name, _LHS_ is another alternative that might look slightly less Fortran-like but would avoid those issues. Both of these are somewhat longer than the single at sign, so I also offer .. (i.e., two consecutive periods) or _ (i.e. a single underscore). At the moment, I think I like the single underscore best, but I haven’t spent much time thinking about this issue.
Given that defined assignment interfaces make assignment statements an alternative way to CALL a subroutine, it might be nice for consistency if in a CALL statement , @ (or whatever is chosen) were made a stand-in for the first subroutine argument.
Van Snyder pointed out a proposal of his that attempted to be more general, allowing stand-ins to be defined for more than just the left-hand side. Most of the time, I tend to prefer general solutions over more specific solutions, but in this case, the syntax needed to handle the case of a stand-in for only the left-hand side is more than in the Ada-like proposal, and it appears to me that there are orders of magnitude more cases where one could use a stand-in for the left-hand side than for the use of other stand-ins. Thus, I do not see Van’s proposal as a good alternative to the Ada-like feature. However, if there are enough uses for these other stand-ins, it might be worth doing Van’s proposal (or its equivalent) in addition to the Ada-like proposal, provided one can resolve their incompatible uses of the at sign character.
While I like the functionality of Van’s proposal, I don’t particularly like its syntax. In my opinion, intermixing the stand-in definitions with the rest of the statement hurts readability, and I have my usual reservations about introducing the at sign character in this way, so I will suggest an alternative with equivalent functionality but a different syntax:
As Van points out, this is essentially ASSOCIATE limited to a single statement. Why doesn’t Van want to simply use ASSOCIATE? I assume that it is because in cases like this, the extra syntactic overhead of the ASSOCIATE construct adds painful verbosity. What can we do to reduce that overhead? Consider for a moment the IF statement and the IF construct. The IF statement
IF (logical-scalar-expression) action-stmt
is essentially a lower-overhead way of writing
IF (logical-scalar-expression) THEN
action-stmt
END IF
What I would suggest is the addition of a simple ASSOCIATE statement
ASSOCIATE (association-list) action-stmt
that would be the equivalent of
ASSOCIATE (association-list)
action-stmt
END ASSOCIATE
This alone cuts more than half of the syntactic overhead. My more radical suggestion would be to make the keyword ASSOCIATE optional in this form:
[ASSOCIATE] (association-list) action-stmt
Unless I’ve missed something, this remains unambiguous, but would allow you write something like
(V=>ALAOCI) CALL backwards_set(V+whatever,V)
It is slightly more verbose than Van’s equivalent
CALL backwards_set((V @ ALAOCI)+whatever,V)
but I find putting the stand-in definition(s) in front to be more readable, and I suspect using an extension of what we already have may be more acceptable to the committee than an entirely new syntax.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mailman.j3-fortran.org/pipermail/j3/attachments/20210902/573e4391/attachment-0001.htm>
More information about the J3
mailing list