[Discussion] Breaking precedence


(Anton Zhilin) #1

Disclaimer: I have not (yet) prepared a proposal, or even something that
could be considered a draft, but I want to hear public opinion on the topic.

SE-0077 (about precedence groups) has been successfully implemented for
Swift 3. (A thousand thanks to John McCall!) It suggests a model where we
can prohibit certain operators from standing next to each other.

This was intended to be the second part of that proposal. We now have to
think if we should drop some precedence relationships between standard
operators. Here are examples of parentheses dropping that can be ambiguous
to reader:

1/3 as Double // should we prohibit this?
1 | 2 ^ 3 // or this?
a && b | c // or this?

Should we break standard precedence hierarchy and if yes, how?
For those involved in the mentioned proposal, how exactly should standard
precedence groups relate?


(Xiaodi Wu) #2

Disclaimer: I have not (yet) prepared a proposal, or even something that
could be considered a draft, but I want to hear public opinion on the topic.

SE-0077 (about precedence groups) has been successfully implemented for
Swift 3. (A thousand thanks to John McCall!) It suggests a model where we
can prohibit certain operators from standing next to each other.

This was intended to be the second part of that proposal. We now have to
think if we should drop some precedence relationships between standard
operators. Here are examples of parentheses dropping that can be ambiguous
to reader:

1/3 as Double // should we prohibit this?

Why should we? I would absolutely want that to work exactly as it does now
(0.333333...).

1 | 2 ^ 3 // or this?

No. Both of those are bitwise operations. They are often used together.
They have a refined relative precedence in Swift that makes sense.

a && b | c // or this?

Now that's more interesting. Maybe?

Should we break standard precedence hierarchy and if yes, how?

···

On Mon, Aug 1, 2016 at 4:41 PM, Anton Zhilin via swift-evolution < swift-evolution@swift.org> wrote:

For those involved in the mentioned proposal, how exactly should standard
precedence groups relate?

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Félix Cloutier) #3

I disagree. The binary operators have properties that are comparable to arithmetic operators, and their precedence is easy to define as such. & has multiplication-like properties (0*0=0, 0*1=0, 1*0=0, 1*1=1); | has addition-like properties (0+0=0, 0+1=1, 1+0=1, 1+1=2); ^ has subtraction-like properties (0-0=0, 0-1=-1, 1-0=1, 1-1=0), and their precedences are set accordingly (& is multiplicative, | and ^ are additive).

The same applies to && and ||. Bit shifts are exponentiative.

Binary operators get especially confusing in some languages because their precedence is lower than comparison operators. For instance, in C, `a & b == c` gets parsed as `a & (b == c)`. In Swift, the precedence of binary operators is above that of comparison operators, so we don't have that problem.

Félix

···

Le 1 août 2016 à 20:48:21, Rob Mayoff via swift-evolution <swift-evolution@swift.org> a écrit :

1 | 2 ^ 3 // or this?

No. Both of those are bitwise operations. They are often used together. They
have a refined relative precedence in Swift that makes sense.

I have no idea what the relative precedence of those operators is in
Swift, C, or any other language, and thinking about it now, no
relative precedence seems sensible or obvious to me. Those operators
(and bitwise-&) are excellent examples of operators that should
require parentheses.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Rob Mayoff) #4

1 | 2 ^ 3 // or this?

No. Both of those are bitwise operations. They are often used together. They
have a refined relative precedence in Swift that makes sense.

I have no idea what the relative precedence of those operators is in
Swift, C, or any other language, and thinking about it now, no
relative precedence seems sensible or obvious to me. Those operators
(and bitwise-&) are excellent examples of operators that should
require parentheses.


(Anton Zhilin) #5

Strange enough, I've just run it and yes, 0.333333... But casting
precedence is lower than multiplicative, so I thought it would parse as
'(1/3) as Double'. Is it a bug?

···

2016-08-02 0:52 GMT+03:00 Xiaodi Wu <xiaodi.wu@gmail.com>:

On Mon, Aug 1, 2016 at 4:41 PM, Anton Zhilin via swift-evolution < > swift-evolution@swift.org> wrote:

Disclaimer: I have not (yet) prepared a proposal, or even something that
could be considered a draft, but I want to hear public opinion on the topic.

SE-0077 (about precedence groups) has been successfully implemented for
Swift 3. (A thousand thanks to John McCall!) It suggests a model where we
can prohibit certain operators from standing next to each other.

This was intended to be the second part of that proposal. We now have to
think if we should drop some precedence relationships between standard
operators. Here are examples of parentheses dropping that can be ambiguous
to reader:

1/3 as Double // should we prohibit this?

Why should we? I would absolutely want that to work exactly as it does now
(0.333333...).


(Xiaodi Wu) #6

Right. I asked about this issue some months ago on this list, and Chris was
very patient to confirm that what you described was indeed the thinking
behind the rationalized precedence of bitwise operators in Swift.

···

On Mon, Aug 1, 2016 at 23:18 Félix Cloutier <swift-evolution@swift.org> wrote:

I disagree. The binary operators have properties that are comparable to
arithmetic operators, and their precedence is easy to define as such. & has
multiplication-like properties (0*0=0, 0*1=0, 1*0=0, 1*1=1); | has
addition-like properties (0+0=0, 0+1=1, 1+0=1, 1+1=2); ^ has
subtraction-like properties (0-0=0, 0-1=-1, 1-0=1, 1-1=0), and their
precedences are set accordingly (& is multiplicative, | and ^ are additive).

The same applies to && and ||. Bit shifts are exponentiative.

Binary operators get especially confusing in some languages because their
precedence is lower than comparison operators. For instance, in C, `a & b
== c` gets parsed as `a & (b == c)`. In Swift, the precedence of binary
operators is above that of comparison operators, so we don't have that
problem.

Félix

Le 1 août 2016 à 20:48:21, Rob Mayoff via swift-evolution < > swift-evolution@swift.org> a écrit :

1 | 2 ^ 3 // or this?

No. Both of those are bitwise operations. They are often used together.
They
have a refined relative precedence in Swift that makes sense.

I have no idea what the relative precedence of those operators is in
Swift, C, or any other language, and thinking about it now, no
relative precedence seems sensible or obvious to me. Those operators
(and bitwise-&) are excellent examples of operators that should
require parentheses.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Stephen Canon) #7

<extreme pedantry>
`^` is actually the *addition* operator on Boolean rings[1]. `x | y` corresponds to the Boolean ring operation `x + y + xy`, which is definitely “addition-like” but isn’t addition.
</extreme pedantry>

Otherwise, spot on.

– Steve

[1] Of course, it’s *also* the subtraction operator, because `x = -x` for all members `x` of a Boolean ring (https://en.wikipedia.org/wiki/Boolean_ring), but one usually calls it “addition".

···

On Aug 2, 2016, at 12:18 AM, Félix Cloutier via swift-evolution <swift-evolution@swift.org> wrote:

I disagree. The binary operators have properties that are comparable to arithmetic operators, and their precedence is easy to define as such. & has multiplication-like properties (0*0=0, 0*1=0, 1*0=0, 1*1=1); | has addition-like properties (0+0=0, 0+1=1, 1+0=1, 1+1=2); ^ has subtraction-like properties (0-0=0, 0-1=-1, 1-0=1, 1-1=0), and their precedences are set accordingly (& is multiplicative, | and ^ are additive).


(Anton Zhilin) #8

I believe that such way of thinking is non-intuitive. In C, bitwise
operators are not intervened by any others, except for comparison operators
(agreed, it was a mistake). We now have possibilities to do so in Swift,
even better. I suggest to branch off right before AdditionPrecedence:

RangeFormation < Addition < Multiplication
RangeFormation < BitwiseOr < BitwiseAnd < LogicalShift

Another concern is NilCoalescing, which can be though to be semantically
similar to Ternary. And at the same time it looks like || and &&, which
would bring it between LogicalConjunction and Comparison.
Also, do Casting and RangeFormation stand where they should?

Next, Ternary operator is unique. Noone would ever like to put operators in
this precedence group, because it would be confusing. Why not simplify our
model and say that ?: has lower precedence than all binary operators,
including Assignment? Unary > binary > ternary, sounds good?

···

2016-08-02 7:18 GMT+03:00 Félix Cloutier <swift-evolution@swift.org>:

I disagree. The binary operators have properties that are comparable to
arithmetic operators, and their precedence is easy to define as such. & has
multiplication-like properties (0*0=0, 0*1=0, 1*0=0, 1*1=1); | has
addition-like properties (0+0=0, 0+1=1, 1+0=1, 1+1=2); ^ has
subtraction-like properties (0-0=0, 0-1=-1, 1-0=1, 1-1=0), and their
precedences are set accordingly (& is multiplicative, | and ^ are additive).

The same applies to && and ||. Bit shifts are exponentiative.


(John McCall) #9

You're applying C-style thinking. Swift does not have implicit promotions. Coercing the result of 1/3 to Double forces the operands to be Doubles as well.

John.

···

On Aug 1, 2016, at 3:01 PM, Anton Zhilin via swift-evolution <swift-evolution@swift.org> wrote:
2016-08-02 0:52 GMT+03:00 Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>>:
On Mon, Aug 1, 2016 at 4:41 PM, Anton Zhilin via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Disclaimer: I have not (yet) prepared a proposal, or even something that could be considered a draft, but I want to hear public opinion on the topic.

SE-0077 (about precedence groups) has been successfully implemented for Swift 3. (A thousand thanks to John McCall!) It suggests a model where we can prohibit certain operators from standing next to each other.

This was intended to be the second part of that proposal. We now have to think if we should drop some precedence relationships between standard operators. Here are examples of parentheses dropping that can be ambiguous to reader:

1/3 as Double // should we prohibit this?

Why should we? I would absolutely want that to work exactly as it does now (0.333333...).

Strange enough, I've just run it and yes, 0.333333... But casting precedence is lower than multiplicative, so I thought it would parse as '(1/3) as Double'. Is it a bug?


(Lily Ballard) #10

Not a bug. `(1 as Int/3) as Double` is a type error, because you can't
cast Int to Double that way, so in order to resolve the types in this
expression the 1 and 3 end up inferred as Doubles as that's the only way
to make the output compatible with `as Double`.

In other words, with this example, the precedence doesn't
actually matter.

-Kevin

···

On Mon, Aug 1, 2016, at 03:01 PM, Anton Zhilin via swift-evolution wrote:

2016-08-02 0:52 GMT+03:00 Xiaodi Wu <xiaodi.wu@gmail.com>:

On Mon, Aug 1, 2016 at 4:41 PM, Anton Zhilin via swift-evolution <swift- >> evolution@swift.org> wrote:

Disclaimer: I have not (yet) prepared a proposal, or even something
that could be considered a draft, but I want to hear public opinion
on the topic.

SE-0077 (about precedence groups) has been successfully implemented
for Swift 3. (A thousand thanks to John McCall!) It suggests a model
where we can prohibit certain operators from standing next to each
other.

This was intended to be the second part of that proposal. We now
have to think if we should drop some precedence relationships
between standard operators. Here are examples of parentheses
dropping that can be ambiguous to reader:

1/3 as Double // should we prohibit this?

Why should we? I would absolutely want that to work exactly as it
does now (0.333333...).

Strange enough, I've just run it and yes, 0.333333... But casting
precedence is lower than multiplicative, so I thought it would parse
as '(1/3) as Double'. Is it a bug?
_________________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Félix Cloutier) #11

I don't think that "intuitive" or "non-intuitive" is what you'd be looking for. There is nothing intuitive about multiplications having a higher precedence than additions; it's just a matter of conventions. I'm not a maths expert (as Stephen showed, I didn't even give the right explanation to binary operators!), but it seems to me that there could well be a parallel universe in which additions have precedence over multiplications without other serious implications.

And as it happens, a majority of people don't know that there is one for binary operators. I believe that the right question should be: do we want to pretend that this convention doesn't exist, to the benefit of people who don't know about it, and the detriment of those who do? Also, do we want to break it for && and || too?

I think that the biggest use case for binary operators in other languages are flags, and in Swift we treat these as collections. I'd venture that &, | and ^ would show up about as frequently as UnsafePointers and the like. It seems to me that Swift's approach has been to make things easy by default without locking away the power tools, and my personal expectation is that if you have to write code that has binary operators despite everything else that Swift has for you, you can be bothered to learn a precedence rule.

That said, one thing that I could definitely get behind is breaking precedence between binary operators and arithmetic operators. I don't think that it makes sense to write something like "a & b / c". Looking at my code, the only place where I needed to mix binary operators and arithmetic operators were `a & (a - 1)` (results in 0 if a is a power of two), and that one needs parentheses anyway.

Félix

···

Le 2 août 2016 à 02:29:41, Anton Zhilin <antonyzhilin@gmail.com> a écrit :

2016-08-02 7:18 GMT+03:00 Félix Cloutier <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:
I disagree. The binary operators have properties that are comparable to arithmetic operators, and their precedence is easy to define as such. & has multiplication-like properties (0*0=0, 0*1=0, 1*0=0, 1*1=1); | has addition-like properties (0+0=0, 0+1=1, 1+0=1, 1+1=2); ^ has subtraction-like properties (0-0=0, 0-1=-1, 1-0=1, 1-1=0), and their precedences are set accordingly (& is multiplicative, | and ^ are additive).

The same applies to && and ||. Bit shifts are exponentiative.

I believe that such way of thinking is non-intuitive. In C, bitwise operators are not intervened by any others, except for comparison operators (agreed, it was a mistake). We now have possibilities to do so in Swift, even better. I suggest to branch off right before AdditionPrecedence:

RangeFormation < Addition < Multiplication
RangeFormation < BitwiseOr < BitwiseAnd < LogicalShift

Another concern is NilCoalescing, which can be though to be semantically similar to Ternary. And at the same time it looks like || and &&, which would bring it between LogicalConjunction and Comparison.
Also, do Casting and RangeFormation stand where they should?

Next, Ternary operator is unique. Noone would ever like to put operators in this precedence group, because it would be confusing. Why not simplify our model and say that ?: has lower precedence than all binary operators, including Assignment? Unary > binary > ternary, sounds good?


(Félix Cloutier) #12

These expressions mix two types of logic that have different implications. For instance, `a * 16` and `a << 4` are "mostly equivalent", except that `a * 16` will crash on overflow. In these cases, I find that grouping provides some visual insulation that groups off the somewhat subtle differences.

Félix

···

Le 2 août 2016 à 08:49:07, Xiaodi Wu <xiaodi.wu@gmail.com> a écrit :

On Tue, Aug 2, 2016 at 10:41 AM, Félix Cloutier <felixcca@yahoo.ca <mailto:felixcca@yahoo.ca>> wrote:
I don't think that "intuitive" or "non-intuitive" is what you'd be looking for. There is nothing intuitive about multiplications having a higher precedence than additions; it's just a matter of conventions. I'm not a maths expert (as Stephen showed, I didn't even give the right explanation to binary operators!), but it seems to me that there could well be a parallel universe in which additions have precedence over multiplications without other serious implications.

And as it happens, a majority of people don't know that there is one for binary operators. I believe that the right question should be: do we want to pretend that this convention doesn't exist, to the benefit of people who don't know about it, and the detriment of those who do? Also, do we want to break it for && and || too?

I think that the biggest use case for binary operators in other languages are flags, and in Swift we treat these as collections. I'd venture that &, | and ^ would show up about as frequently as UnsafePointers and the like. It seems to me that Swift's approach has been to make things easy by default without locking away the power tools, and my personal expectation is that if you have to write code that has binary operators despite everything else that Swift has for you, you can be bothered to learn a precedence rule.

That said, one thing that I could definitely get behind is breaking precedence between binary operators and arithmetic operators. I don't think that it makes sense to write something like "a & b / c". Looking at my code, the only place where I needed to mix binary operators and arithmetic operators were `a & (a - 1)` (results in 0 if a is a power of two), and that one needs parentheses anyway.

Although here, your same argument applies. If you need to write `a & b / c`, then you can be bothered either to learn or look up a table, or you can just put in the parenthesis yourself. Likewise, if you're a reader of the code, it's highly likely that this is a complex formula anyway; you can either know the relative precedence or look it up, but that's the *least* of your worries in terms of what it will take to understand that code. I see no reason to force parentheses unless it actually prevents user error.

Félix

Le 2 août 2016 à 02:29:41, Anton Zhilin <antonyzhilin@gmail.com <mailto:antonyzhilin@gmail.com>> a écrit :

2016-08-02 7:18 GMT+03:00 Félix Cloutier <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:
I disagree. The binary operators have properties that are comparable to arithmetic operators, and their precedence is easy to define as such. & has multiplication-like properties (0*0=0, 0*1=0, 1*0=0, 1*1=1); | has addition-like properties (0+0=0, 0+1=1, 1+0=1, 1+1=2); ^ has subtraction-like properties (0-0=0, 0-1=-1, 1-0=1, 1-1=0), and their precedences are set accordingly (& is multiplicative, | and ^ are additive).

The same applies to && and ||. Bit shifts are exponentiative.

I believe that such way of thinking is non-intuitive. In C, bitwise operators are not intervened by any others, except for comparison operators (agreed, it was a mistake). We now have possibilities to do so in Swift, even better. I suggest to branch off right before AdditionPrecedence:

RangeFormation < Addition < Multiplication
RangeFormation < BitwiseOr < BitwiseAnd < LogicalShift

Another concern is NilCoalescing, which can be though to be semantically similar to Ternary. And at the same time it looks like || and &&, which would bring it between LogicalConjunction and Comparison.
Also, do Casting and RangeFormation stand where they should?

Next, Ternary operator is unique. Noone would ever like to put operators in this precedence group, because it would be confusing. Why not simplify our model and say that ?: has lower precedence than all binary operators, including Assignment? Unary > binary > ternary, sounds good?


(Ross O'Brien) #13

Assignment has the lowest precedence. 'x = condition ? y : z' doesn't make
sense if assignment is resolved before the ternary; the compiler would
complain that perhaps you meant '==' instead of '='.

There was an idea ages ago for essentially a 'switch expression' which was
effectively ternary for cases; if we ever make that a reality, it should
probably have the same precedence as ternary. But otherwise it's probably
going to continue to be a group of one.

···

On Tue, Aug 2, 2016 at 10:29 AM, Anton Zhilin via swift-evolution < swift-evolution@swift.org> wrote:

2016-08-02 7:18 GMT+03:00 Félix Cloutier <swift-evolution@swift.org>:

I disagree. The binary operators have properties that are comparable to
arithmetic operators, and their precedence is easy to define as such. & has
multiplication-like properties (0*0=0, 0*1=0, 1*0=0, 1*1=1); | has
addition-like properties (0+0=0, 0+1=1, 1+0=1, 1+1=2); ^ has
subtraction-like properties (0-0=0, 0-1=-1, 1-0=1, 1-1=0), and their
precedences are set accordingly (& is multiplicative, | and ^ are additive).

The same applies to && and ||. Bit shifts are exponentiative.

I believe that such way of thinking is non-intuitive. In C, bitwise
operators are not intervened by any others, except for comparison operators
(agreed, it was a mistake). We now have possibilities to do so in Swift,
even better. I suggest to branch off right before AdditionPrecedence:

RangeFormation < Addition < Multiplication
RangeFormation < BitwiseOr < BitwiseAnd < LogicalShift

Another concern is NilCoalescing, which can be though to be semantically
similar to Ternary. And at the same time it looks like || and &&, which
would bring it between LogicalConjunction and Comparison.
Also, do Casting and RangeFormation stand where they should?

Next, Ternary operator is unique. Noone would ever like to put operators
in this precedence group, because it would be confusing. Why not simplify
our model and say that ?: has lower precedence than all binary operators,
including Assignment? Unary > binary > ternary, sounds good?

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Xiaodi Wu) #14

I don't think that "intuitive" or "non-intuitive" is what you'd be looking
for. There is nothing intuitive about multiplications having a higher
precedence than additions; it's just a matter of conventions. I'm not a
maths expert (as Stephen showed, I didn't even give the right explanation
to binary operators!), but it seems to me that there could well be a
parallel universe in which additions have precedence over multiplications
without other serious implications.

And as it happens, a majority of people don't know that there is one for
binary operators. I believe that the right question should be: do we want
to pretend that this convention doesn't exist, to the benefit of people who
don't know about it, and the detriment of those who do? Also, do we want to
break it for && and || too?

I think that the biggest use case for binary operators in other languages
are flags, and in Swift we treat these as collections. I'd venture that &,
> and ^ would show up about as frequently as UnsafePointers and the like.
It seems to me that Swift's approach has been to make things easy by
default without locking away the power tools, and my personal expectation
is that if you have to write code that has binary operators despite
everything else that Swift has for you, you can be bothered to learn a
precedence rule.

That said, one thing that I could definitely get behind is breaking
precedence between binary operators and arithmetic operators. I don't think
that it makes sense to write something like "a & b / c". Looking at my
code, the only place where I needed to mix binary operators and arithmetic
operators were `a & (a - 1)` (results in 0 if a is a power of two), and
that one needs parentheses anyway.

Although here, your same argument applies. If you need to write `a & b /
c`, then you can be bothered either to learn or look up a table, or you can
just put in the parenthesis yourself. Likewise, if you're a reader of the
code, it's highly likely that this is a complex formula anyway; you can
either know the relative precedence or look it up, but that's the *least*
of your worries in terms of what it will take to understand that code. I
see no reason to force parentheses unless it actually prevents user error.

···

On Tue, Aug 2, 2016 at 10:41 AM, Félix Cloutier <felixcca@yahoo.ca> wrote:

Félix

Le 2 août 2016 à 02:29:41, Anton Zhilin <antonyzhilin@gmail.com> a écrit :

2016-08-02 7:18 GMT+03:00 Félix Cloutier <swift-evolution@swift.org>:

I disagree. The binary operators have properties that are comparable to
arithmetic operators, and their precedence is easy to define as such. & has
multiplication-like properties (0*0=0, 0*1=0, 1*0=0, 1*1=1); | has
addition-like properties (0+0=0, 0+1=1, 1+0=1, 1+1=2); ^ has
subtraction-like properties (0-0=0, 0-1=-1, 1-0=1, 1-1=0), and their
precedences are set accordingly (& is multiplicative, | and ^ are additive).

The same applies to && and ||. Bit shifts are exponentiative.

I believe that such way of thinking is non-intuitive. In C, bitwise
operators are not intervened by any others, except for comparison operators
(agreed, it was a mistake). We now have possibilities to do so in Swift,
even better. I suggest to branch off right before AdditionPrecedence:

RangeFormation < Addition < Multiplication
RangeFormation < BitwiseOr < BitwiseAnd < LogicalShift

Another concern is NilCoalescing, which can be though to be semantically
similar to Ternary. And at the same time it looks like || and &&, which
would bring it between LogicalConjunction and Comparison.
Also, do Casting and RangeFormation stand where they should?

Next, Ternary operator is unique. Noone would ever like to put operators
in this precedence group, because it would be confusing. Why not simplify
our model and say that ?: has lower precedence than all binary operators,
including Assignment? Unary > binary > ternary, sounds good?


(Xiaodi Wu) #15

What's your evidence that it's non-intuitive? I literally use << for powers
of 2, as I'm sure do many others. As in, 1 << 3 == 8. Do you have a better
way of computing, say, 2^53?

···

On Tue, Aug 2, 2016 at 04:29 Anton Zhilin <antonyzhilin@gmail.com> wrote:

2016-08-02 7:18 GMT+03:00 Félix Cloutier <swift-evolution@swift.org>:

I disagree. The binary operators have properties that are comparable to
arithmetic operators, and their precedence is easy to define as such. & has
multiplication-like properties (0*0=0, 0*1=0, 1*0=0, 1*1=1); | has
addition-like properties (0+0=0, 0+1=1, 1+0=1, 1+1=2); ^ has
subtraction-like properties (0-0=0, 0-1=-1, 1-0=1, 1-1=0), and their
precedences are set accordingly (& is multiplicative, | and ^ are additive).

The same applies to && and ||. Bit shifts are exponentiative.

I believe that such way of thinking is non-intuitive. In C, bitwise
operators are not intervened by any others, except for comparison operators
(agreed, it was a mistake). We now have possibilities to do so in Swift,
even better. I suggest to branch off right before AdditionPrecedence:

RangeFormation < Addition < Multiplication
RangeFormation < BitwiseOr < BitwiseAnd < LogicalShift

Another concern is NilCoalescing, which can be though to be semantically
similar to Ternary. And at the same time it looks like || and &&, which
would bring it between LogicalConjunction and Comparison.
Also, do Casting and RangeFormation stand where they should?

Next, Ternary operator is unique. Noone would ever like to put operators
in this precedence group, because it would be confusing. Why not simplify
our model and say that ?: has lower precedence than all binary operators,
including Assignment? Unary > binary > ternary, sounds good?


(Xiaodi Wu) #16

That's an excellent point, actually. Would there be downsides not yet
considered?

···

On Tue, Aug 2, 2016 at 11:03 Félix Cloutier <felixcca@yahoo.ca> wrote:

These expressions mix two types of logic that have different implications.
For instance, `a * 16` and `a << 4` are "mostly equivalent", except that `a
* 16` will crash on overflow. In these cases, I find that grouping provides
some visual insulation that groups off the somewhat subtle differences.

Félix

Le 2 août 2016 à 08:49:07, Xiaodi Wu <xiaodi.wu@gmail.com> a écrit :

On Tue, Aug 2, 2016 at 10:41 AM, Félix Cloutier <felixcca@yahoo.ca> wrote:

I don't think that "intuitive" or "non-intuitive" is what you'd be
looking for. There is nothing intuitive about multiplications having a
higher precedence than additions; it's just a matter of conventions. I'm
not a maths expert (as Stephen showed, I didn't even give the right
explanation to binary operators!), but it seems to me that there could well
be a parallel universe in which additions have precedence over
multiplications without other serious implications.

And as it happens, a majority of people don't know that there is one for
binary operators. I believe that the right question should be: do we want
to pretend that this convention doesn't exist, to the benefit of people who
don't know about it, and the detriment of those who do? Also, do we want to
break it for && and || too?

I think that the biggest use case for binary operators in other languages
are flags, and in Swift we treat these as collections. I'd venture that &,
> and ^ would show up about as frequently as UnsafePointers and the like.
It seems to me that Swift's approach has been to make things easy by
default without locking away the power tools, and my personal expectation
is that if you have to write code that has binary operators despite
everything else that Swift has for you, you can be bothered to learn a
precedence rule.

That said, one thing that I could definitely get behind is breaking
precedence between binary operators and arithmetic operators. I don't think
that it makes sense to write something like "a & b / c". Looking at my
code, the only place where I needed to mix binary operators and arithmetic
operators were `a & (a - 1)` (results in 0 if a is a power of two), and
that one needs parentheses anyway.

Although here, your same argument applies. If you need to write `a & b /
c`, then you can be bothered either to learn or look up a table, or you can
just put in the parenthesis yourself. Likewise, if you're a reader of the
code, it's highly likely that this is a complex formula anyway; you can
either know the relative precedence or look it up, but that's the *least*
of your worries in terms of what it will take to understand that code. I
see no reason to force parentheses unless it actually prevents user error.

Félix

Le 2 août 2016 à 02:29:41, Anton Zhilin <antonyzhilin@gmail.com> a écrit
:

2016-08-02 7:18 GMT+03:00 Félix Cloutier <swift-evolution@swift.org>:

I disagree. The binary operators have properties that are comparable to
arithmetic operators, and their precedence is easy to define as such. & has
multiplication-like properties (0*0=0, 0*1=0, 1*0=0, 1*1=1); | has
addition-like properties (0+0=0, 0+1=1, 1+0=1, 1+1=2); ^ has
subtraction-like properties (0-0=0, 0-1=-1, 1-0=1, 1-1=0), and their
precedences are set accordingly (& is multiplicative, | and ^ are additive).

The same applies to && and ||. Bit shifts are exponentiative.

I believe that such way of thinking is non-intuitive. In C, bitwise
operators are not intervened by any others, except for comparison operators
(agreed, it was a mistake). We now have possibilities to do so in Swift,
even better. I suggest to branch off right before AdditionPrecedence:

RangeFormation < Addition < Multiplication
RangeFormation < BitwiseOr < BitwiseAnd < LogicalShift

Another concern is NilCoalescing, which can be though to be semantically
similar to Ternary. And at the same time it looks like || and &&, which
would bring it between LogicalConjunction and Comparison.
Also, do Casting and RangeFormation stand where they should?

Next, Ternary operator is unique. Noone would ever like to put operators
in this precedence group, because it would be confusing. Why not simplify
our model and say that ?: has lower precedence than all binary operators,
including Assignment? Unary > binary > ternary, sounds good?


(Daniel Duan) #17

Speaking for myself, I will *never* remember which of `&&` and `||` has higher precedence.

I had the opposite experience. The point here is don’t rob others for our own conveniences, which are definitionally subjective.

···

On Aug 2, 2016, at 10:30 AM, Nevin Brackett-Rozinsky via swift-evolution <swift-evolution@swift.org> wrote:

I think of them as peers, so I always use parentheses around them, and whenever I read code that mingles them without parentheses its meaning is *unclear* to me.

One of Swift’s main goals is clarity at the point of use. After all, code is read far more often than it is written. To me, an expression like `a && b || c && d` is not clear when I read it.

The same goes for bitwise operators: I view them as peers. I do not think of them as “additive” or “multiplicative” (and definitely not “subtractive”), so code that relies on their precedences will always send me scrambling to look up which comes first.

Certainly something like `a + b | c & d - e * f ^ g` is meaningless to me without parentheses.

Nevin

On Tue, Aug 2, 2016 at 12:08 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
That's an excellent point, actually. Would there be downsides not yet considered?

On Tue, Aug 2, 2016 at 11:03 Félix Cloutier <felixcca@yahoo.ca <mailto:felixcca@yahoo.ca>> wrote:
These expressions mix two types of logic that have different implications. For instance, `a * 16` and `a << 4` are "mostly equivalent", except that `a * 16` will crash on overflow. In these cases, I find that grouping provides some visual insulation that groups off the somewhat subtle differences.

Félix

Le 2 août 2016 à 08:49:07, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> a écrit :

On Tue, Aug 2, 2016 at 10:41 AM, Félix Cloutier <felixcca@yahoo.ca <mailto:felixcca@yahoo.ca>> wrote:
I don't think that "intuitive" or "non-intuitive" is what you'd be looking for. There is nothing intuitive about multiplications having a higher precedence than additions; it's just a matter of conventions. I'm not a maths expert (as Stephen showed, I didn't even give the right explanation to binary operators!), but it seems to me that there could well be a parallel universe in which additions have precedence over multiplications without other serious implications.

And as it happens, a majority of people don't know that there is one for binary operators. I believe that the right question should be: do we want to pretend that this convention doesn't exist, to the benefit of people who don't know about it, and the detriment of those who do? Also, do we want to break it for && and || too?

I think that the biggest use case for binary operators in other languages are flags, and in Swift we treat these as collections. I'd venture that &, | and ^ would show up about as frequently as UnsafePointers and the like. It seems to me that Swift's approach has been to make things easy by default without locking away the power tools, and my personal expectation is that if you have to write code that has binary operators despite everything else that Swift has for you, you can be bothered to learn a precedence rule.

That said, one thing that I could definitely get behind is breaking precedence between binary operators and arithmetic operators. I don't think that it makes sense to write something like "a & b / c". Looking at my code, the only place where I needed to mix binary operators and arithmetic operators were `a & (a - 1)` (results in 0 if a is a power of two), and that one needs parentheses anyway.

Although here, your same argument applies. If you need to write `a & b / c`, then you can be bothered either to learn or look up a table, or you can just put in the parenthesis yourself. Likewise, if you're a reader of the code, it's highly likely that this is a complex formula anyway; you can either know the relative precedence or look it up, but that's the *least* of your worries in terms of what it will take to understand that code. I see no reason to force parentheses unless it actually prevents user error.

Félix

Le 2 août 2016 à 02:29:41, Anton Zhilin <antonyzhilin@gmail.com <mailto:antonyzhilin@gmail.com>> a écrit :

2016-08-02 7:18 GMT+03:00 Félix Cloutier <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:
I disagree. The binary operators have properties that are comparable to arithmetic operators, and their precedence is easy to define as such. & has multiplication-like properties (0*0=0, 0*1=0, 1*0=0, 1*1=1); | has addition-like properties (0+0=0, 0+1=1, 1+0=1, 1+1=2); ^ has subtraction-like properties (0-0=0, 0-1=-1, 1-0=1, 1-1=0), and their precedences are set accordingly (& is multiplicative, | and ^ are additive).

The same applies to && and ||. Bit shifts are exponentiative.

I believe that such way of thinking is non-intuitive. In C, bitwise operators are not intervened by any others, except for comparison operators (agreed, it was a mistake). We now have possibilities to do so in Swift, even better. I suggest to branch off right before AdditionPrecedence:

RangeFormation < Addition < Multiplication
RangeFormation < BitwiseOr < BitwiseAnd < LogicalShift

Another concern is NilCoalescing, which can be though to be semantically similar to Ternary. And at the same time it looks like || and &&, which would bring it between LogicalConjunction and Comparison.
Also, do Casting and RangeFormation stand where they should?

Next, Ternary operator is unique. Noone would ever like to put operators in this precedence group, because it would be confusing. Why not simplify our model and say that ?: has lower precedence than all binary operators, including Assignment? Unary > binary > ternary, sounds good?

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


#18

Speaking for myself, I will *never* remember which of `&&` and `||` has
higher precedence. I think of them as peers, so I always use parentheses
around them, and whenever I read code that mingles them without parentheses
its meaning is *unclear* to me.

One of Swift’s main goals is clarity at the point of use. After all, code
is read far more often than it is written. To me, an expression like `a &&
b || c && d` is not clear when I read it.

The same goes for bitwise operators: I view them as peers. I do not think
of them as “additive” or “multiplicative” (and definitely not
“subtractive”), so code that relies on their precedences will always send
me scrambling to look up which comes first.

Certainly something like `a + b | c & d - e * f ^ g` is meaningless to me
without parentheses.

Nevin

···

On Tue, Aug 2, 2016 at 12:08 PM, Xiaodi Wu via swift-evolution < swift-evolution@swift.org> wrote:

That's an excellent point, actually. Would there be downsides not yet
considered?

On Tue, Aug 2, 2016 at 11:03 Félix Cloutier <felixcca@yahoo.ca> wrote:

These expressions mix two types of logic that have different
implications. For instance, `a * 16` and `a << 4` are "mostly equivalent",
except that `a * 16` will crash on overflow. In these cases, I find that
grouping provides some visual insulation that groups off the somewhat
subtle differences.

Félix

Le 2 août 2016 à 08:49:07, Xiaodi Wu <xiaodi.wu@gmail.com> a écrit :

On Tue, Aug 2, 2016 at 10:41 AM, Félix Cloutier <felixcca@yahoo.ca> >> wrote:

I don't think that "intuitive" or "non-intuitive" is what you'd be
looking for. There is nothing intuitive about multiplications having a
higher precedence than additions; it's just a matter of conventions. I'm
not a maths expert (as Stephen showed, I didn't even give the right
explanation to binary operators!), but it seems to me that there could well
be a parallel universe in which additions have precedence over
multiplications without other serious implications.

And as it happens, a majority of people don't know that there is one for
binary operators. I believe that the right question should be: do we want
to pretend that this convention doesn't exist, to the benefit of people who
don't know about it, and the detriment of those who do? Also, do we want to
break it for && and || too?

I think that the biggest use case for binary operators in other
languages are flags, and in Swift we treat these as collections. I'd
venture that &, | and ^ would show up about as frequently as UnsafePointers
and the like. It seems to me that Swift's approach has been to make things
easy by default without locking away the power tools, and my personal
expectation is that if you have to write code that has binary operators
despite everything else that Swift has for you, you can be bothered to
learn a precedence rule.

That said, one thing that I could definitely get behind is breaking
precedence between binary operators and arithmetic operators. I don't think
that it makes sense to write something like "a & b / c". Looking at my
code, the only place where I needed to mix binary operators and arithmetic
operators were `a & (a - 1)` (results in 0 if a is a power of two), and
that one needs parentheses anyway.

Although here, your same argument applies. If you need to write `a & b /
c`, then you can be bothered either to learn or look up a table, or you can
just put in the parenthesis yourself. Likewise, if you're a reader of the
code, it's highly likely that this is a complex formula anyway; you can
either know the relative precedence or look it up, but that's the *least*
of your worries in terms of what it will take to understand that code. I
see no reason to force parentheses unless it actually prevents user error.

Félix

Le 2 août 2016 à 02:29:41, Anton Zhilin <antonyzhilin@gmail.com> a
écrit :

2016-08-02 7:18 GMT+03:00 Félix Cloutier <swift-evolution@swift.org>:

I disagree. The binary operators have properties that are comparable to
arithmetic operators, and their precedence is easy to define as such. & has
multiplication-like properties (0*0=0, 0*1=0, 1*0=0, 1*1=1); | has
addition-like properties (0+0=0, 0+1=1, 1+0=1, 1+1=2); ^ has
subtraction-like properties (0-0=0, 0-1=-1, 1-0=1, 1-1=0), and their
precedences are set accordingly (& is multiplicative, | and ^ are additive).

The same applies to && and ||. Bit shifts are exponentiative.

I believe that such way of thinking is non-intuitive. In C, bitwise
operators are not intervened by any others, except for comparison operators
(agreed, it was a mistake). We now have possibilities to do so in Swift,
even better. I suggest to branch off right before AdditionPrecedence:

RangeFormation < Addition < Multiplication
RangeFormation < BitwiseOr < BitwiseAnd < LogicalShift

Another concern is NilCoalescing, which can be though to be semantically
similar to Ternary. And at the same time it looks like || and &&, which
would bring it between LogicalConjunction and Comparison.
Also, do Casting and RangeFormation stand where they should?

Next, Ternary operator is unique. Noone would ever like to put operators
in this precedence group, because it would be confusing. Why not simplify
our model and say that ?: has lower precedence than all binary operators,
including Assignment? Unary > binary > ternary, sounds good?

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Xiaodi Wu) #19

Re: ternary operators--this goes far beyond the boundaries of the
discussion you yourself started. Removing certain relative precedences so
that parentheses are required is one thing; *changing* relative precedences
is quite another. For me at least, the latter is an absolute nonstarter.
Every existing usage of the operator could potentially *silently* break.

As to removing certain precedences--the current Swift precedences are
rationally defensible. Perhaps anything you propose would be too, but
trading one rational scheme for another (at best) is not justification
enough for a source-breaking change. Instead, show me a scenario in which
you write something today that could plausibly do something other than what
it might look like, where forcing parentheses would fix the issue--i.e.
convince me a change would eliminate actual user errors--and I'm on board.
So far, I haven't seen one (even the "maybe" scenario above, a && b | c,
wouldn't work in Swift unless you defined custom operator functions
yourself, so there's no real possibility of a silent error).

···

On Tue, Aug 2, 2016 at 08:31 Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

What's your evidence that it's non-intuitive? I literally use << for
powers of 2, as I'm sure do many others. As in, 1 << 3 == 8. Do you have a
better way of computing, say, 2^53?

On Tue, Aug 2, 2016 at 04:29 Anton Zhilin <antonyzhilin@gmail.com> wrote:

2016-08-02 7:18 GMT+03:00 Félix Cloutier <swift-evolution@swift.org>:

I disagree. The binary operators have properties that are comparable to
arithmetic operators, and their precedence is easy to define as such. & has
multiplication-like properties (0*0=0, 0*1=0, 1*0=0, 1*1=1); | has
addition-like properties (0+0=0, 0+1=1, 1+0=1, 1+1=2); ^ has
subtraction-like properties (0-0=0, 0-1=-1, 1-0=1, 1-1=0), and their
precedences are set accordingly (& is multiplicative, | and ^ are additive).

The same applies to && and ||. Bit shifts are exponentiative.

I believe that such way of thinking is non-intuitive. In C, bitwise
operators are not intervened by any others, except for comparison operators
(agreed, it was a mistake). We now have possibilities to do so in Swift,
even better. I suggest to branch off right before AdditionPrecedence:

RangeFormation < Addition < Multiplication
RangeFormation < BitwiseOr < BitwiseAnd < LogicalShift

Another concern is NilCoalescing, which can be though to be semantically
similar to Ternary. And at the same time it looks like || and &&, which
would bring it between LogicalConjunction and Comparison.
Also, do Casting and RangeFormation stand where they should?

Next, Ternary operator is unique. Noone would ever like to put operators
in this precedence group, because it would be confusing. Why not simplify
our model and say that ?: has lower precedence than all binary operators,
including Assignment? Unary > binary > ternary, sounds good?


(Xiaodi Wu) #20

This is an expansive argument you advance. Should users be expected to
learn *any* rules of precedence beyond those of basic arithmetic? It would
seem that you are arguing no. Yet Swift just went through an arduous
redesign to permit--nay, improve--exactly that.

···

On Tue, Aug 2, 2016 at 12:30 Nevin Brackett-Rozinsky < nevin.brackettrozinsky@gmail.com> wrote:

Speaking for myself, I will *never* remember which of `&&` and `||` has
higher precedence. I think of them as peers, so I always use parentheses
around them, and whenever I read code that mingles them without parentheses
its meaning is *unclear* to me.

One of Swift’s main goals is clarity at the point of use. After all, code
is read far more often than it is written. To me, an expression like `a &&
b || c && d` is not clear when I read it.

The same goes for bitwise operators: I view them as peers. I do not think
of them as “additive” or “multiplicative” (and definitely not
“subtractive”), so code that relies on their precedences will always send
me scrambling to look up which comes first.

Certainly something like `a + b | c & d - e * f ^ g` is meaningless to me
without parentheses.

Nevin

On Tue, Aug 2, 2016 at 12:08 PM, Xiaodi Wu via swift-evolution < > swift-evolution@swift.org> wrote:

That's an excellent point, actually. Would there be downsides not yet
considered?

On Tue, Aug 2, 2016 at 11:03 Félix Cloutier <felixcca@yahoo.ca> wrote:

These expressions mix two types of logic that have different
implications. For instance, `a * 16` and `a << 4` are "mostly equivalent",
except that `a * 16` will crash on overflow. In these cases, I find that
grouping provides some visual insulation that groups off the somewhat
subtle differences.

Félix

Le 2 août 2016 à 08:49:07, Xiaodi Wu <xiaodi.wu@gmail.com> a écrit :

On Tue, Aug 2, 2016 at 10:41 AM, Félix Cloutier <felixcca@yahoo.ca> >>> wrote:

I don't think that "intuitive" or "non-intuitive" is what you'd be
looking for. There is nothing intuitive about multiplications having a
higher precedence than additions; it's just a matter of conventions. I'm
not a maths expert (as Stephen showed, I didn't even give the right
explanation to binary operators!), but it seems to me that there could well
be a parallel universe in which additions have precedence over
multiplications without other serious implications.

And as it happens, a majority of people don't know that there is one
for binary operators. I believe that the right question should be: do we
want to pretend that this convention doesn't exist, to the benefit of
people who don't know about it, and the detriment of those who do? Also, do
we want to break it for && and || too?

I think that the biggest use case for binary operators in other
languages are flags, and in Swift we treat these as collections. I'd
venture that &, | and ^ would show up about as frequently as UnsafePointers
and the like. It seems to me that Swift's approach has been to make things
easy by default without locking away the power tools, and my personal
expectation is that if you have to write code that has binary operators
despite everything else that Swift has for you, you can be bothered to
learn a precedence rule.

That said, one thing that I could definitely get behind is breaking
precedence between binary operators and arithmetic operators. I don't think
that it makes sense to write something like "a & b / c". Looking at my
code, the only place where I needed to mix binary operators and arithmetic
operators were `a & (a - 1)` (results in 0 if a is a power of two), and
that one needs parentheses anyway.

Although here, your same argument applies. If you need to write `a & b /
c`, then you can be bothered either to learn or look up a table, or you can
just put in the parenthesis yourself. Likewise, if you're a reader of the
code, it's highly likely that this is a complex formula anyway; you can
either know the relative precedence or look it up, but that's the *least*
of your worries in terms of what it will take to understand that code. I
see no reason to force parentheses unless it actually prevents user error.

Félix

Le 2 août 2016 à 02:29:41, Anton Zhilin <antonyzhilin@gmail.com> a
écrit :

2016-08-02 7:18 GMT+03:00 Félix Cloutier <swift-evolution@swift.org>:

I disagree. The binary operators have properties that are comparable
to arithmetic operators, and their precedence is easy to define as such. &
has multiplication-like properties (0*0=0, 0*1=0, 1*0=0, 1*1=1); | has
addition-like properties (0+0=0, 0+1=1, 1+0=1, 1+1=2); ^ has
subtraction-like properties (0-0=0, 0-1=-1, 1-0=1, 1-1=0), and their
precedences are set accordingly (& is multiplicative, | and ^ are additive).

The same applies to && and ||. Bit shifts are exponentiative.

I believe that such way of thinking is non-intuitive. In C, bitwise
operators are not intervened by any others, except for comparison operators
(agreed, it was a mistake). We now have possibilities to do so in Swift,
even better. I suggest to branch off right before AdditionPrecedence:

RangeFormation < Addition < Multiplication
RangeFormation < BitwiseOr < BitwiseAnd < LogicalShift

Another concern is NilCoalescing, which can be though to be
semantically similar to Ternary. And at the same time it looks like || and
&&, which would bring it between LogicalConjunction and Comparison.
Also, do Casting and RangeFormation stand where they should?

Next, Ternary operator is unique. Noone would ever like to put
operators in this precedence group, because it would be confusing. Why not
simplify our model and say that ?: has lower precedence than all binary
operators, including Assignment? Unary > binary > ternary, sounds good?

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution