chapel-lang / chapel

a Productive Parallel Programming Language
https://chapel-lang.org
Other
1.8k stars 421 forks source link

Add max=, min= operators? #7629

Open bradcray opened 7 years ago

bradcray commented 7 years ago

This is reasonably sugary, but I find myself wanting them more often than you'd think: Should we support 'min=' and 'max=' operators to complement '+=', '*=', etc. My rationale is partially that 'a = max(a, b)' is a bit of a mouthful, partially that 'a max= b' could avoid a copy of 'a' in the event that it was the max, and mostly that it just "seems right" to me somehow (perhaps in part because most other reduction operators support an op= operator).

damianmoz commented 7 years ago

I never know whether max and min are functions or operators in Chapel. Also max and min are a bit long for operators. I find too much clutter before the equal sign distracting ................................. a max= b We already use 2 character operators with '=' like <<= and >>= so why not ................................. a >?= b Leave the names 'min and 'max' for functions. Also, I would like to revive the colon-equals ':=' for assignment which adds symmetry to the equal sign.

damianmoz commented 7 years ago

Just on that quickly, when I try and do ............................ x = y max z; where x, y and z are floats, it says that there is a syntax error. Are max and min operators in the next version because they are functions in 1.16.0.

bradcray commented 6 years ago

min and max are not currently infix operators in any version of Chapel. They are recognized options for use with the reduce operator (i.e., max reduce A). I wasn't going to go so far as to suggest that they be recognized operators (because, without some other change, that would require them to be reserved words, and I'm not keen on reserving those words), but I can see how that would make them seem more symmetrical. I was focused more on improving the symmetry between reductions and op= assignments than infix operators.

I think the downside of using >?= and '<?=' is that then there are two ways of saying max/min in different contexts. I think they also introduce a bit of a learning curve. We do support reduce= currently, which makes max= and min= look attractively brief. :)

I'm going to avoid getting sidetracked into a := discussion on this issue, but if you want to fork it to a new issue, please feel free.

damianmoz commented 6 years ago

I hope I am replying correctly.

On Tue, 28 Nov 2017, Brad Chamberlain wrote:

I wasn't going to go so far as to suggest that they be recognized operators

That's what I thought. Silly me. Must be because I am south of the equator!

(because, without some other change, that would require them to be reserved words, and I'm not keen on reserving those words), but I can see how that would make them seem more symmetrical.

I was thinking you were wanting to make them reserved words, in which case you need to think about 'rem' and 'div' and one or two others.

I was focused more on improving the symmetry between reductions and op= assignments than infix operators.

I misunderstood. Sorry.

In that case I totally agree with your concept of min= and max=.

Given what you said and my new understanding of where you are coming from, the rest of the conversation is moot. But I will reply.

I think the downside of using >?= and '<?=' is that then there are two ways of saying max/min in different contexts.

Yes, but it avoids the reserved word issues.

I think they also introduce a bit of a learning curve.

Chapel users are smart enough. Although as I mistunderstood what you were trying to say, maybe I need to exclude myself from that general statement!

Seriously, Chapel has far more complex concepts. '>?' and '<?' are a breeze compared to parallel programming concepts.

We do support reduce= currently, which makes max= and min= look attractively brief. :)

Agreed.

I'm going to avoid getting sidetracked into a := discussion on this issue, but if you want to fork it to a new issue, please feel free.

Don't worry. There are more important things in Chapel.

Regards - Damian

Pacific Engineering Systems International, 277-279 Broadway, Glebe NSW 2037 Ph:+61-2-8571-0847 .. Fx:+61-2-9692-9623 | unsolicited email not wanted here Views & opinions here are mine and not those of any past or present employer

bradcray commented 6 years ago

To clarify one point: My thinking in this proposal is that min= and max= would be tokens recognized by the compiler, but that plain-old min and max would not be (i.e., they would continue to be normal identifiers). This is similar to how += or reduce= are parsed—as single tokens rather than a + or reduce token followed by a = token.

nimitbhardwaj commented 6 years ago

This would be cool and nice way if there is like, a fun= b, means a = fun(a, b), then this will deal with min and max too, even perhaps it will be more useful in some applications, where fun is a function taking 2 arguments

bradcray commented 6 years ago

@nimitbhardwaj : I think that that's an intriguing idea and am thinking about what the implementation complexity would be. If we were to pursue this, I think it'd be crucial that the user would be able to implement a proc fun=() overload rather than relying on the fallback = + fun() implementation because some operations can be written more efficiently if it's known that a single variable serves in both the input and output roles.

nimitbhardwaj commented 6 years ago

@bradcray yes its better, operator overloading would be much better, as you say, sometimes it is efficient and fast to perform such operations, like a += b is more efficient than a = a + b, so I think functions will, yes this overload will be fine, and for the complexity of implementation of this type of overloading, I think it will not be so much difficult, because operator overloading is already there in language, so adding one more type will not be so difficult. Perhaps.

bradcray commented 6 years ago

I was originally imagining that this would be hard to parse, as I was imagining lexing foo= as two tokens: an identifier (TIDENT) like foo followed by the assignment operator = (TASSIGN) and I think that approach would be fraught with problems. But upon thinking about it more, I think the right way to approach this (were we to do so) would be to add a new token to the lexer, call it TIDENT_ASSIGN that was like an identifier followed by an = with no intervening whitespace. This operator could then be recognized by the parser in both the same context as other op= cases and in the proc definition rules. I think this is an intriguing idea.

nimitbhardwaj commented 6 years ago

yes, its better to give foo= a new token like you said TIDENT_ASSIGN, its regular expression is simple <IDENTIFIER>+=, and for case of parsing it will not pose a problem then may be.

bradcray commented 2 years ago

FWIW, I've found myself wanting this feature again frequently while working on Advent of Code 2021 programs.

damianmoz commented 2 years ago

If you define them as operators, doesn't that mean you get what you want and things fall out in the wash. That is the cleanest way. Please consider how Algol 68 handled the concept of op, a user defined operator, and see if there is any wisdom to be learned.

Note that your argument of avoiding the copy in the operation

x max= y

in these days of max and min are assembler instructions is moot or even a red herring.

I can live with them as reserved words but I am a very small piece of the this whole puzzle. Anybody who does not treat max and min as reserved words is looking for trouble.

Once I read the Algol68 report, I have treated max and min as reserved words anyway. And once ADA grabbed max and min for what it calls an attribute of a type, I knew my decision to treat them as reserved words in any programming language was right. Other languages commandeer/hijack those two abbreviations.

damianmoz commented 2 years ago

And yes, I believe that has issues for any existing code which does

param maxint = max(int(64));
param minint = min(int(64));

but they really should have been called maxval and minval as other languages have done.

param maxint = maxval(int(64));
param minint = minval(int(64));

Sorry, I should have called out that mistake earlier.

As max and min are now first class operators, their use in reduce statements should not be affected.

What have I forgotten to mention?????? I am sure my comment are incomplete.

damianmoz commented 2 years ago

For completeness, the symbols for a left ceiling and left floor, what *roff calls [lc] and [lf] or unicode U+2308 and U+230A respectively, have been used for the maximum and minimum operator for half a century. Not that I am proposing their use.

An argument can be made for compound operators involving < (lesser of) and > (greater of) . Prefixing both by a colon like :< and :> could be one such solution This avoids the problems raised by now having to treat the words min and max as reserved words but introduces others. Specifically, you get into the same arguments that APL and J and that style of programming language raises. Do we really want to get into that space?

damianmoz commented 2 years ago

For consistency with greater than (>) or less than (<), and at the risk of doing nasty things to English:

>     greater than
<     less(er) than

one probably should do

>:    greater of
<:    lesser of

or even

>|    greater of
<|    lesser of

which means that one can write

a >|= b;  /* instead of */  a = a >| b;
          /* or even .. */
x <|= y;  /* instead of */  x = x <| y;

          /* compared to */
a max= b;
          /* ....*/
x min= y;

My 2c.

damianmoz commented 2 years ago

The question is, how do we translate the max (>|) or min (<|) operator.

I was pleasantly surprised to find that CLANG provided the correct answer as per IEEE754.

Sadly, that would mean for cases where we know that no argument is a NaN, we loose the performance benefit of Intel intrinsics (because it has been broken for years), or RISC-V intrinsics (because they adopted Intel's approach 10 years after IEEE754 noted that this was broken for reasons best known to them). ARM provides both the broken instruction and the correct one from which the compiler can choose, both being fast.

Worth noting this when it comes up for consideration.

bradcray commented 2 years ago

(pulling a relevant comment over from https://github.com/chapel-lang/chapel/issues/18952#issuecomment-1009485602)

having max= an operator but not max is not orthogonal. I do not like non-orthogonality.

That's true, but the fact of the matter is that I find myself writing (and being annoyed at having to write) a = max(a, b); all the time making me wish I could just write a max= b—particularly when a is an expression like A[i].delta and b is a non-trivial expression. Yet I never find myself wanting to write c = max(a, b); using an infix operator like c = a max b; no matter what the expressions are. I also believe that sometimes non-orthogonality is the best way to get bang for your buck, particularly if the need is also non-orthogonal.

damianmoz commented 2 years ago

I would use (and have used) c = a max b in the past. It looks cleaner that c = max(a, b). But that is no reason to implement it in Chapel. I was not at that time sufficiently skilled with defining operators (or maybe I had no need) to do a = max(a, b).

bradcray commented 11 months ago

FWIW, I've found myself wanting this feature again frequently while working on Advent of Code 2021 programs.

And once again while working on Advent of Code 2023 programs.

damianmoz commented 11 months ago

Likewise. It makes sense in a lot of situations,