[Last second] Precedence of nil-coalescing operator seems too low


(Erica Sadun) #1

Given: `let x = Optional(3)` then
`let y = 5 + x ?? 2` will not compile
but
`let y = 5 + (x ?? 2)` will.

Should NilCoalescingPrecedence be raised? The current operator precedence chain is:

BitwiseShiftPrecedence > MultiplicationPrecedence > AdditionPrecedence > RangeFormationPrecedence > CastingPrecedence > NilCoalescingPrecedence > ComparisonPrecedence > LogicalConjunctionPrecedence > LogicalDisjunctionPrecedence > TernaryPrecedence > AssignmentPrecedence > FunctionArrowPrecedence > [nothing]

It seems to me that `NilCoalescingPrecedence` should probably be higher than `MultiplicationPrecedence` and possibly higher `BitwiseShiftPrecedence` as its job is to produce an unwrapped value that can then be operated upon.

I think CastingPrecedence should be even higher because
`expression as? T ?? fallback value`
should be parsed as
`(expression as? T) ?? (fallback value)`

I apologize profusely because I know this is beyond last minute,

-- E


(Jacob Bandes-Storch) #2

Perhaps-conversely, what should this code do?

    let nextIndex = foundIndex ?? lastIndex + 1

Jacob

···

On Sat, Sep 3, 2016 at 9:05 PM, Erica Sadun via swift-evolution < swift-evolution@swift.org> wrote:

Given: `let x = Optional(3)` then

`let y = 5 + x ?? 2` will not compile

but

`let y = 5 + (x ?? 2)` will.

Should NilCoalescingPrecedence be raised? The current operator precedence
chain is:

BitwiseShiftPrecedence > MultiplicationPrecedence > AdditionPrecedence >
RangeFormationPrecedence > CastingPrecedence > NilCoalescingPrecedence >
ComparisonPrecedence > LogicalConjunctionPrecedence >
LogicalDisjunctionPrecedence > TernaryPrecedence > AssignmentPrecedence >
FunctionArrowPrecedence > [nothing]

It seems to me that `NilCoalescingPrecedence` should probably be higher
than `MultiplicationPrecedence` and possibly higher `
BitwiseShiftPrecedence` as its job is to produce an unwrapped value that
can then be operated upon.

I think CastingPrecedence should be even higher because

`expression as? T ?? fallback value`

should be parsed as

`(expression as? T) ?? (fallback value)`

I apologize profusely because I know this is beyond last minute,

-- E

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


(Erica Sadun) #3

Perhaps-conversely, what should this code do?

    let nextIndex = foundIndex ?? lastIndex + 1

Jacob

It's a good counter example. And there's no optional-associative option.

-- E

···

On Sep 3, 2016, at 10:15 PM, Jacob Bandes-Storch <jtbandes@gmail.com> wrote:

On Sat, Sep 3, 2016 at 9:05 PM, Erica Sadun via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Given: `let x = Optional(3)` then
`let y = 5 + x ?? 2` will not compile
but
`let y = 5 + (x ?? 2)` will.

Should NilCoalescingPrecedence be raised? The current operator precedence chain is:

BitwiseShiftPrecedence > MultiplicationPrecedence > AdditionPrecedence > RangeFormationPrecedence > CastingPrecedence > NilCoalescingPrecedence > ComparisonPrecedence > LogicalConjunctionPrecedence > LogicalDisjunctionPrecedence > TernaryPrecedence > AssignmentPrecedence > FunctionArrowPrecedence > [nothing]

It seems to me that `NilCoalescingPrecedence` should probably be higher than `MultiplicationPrecedence` and possibly higher `BitwiseShiftPrecedence` as its job is to produce an unwrapped value that can then be operated upon.

I think CastingPrecedence should be even higher because
`expression as? T ?? fallback value`
should be parsed as
`(expression as? T) ?? (fallback value)`

I apologize profusely because I know this is beyond last minute,

-- E

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


(Jacob Bandes-Storch) #4

Now you've gotten me thinking about precedence of other operators too.

Since ?? is prone to causing confusion in either direction (cf. your
example and my example), it could be put in its own group whose relation to
the numeric operators is intentionally undefined (thus requiring parens).

I don't know about other folks, but I'll certainly get confused if & and |
and ^ are mixed. What if we removed their relation to each other (requiring
parens when mixing them)?

<img src=’/uploads/default/original/1X/1fbe0ed93116d6ac6bfbf2cb8fccfe9a8e7d3e34.png’ width=‘416’ height=‘500’>

For comparison (ha), here's what we have today:

<img src=’/uploads/default/original/1X/fd488949a5a700e891bd2c0f89d875670470beee.png’ width=‘583’ height=‘500’>

Jacob

···

On Sat, Sep 3, 2016 at 10:20 PM, Erica Sadun <erica@ericasadun.com> wrote:

On Sep 3, 2016, at 10:15 PM, Jacob Bandes-Storch <jtbandes@gmail.com> > wrote:

Perhaps-conversely, what should this code do?

    let nextIndex = foundIndex ?? lastIndex + 1

Jacob

It's a good counter example. And there's no optional-associative option.

-- E

On Sat, Sep 3, 2016 at 9:05 PM, Erica Sadun via swift-evolution < > swift-evolution@swift.org> wrote:

Given: `let x = Optional(3)` then

`let y = 5 + x ?? 2` will not compile

but

`let y = 5 + (x ?? 2)` will.

Should NilCoalescingPrecedence be raised? The current operator
precedence chain is:

BitwiseShiftPrecedence > MultiplicationPrecedence > AdditionPrecedence >
RangeFormationPrecedence > CastingPrecedence > NilCoalescingPrecedence >
ComparisonPrecedence > LogicalConjunctionPrecedence >
LogicalDisjunctionPrecedence > TernaryPrecedence > AssignmentPrecedence >
FunctionArrowPrecedence > [nothing]

It seems to me that `NilCoalescingPrecedence` should probably be higher
than `MultiplicationPrecedence` and possibly higher `
BitwiseShiftPrecedence` as its job is to produce an unwrapped value that
can then be operated upon.

I think CastingPrecedence should be even higher because

`expression as? T ?? fallback value`

should be parsed as

`(expression as? T) ?? (fallback value)`

I apologize profusely because I know this is beyond last minute,

-- E

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


(Erica Sadun) #5

At this point, I'm not sure whether this is an -evolution question or a -dev question. The latter would be much easier to work on at this time and could potentially be postponed to a dot release. I know that any conversation not directly related to 3.0 right now is a Bad Thing.

And I suspect that Steven C is probably the right person to know about wrangling precedence and existing standards.

-- E

···

On Sep 5, 2016, at 12:30 AM, Jacob Bandes-Storch <jtbandes@gmail.com> wrote:

Now you've gotten me thinking about precedence of other operators too.

Since ?? is prone to causing confusion in either direction (cf. your example and my example), it could be put in its own group whose relation to the numeric operators is intentionally undefined (thus requiring parens).

I don't know about other folks, but I'll certainly get confused if & and | and ^ are mixed. What if we removed their relation to each other (requiring parens when mixing them)?

<proposed-precedence.png>

For comparison (ha), here's what we have today:

<current-precedence.png>

Jacob

On Sat, Sep 3, 2016 at 10:20 PM, Erica Sadun <erica@ericasadun.com <mailto:erica@ericasadun.com>> wrote:

On Sep 3, 2016, at 10:15 PM, Jacob Bandes-Storch <jtbandes@gmail.com <mailto:jtbandes@gmail.com>> wrote:

Perhaps-conversely, what should this code do?

    let nextIndex = foundIndex ?? lastIndex + 1

Jacob

It's a good counter example. And there's no optional-associative option.

-- E

On Sat, Sep 3, 2016 at 9:05 PM, Erica Sadun via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Given: `let x = Optional(3)` then
`let y = 5 + x ?? 2` will not compile
but
`let y = 5 + (x ?? 2)` will.

Should NilCoalescingPrecedence be raised? The current operator precedence chain is:

BitwiseShiftPrecedence > MultiplicationPrecedence > AdditionPrecedence > RangeFormationPrecedence > CastingPrecedence > NilCoalescingPrecedence > ComparisonPrecedence > LogicalConjunctionPrecedence > LogicalDisjunctionPrecedence > TernaryPrecedence > AssignmentPrecedence > FunctionArrowPrecedence > [nothing]

It seems to me that `NilCoalescingPrecedence` should probably be higher than `MultiplicationPrecedence` and possibly higher `BitwiseShiftPrecedence` as its job is to produce an unwrapped value that can then be operated upon.

I think CastingPrecedence should be even higher because
`expression as? T ?? fallback value`
should be parsed as
`(expression as? T) ?? (fallback value)`

I apologize profusely because I know this is beyond last minute,

-- E

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


(Xiaodi Wu) #6

This suggestion has been pitched earlier and I've expressed my opinion in
those earlier threads, but I'll repeat myself here:

I'm hugely opposed to such changes to the precedence table. Those of us who
work with bitwise operators on a regular basis have memorized their
precedence in Swift (and other languages) and rely on such precedence to
write readable, correct code without excessively nested parentheses. Any
change here would break existing, carefully constructed code, punishing
those who *have* put in the effort to learn the precedence table. To any
other user of Swift, it should come as no surprise that operators *have*
precedence and associativity, and it is not such a burden for a user either
to memorize or to consult a table for these properties when they are unsure.

There is no way whatsoever to use intuition to arrive at the exact
precedence of `??`, or `as`, or `&&`, or `||`, or `&`, or `|`, or `^` or
`<<`, or `>>`, and there will be no relative precedence that will prove
intuitive to all. (That said, there is a rational basis for the relative
precedence of `&`, `|`, and `^` to each other.) If you believe this
situation to be problematic, then you must conclude that we should remove
relative precedence between any operators except perhaps the basic
arithmetic operators `+`, `-`, `*`, `/`. This line of reasoning would be a
huge U-turn from the direction of Swift, which after all just revised the
syntax with which custom operator precedence is defined. Such a feature
would be totally out of place in a language where operators other than
those for basic arithmetic are not supposed to have precedence relations
with each other.

(Of course, the relative precedence of arithmetic operators is in some ways
arbitrary as well, though it is inherited from math that everyone knows.
How did you learn it in the first place? You memorized it.)

···

On Mon, Sep 5, 2016 at 2:47 PM, Erica Sadun via swift-evolution < swift-evolution@swift.org> wrote:

At this point, I'm not sure whether this is an -evolution question or a
-dev question. The latter would be much easier to work on at this time and
could potentially be postponed to a dot release. I know that any
conversation not directly related to 3.0 right now is a Bad Thing.

And I suspect that Steven C is probably the right person to know about
wrangling precedence and existing standards.

-- E

On Sep 5, 2016, at 12:30 AM, Jacob Bandes-Storch <jtbandes@gmail.com> > wrote:

Now you've gotten me thinking about precedence of other operators too.

Since ?? is prone to causing confusion in either direction (cf. your
example and my example), it could be put in its own group whose relation to
the numeric operators is intentionally undefined (thus requiring parens).

I don't know about other folks, but I'll certainly get confused if & and |
and ^ are mixed. What if we removed their relation to each other (requiring
parens when mixing them)?

<proposed-precedence.png>

For comparison (ha), here's what we have today:

<current-precedence.png>

Jacob

On Sat, Sep 3, 2016 at 10:20 PM, Erica Sadun <erica@ericasadun.com> wrote:

On Sep 3, 2016, at 10:15 PM, Jacob Bandes-Storch <jtbandes@gmail.com> >> wrote:

Perhaps-conversely, what should this code do?

    let nextIndex = foundIndex ?? lastIndex + 1

Jacob

It's a good counter example. And there's no optional-associative option.

-- E

On Sat, Sep 3, 2016 at 9:05 PM, Erica Sadun via swift-evolution < >> swift-evolution@swift.org> wrote:

Given: `let x = Optional(3)` then

`let y = 5 + x ?? 2` will not compile

but

`let y = 5 + (x ?? 2)` will.

Should NilCoalescingPrecedence be raised? The current operator
precedence chain is:

BitwiseShiftPrecedence > MultiplicationPrecedence > AdditionPrecedence >
RangeFormationPrecedence > CastingPrecedence > NilCoalescingPrecedence >
ComparisonPrecedence > LogicalConjunctionPrecedence >
LogicalDisjunctionPrecedence > TernaryPrecedence > AssignmentPrecedence >
FunctionArrowPrecedence > [nothing]

It seems to me that `NilCoalescingPrecedence` should probably be higher
than `MultiplicationPrecedence` and possibly higher `
BitwiseShiftPrecedence` as its job is to produce an unwrapped value
that can then be operated upon.

I think CastingPrecedence should be even higher because

`expression as? T ?? fallback value`

should be parsed as

`(expression as? T) ?? (fallback value)`

I apologize profusely because I know this is beyond last minute,

-- E

_______________________________________________
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


(Xiaodi Wu) #7

Likely relevant to this discussion, Chris Lattner's comment in the last
thread about changing operator precedence (early August):

Some 2c to consider:

···

1) Swift 4 is being designed with the ability to all us to make “source
changes" that don’t break existing source code.
2) It is really important that we continue to polish the language to make
it great for the long term, so the spirit of your proposal is great to
explore.
3) That said, any source breaking change needs to be *very* strongly
motivated. To make this change, you should find evidence of common bugs
that would be solved by this, evidence of actual user confusion in
practice, etc.
4) Assuming all of the above works out, we still won’t be able to evaluate
the proposal until we have the exact details of how the “source stability”
model works. Until we have that model, we don’t know that this proposal
will be able to be covered by it.
On #4, I’m pretty certain that we will be fine, but until we have the
model nailed down, we procedurally can’t even have the formal review
period. In any case, you need to build a specific proposal with very
strong rationale first. :slight_smile:
-Chris

On Mon, Sep 5, 2016 at 3:19 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

This suggestion has been pitched earlier and I've expressed my opinion in
those earlier threads, but I'll repeat myself here:

I'm hugely opposed to such changes to the precedence table. Those of us
who work with bitwise operators on a regular basis have memorized their
precedence in Swift (and other languages) and rely on such precedence to
write readable, correct code without excessively nested parentheses. Any
change here would break existing, carefully constructed code, punishing
those who *have* put in the effort to learn the precedence table. To any
other user of Swift, it should come as no surprise that operators *have*
precedence and associativity, and it is not such a burden for a user either
to memorize or to consult a table for these properties when they are unsure
.

There is no way whatsoever to use intuition to arrive at the exact
precedence of `??`, or `as`, or `&&`, or `||`, or `&`, or `|`, or `^` or
`<<`, or `>>`, and there will be no relative precedence that will prove
intuitive to all. (That said, there is a rational basis for the relative
precedence of `&`, `|`, and `^` to each other.) If you believe this
situation to be problematic, then you must conclude that we should remove
relative precedence between any operators except perhaps the basic
arithmetic operators `+`, `-`, `*`, `/`. This line of reasoning would be
a huge U-turn from the direction of Swift, which after all just revised the
syntax with which custom operator precedence is defined. Such a feature
would be totally out of place in a language where operators other than
those for basic arithmetic are not supposed to have precedence relations
with each other.

(Of course, the relative precedence of arithmetic operators is in some
ways arbitrary as well, though it is inherited from math that everyone
knows. How did you learn it in the first place? You memorized it.)

On Mon, Sep 5, 2016 at 2:47 PM, Erica Sadun via swift-evolution < > swift-evolution@swift.org> wrote:

At this point, I'm not sure whether this is an -evolution question or a
-dev question. The latter would be much easier to work on at this time and
could potentially be postponed to a dot release. I know that any
conversation not directly related to 3.0 right now is a Bad Thing.

And I suspect that Steven C is probably the right person to know about
wrangling precedence and existing standards.

-- E

On Sep 5, 2016, at 12:30 AM, Jacob Bandes-Storch <jtbandes@gmail.com> >> wrote:

Now you've gotten me thinking about precedence of other operators too.

Since ?? is prone to causing confusion in either direction (cf. your
example and my example), it could be put in its own group whose relation to
the numeric operators is intentionally undefined (thus requiring parens).

I don't know about other folks, but I'll certainly get confused if & and
> and ^ are mixed. What if we removed their relation to each other
(requiring parens when mixing them)?

<proposed-precedence.png>

For comparison (ha), here's what we have today:

<current-precedence.png>

Jacob

On Sat, Sep 3, 2016 at 10:20 PM, Erica Sadun <erica@ericasadun.com> >> wrote:

On Sep 3, 2016, at 10:15 PM, Jacob Bandes-Storch <jtbandes@gmail.com> >>> wrote:

Perhaps-conversely, what should this code do?

    let nextIndex = foundIndex ?? lastIndex + 1

Jacob

It's a good counter example. And there's no optional-associative option.

-- E

On Sat, Sep 3, 2016 at 9:05 PM, Erica Sadun via swift-evolution < >>> swift-evolution@swift.org> wrote:

Given: `let x = Optional(3)` then

`let y = 5 + x ?? 2` will not compile

but

`let y = 5 + (x ?? 2)` will.

Should NilCoalescingPrecedence be raised? The current operator
precedence chain is:

BitwiseShiftPrecedence > MultiplicationPrecedence > AdditionPrecedence
> RangeFormationPrecedence > CastingPrecedence > NilCoalescingPrecedence >
ComparisonPrecedence > LogicalConjunctionPrecedence >
LogicalDisjunctionPrecedence > TernaryPrecedence > AssignmentPrecedence >
FunctionArrowPrecedence > [nothing]

It seems to me that `NilCoalescingPrecedence` should probably be
higher than `MultiplicationPrecedence` and possibly higher `
BitwiseShiftPrecedence` as its job is to produce an unwrapped value
that can then be operated upon.

I think CastingPrecedence should be even higher because

`expression as? T ?? fallback value`

should be parsed as

`(expression as? T) ?? (fallback value)`

I apologize profusely because I know this is beyond last minute,

-- E

_______________________________________________
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


(Vladimir) #8

This suggestion has been pitched earlier and I've expressed my opinion in
those earlier threads, but I'll repeat myself here:

I'm hugely opposed to such changes to the precedence table. Those of us who
work with bitwise operators on a regular basis have memorized their
precedence in Swift (and other languages) and rely on such precedence to
write readable, correct code without excessively nested parentheses. Any
change here would break existing, carefully constructed code, punishing
those who *have* put in the effort to learn the precedence table. To any

FWIW, I can't understand such an opinion. From my view this is a fragile code, you are saying about *writing* of the code, but code will be read much often than written.

The question is not how *you* good as developer and if *you* rememebered *all* precedence in Swift, but how many *bugs* will you have in 3rd party code, written by someone who not as good as you or probably just loose the concentration for one moment and written incorrect logic just because it s *so easy* to make such kind of mistakes.
And also the question is how your code will be understood by some other developer who will *read* your code and probably *modify* it later.

Currently we can have such a code:

let nextIndex = foundIndex ?? lastIndex + 1

let result = a || (b) ? isOne() : isTwo()

s << (x == 10) ? "10" : "not 10"

let i = 4 << 1 + 3

If we'll ask here in list for results for these lines, I believe we'll have a big number of people who will not answer correctly.

Swift *requires* to be careful in some situations, for example when working with Integer types, you can't "just" add Int and Int8, you have to explicitly case. Why? Because IMO Swift want to be safe(as much as possible) language, which helps to produce code without surprises, where you explicitly show your intention.

Do you really believe that the above code is clear about the intention of the developer and the *ability* to write such code don't open the door to hard-find bugs in code(not in your code, of course)

And about lints. They can help only for your own code in project.
Even if you added 3rd party code to your project as source, with lint you'll have to change all of such code and analyze logic in each questionable line (if this line is correct or there is a logical error). Plus, if you add 3rd party code as compiled framework/module you can't check it at all.

Yes, I do believe that Swift should require a parenthesis for any expression, where it is not clear what is the order of operations. Probably when we have an operations of different groups in the same expression. Exactly for the same reason - to make developer be clear about the intention of such code:

let nextIndex = foundIndex ?? (lastIndex + 1)

let result = a || ((b) ? isOne() : isTwo())

s << ((x == 10) ? "10" : "not 10")

let i = 4 << (1 + 3)

IMO much clear, I see *no* visual problems because of added parenthesis, only increased readability and my "parser" in head processes this faster

Again, there were (fixed now, I believe) a lot of logical errors(bugs) in well-known open source projects(I do believe there was code review for them and code most likely written by well-skilled developers) because of possibility to mix different groups of operators in same line without parenthesis (for example in Chromium,ReactOS,MongoDB,Apache Xerces Project,Unreal Engine 4,Wine,FreeBSD Kernel,Open X-Ray Engine,OpenJDK,CryEngine V,GCC). Again, not just because C allows to treat boolean values as integers, but because of mixing of operators.

I do believe Swift should be the language that prevent such "possible-buggy" code in all areas.

So, my suggestion is: produce a warning when operators from different groups mixed in the same expression without parenthesis. Or let's discuss another condition/method. This will not break the "old" code, but if you want to write not clear code and don't want to be explicit about your intention - you will produce code with warnings. Fair enough, I think.

Such kind of bugs is hard-to-find and IMO it is worth to have "warning" and to add parenthesis to prevent them not only in *your* code, but also in any 3rd party code or in compiled framework/module.

To all: please provide your opinions on this subject, so we all can see if a lot of developers support my opinion(or don't).
Thank you for reading this.

···

On 05.09.2016 23:19, Xiaodi Wu via swift-evolution wrote:

other user of Swift, it should come as no surprise that operators *have*
precedence and associativity, and it is not such a burden for a user either
to memorize or to consult a table for these properties when they are unsure.

There is no way whatsoever to use intuition to arrive at the exact
precedence of `??`, or `as`,or `&&`, or `||`, or `&`, or `|`, or `^` or
`<<`, or `>>`, and there will be no relative precedence that will prove
intuitive to all. (That said, there is a rational basis for the relative
precedence of `&`, `|`, and `^` to each other.) If you believe this
situation to be problematic, then you must conclude that we should remove
relative precedence between any operators except perhaps the basic
arithmetic operators `+`, `-`, `*`, `/`. This line of reasoning would be a
huge U-turn from the direction of Swift, which after all just revised the
syntax with which custom operator precedence is defined. Such a feature
would be totally out of place in a language where operators other than
those for basic arithmetic are not supposed to have precedence relations
with each other.

(Of course, the relative precedence of arithmetic operators is in some ways
arbitrary as well, though it is inherited from math that everyone knows.
How did you learn it in the first place? You memorized it.)

On Mon, Sep 5, 2016 at 2:47 PM, Erica Sadun via swift-evolution > <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

    At this point, I'm not sure whether this is an -evolution question or a
    -dev question. The latter would be much easier to work on at this time
    and could potentially be postponed to a dot release. I know that any
    conversation not directly related to 3.0 right now is a Bad Thing.

    And I suspect that Steven C is probably the right person to know about
    wrangling precedence and existing standards.

    -- E

    On Sep 5, 2016, at 12:30 AM, Jacob Bandes-Storch <jtbandes@gmail.com >> <mailto:jtbandes@gmail.com>> wrote:

    Now you've gotten me thinking about precedence of other operators too.

    Since ?? is prone to causing confusion in either direction (cf. your
    example and my example), it could be put in its own group whose
    relation to the numeric operators is intentionally undefined (thus
    requiring parens).

    I don't know about other folks, but I'll certainly get confused if &
    and | and ^ are mixed. What if we removed their relation to each
    other (requiring parens when mixing them)?

    <proposed-precedence.png>

    For comparison (ha), here's what we have today:

    <current-precedence.png>

    Jacob

    On Sat, Sep 3, 2016 at 10:20 PM, Erica Sadun <erica@ericasadun.com >> <mailto:erica@ericasadun.com>> wrote:

        On Sep 3, 2016, at 10:15 PM, Jacob Bandes-Storch >>> <jtbandes@gmail.com <mailto:jtbandes@gmail.com>> wrote:

        Perhaps-conversely, what should this code do?

            let nextIndex = foundIndex ?? lastIndex + 1

        Jacob

        It's a good counter example. And there's no optional-associative
        option.

        -- E

        On Sat, Sep 3, 2016 at 9:05 PM, Erica Sadun via swift-evolution >>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> >>> wrote:

            Given: `let x = Optional(3)` then

                `let y = 5 + x ?? 2` will not compile

            but

                `let y = 5 + (x ?? 2)` will.

            Should NilCoalescingPrecedence be raised? The current
            operator precedence chain is:

                BitwiseShiftPrecedence > MultiplicationPrecedence >
                AdditionPrecedence > RangeFormationPrecedence >
                CastingPrecedence > NilCoalescingPrecedence >
                ComparisonPrecedence > LogicalConjunctionPrecedence >
                LogicalDisjunctionPrecedence > TernaryPrecedence >
                AssignmentPrecedence > FunctionArrowPrecedence > [nothing]

            It seems to me that `NilCoalescingPrecedence` should
            probably be higher than `MultiplicationPrecedence` and
            possibly higher `BitwiseShiftPrecedence` as its job is to
            produce an unwrapped value that can then be operated upon.

            I think CastingPrecedence should be even higher because

                `expression as? T ?? fallback value`

            should be parsed as

                `(expression as? T) ?? (fallback value)`

            I apologize profusely because I know this is beyond last minute,

            -- E

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

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

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


(Jacob Bandes-Storch) #9

This suggestion has been pitched earlier and I've expressed my opinion in
those earlier threads, but I'll repeat myself here:

I'm hugely opposed to such changes to the precedence table. Those of us
who work with bitwise operators on a regular basis have memorized their
precedence in Swift (and other languages) and rely on such precedence to
write readable, correct code without excessively nested parentheses.

Could you point me towards some examples of such code? I don't write it
very often, so I don't feel I can really evaluate this. (This seems
analogous to the "terms of art" categorization from the API Design
Guidelines threads.) Much of the code I would normally write using bitwise
operators has been replaced with the SetAlgebra protocol methods provided
on OptionSet types.

Any change here would break existing, carefully constructed code, punishing

those who *have* put in the effort to learn the precedence table. To any
other user of Swift, it should come as no surprise that operators *have*
precedence and associativity, and it is not such a burden for a user either
to memorize or to consult a table for these properties when they are unsure
.

There is no way whatsoever to use intuition to arrive at the exact
precedence of `??`, or `as`, or `&&`, or `||`, or `&`, or `|`, or `^` or
`<<`, or `>>`, and there will be no relative precedence that will prove
intuitive to all. (That said, there is a rational basis for the relative
precedence of `&`, `|`, and `^` to each other.) If you believe this
situation to be problematic, then you must conclude that we should remove
relative precedence between any operators except perhaps the basic
arithmetic operators `+`, `-`, `*`, `/`.

I hadn't really noticed until now that && and || have a defined ordering,
and can be mixed. These are operators I *do* use regularly, and mixing them
still makes me uncomfortable / seems unclear. Clang provides a warning for
this: -Wlogical-op-parentheses.

I have a hard time seeing why the bitwise and arithmetic operators should
belong to the same set of precedence groups, such that & and * are BOTH
stronger than + and |. Even if some people are more acquainted with the
bitwise operators' precedences, as you say, why should & be stronger than
+ ? Why should * be stronger than | ?

How about this?

<img src=’/uploads/default/original/1X/5ab62568dbda052c12066b50df31741ee39dca07.png’ width=‘428’ height=‘500’>

(Personally, I'm comfortable with BitwiseShift being stronger than both the
other bitwise and the arithmetic operators, because it's a close cousin of
exponentiation.)

This line of reasoning would be a huge U-turn from the direction of Swift,
which after all just revised the syntax with which custom operator
precedence is defined. Such a feature would be totally out of place in a
language where operators other than those for basic arithmetic are not
supposed to have precedence relations with each other.

(Of course, the relative precedence of arithmetic operators is in some
ways arbitrary as well, though it is inherited from math that everyone
knows. How did you learn it in the first place? You memorized it.)

(from a later email)

The promise of Swift 3 was that going forward, only essential

source-breaking changes would occur; here, nothing about operators has
changed since version 1, yet all of a sudden we are considering how to
fundamentally alter how they work. Existing code *will break*, and
sometimes silently, if such changes are made.

How could the breakage possibly be silent for a change like this? I'm not
proposing to add any new precedence relationships, just to remove/separate
existing ones.

···

On Mon, Sep 5, 2016 at 1:19 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:


(Xiaodi Wu) #10

This suggestion has been pitched earlier and I've expressed my opinion in
those earlier threads, but I'll repeat myself here:

I'm hugely opposed to such changes to the precedence table. Those of us
who
work with bitwise operators on a regular basis have memorized their
precedence in Swift (and other languages) and rely on such precedence to
write readable, correct code without excessively nested parentheses. Any
change here would break existing, carefully constructed code, punishing
those who *have* put in the effort to learn the precedence table. To any

FWIW, I can't understand such an opinion. From my view this is a fragile
code, you are saying about *writing* of the code, but code will be read
much often than written.

The question is not how *you* good as developer and if *you* rememebered
*all* precedence in Swift, but how many *bugs* will you have in 3rd party
code, written by someone who not as good as you or probably just loose the
concentration for one moment and written incorrect logic just because it s
*so easy* to make such kind of mistakes.
And also the question is how your code will be understood by some other
developer who will *read* your code and probably *modify* it later.

Currently we can have such a code:

let nextIndex = foundIndex ?? lastIndex + 1

let result = a || (b) ? isOne() : isTwo()

s << (x == 10) ? "10" : "not 10"

let i = 4 << 1 + 3

If we'll ask here in list for results for these lines, I believe we'll
have a big number of people who will not answer correctly.

(a) I don't believe so; (b) even if that were the case, it's *learnable*
(i.e. once you have learned it once, it is possible to remember); (c) it is
not the right test--there are plenty of functions that people would
struggle to understand without consulting documentation; but no one writes
or reads code using only Notepad, and with the use of a single table,
*everyone* can answer correctly. Compare this to the situation with
existentials, which involves high-level concepts regarding type systems: it
would take much more than a single table to help someone understand why
Self or associated type constraints prevent you from doing certain things
with protocols.

Swift *requires* to be careful in some situations, for example when
working with Integer types, you can't "just" add Int and Int8, you have to
explicitly case. Why? Because IMO Swift want to be safe(as much as
possible) language, which helps to produce code without surprises, where
you explicitly show your intention.

I believe safe promotions are coming to Swift eventually; some of this is
intentional, but not all of the strictness here is by design.

Do you really believe that the above code is clear about the intention of
the developer and the *ability* to write such code don't open the door to
hard-find bugs in code(not in your code, of course)

And about lints. They can help only for your own code in project.
Even if you added 3rd party code to your project as source, with lint
you'll have to change all of such code and analyze logic in each
questionable line (if this line is correct or there is a logical error).
Plus, if you add 3rd party code as compiled framework/module you can't
check it at all.

Yes, I do believe that Swift should require a parenthesis for any
expression, where it is not clear what is the order of operations. Probably
when we have an operations of different groups in the same expression.
Exactly for the same reason - to make developer be clear about the
intention of such code:

let nextIndex = foundIndex ?? (lastIndex + 1)

let result = a || ((b) ? isOne() : isTwo())

s << ((x == 10) ? "10" : "not 10")

let i = 4 << (1 + 3)

IMO much clear, I see *no* visual problems because of added parenthesis,
only increased readability and my "parser" in head processes this faster

There is nothing today that prevents you, or any careful developer, from
using the parentheses *as necessary*, and all evidence points to people
using parentheses as necessary and wisely. I am *not arguing* that these
parentheses are not good. However, if you remove precedence relations, then
you are *forcing* everyone to use parentheses, always, in all situations.
Unlike other parts of Swift, it isn't even possible to extend existing
precedence groups that don't have relations, like it's possible to extend
types you don't own.

Again, there were (fixed now, I believe) a lot of logical errors(bugs) in
well-known open source projects(I do believe there was code review for them
and code most likely written by well-skilled developers) because of
possibility to mix different groups of operators in same line without
parenthesis (for example in Chromium,ReactOS,MongoDB,Apache Xerces
Project,Unreal Engine 4,Wine,FreeBSD Kernel,Open X-Ray
Engine,OpenJDK,CryEngine V,GCC). Again, not just because C allows to treat
boolean values as integers, but because of mixing of operators.

Can you provide examples that show this? The real-world example you
provided could not happen in Swift. Since you say that you know of lots of
logical errors that result from mixing of operators in these projects, can
you provide some examples from these projects that would also arise in
Swift? If so, you would persuade me of the problem.

I do believe Swift should be the language that prevent such
"possible-buggy" code in all areas.

So, my suggestion is: produce a warning when operators from different
groups mixed in the same expression without parenthesis. Or let's discuss
another condition/method. This will not break the "old" code, but if you
want to write not clear code and don't want to be explicit about your
intention - you will produce code with warnings. Fair enough, I think.

I think you are describing exactly the function of a linter.

Such kind of bugs is hard-to-find and IMO it is worth to have "warning"
and to add parenthesis to prevent them not only in *your* code, but also in
any 3rd party code or in compiled framework/module.

To all: please provide your opinions on this subject, so we all can see if
a lot of developers support my opinion(or don't).

Previous threads have shown opinions on both sides.

In the period of a few months, there have been proposals to change the
precedence and to remove the precedence of <<, >>, &, | ^, is, as, ??, ..<,
..., &&, ||, ?:. This represents *all* non-arithmetic, non-comparison,
non-assignment operators.

The promise of Swift 3 was that going forward, only essential
source-breaking changes would occur; here, nothing about operators has
changed since version 1, yet all of a sudden we are considering how to
fundamentally alter how they work. Existing code *will break*, and
sometimes silently, if such changes are made.

···

On Wed, Sep 7, 2016 at 8:11 AM, Vladimir.S <svabox@gmail.com> wrote:

On 05.09.2016 23:19, Xiaodi Wu via swift-evolution wrote:

Thank you for reading this.

other user of Swift, it should come as no surprise that operators *have*

precedence and associativity, and it is not such a burden for a user
either
to memorize or to consult a table for these properties when they are
unsure.

There is no way whatsoever to use intuition to arrive at the exact
precedence of `??`, or `as`,or `&&`, or `||`, or `&`, or `|`, or `^` or
`<<`, or `>>`, and there will be no relative precedence that will prove
intuitive to all. (That said, there is a rational basis for the relative
precedence of `&`, `|`, and `^` to each other.) If you believe this
situation to be problematic, then you must conclude that we should remove
relative precedence between any operators except perhaps the basic
arithmetic operators `+`, `-`, `*`, `/`. This line of reasoning would be a
huge U-turn from the direction of Swift, which after all just revised the
syntax with which custom operator precedence is defined. Such a feature
would be totally out of place in a language where operators other than
those for basic arithmetic are not supposed to have precedence relations
with each other.

(Of course, the relative precedence of arithmetic operators is in some
ways
arbitrary as well, though it is inherited from math that everyone knows.
How did you learn it in the first place? You memorized it.)

On Mon, Sep 5, 2016 at 2:47 PM, Erica Sadun via swift-evolution >> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

    At this point, I'm not sure whether this is an -evolution question or
a
    -dev question. The latter would be much easier to work on at this time
    and could potentially be postponed to a dot release. I know that any
    conversation not directly related to 3.0 right now is a Bad Thing.

    And I suspect that Steven C is probably the right person to know about
    wrangling precedence and existing standards.

    -- E

    On Sep 5, 2016, at 12:30 AM, Jacob Bandes-Storch <jtbandes@gmail.com >>> <mailto:jtbandes@gmail.com>> wrote:

    Now you've gotten me thinking about precedence of other operators
too.

    Since ?? is prone to causing confusion in either direction (cf. your
    example and my example), it could be put in its own group whose
    relation to the numeric operators is intentionally undefined (thus
    requiring parens).

    I don't know about other folks, but I'll certainly get confused if &
    and | and ^ are mixed. What if we removed their relation to each
    other (requiring parens when mixing them)?

    <proposed-precedence.png>

    For comparison (ha), here's what we have today:

    <current-precedence.png>

    Jacob

    On Sat, Sep 3, 2016 at 10:20 PM, Erica Sadun <erica@ericasadun.com >>> <mailto:erica@ericasadun.com>> wrote:

        On Sep 3, 2016, at 10:15 PM, Jacob Bandes-Storch >>>> <jtbandes@gmail.com <mailto:jtbandes@gmail.com>> wrote:

        Perhaps-conversely, what should this code do?

            let nextIndex = foundIndex ?? lastIndex + 1

        Jacob

        It's a good counter example. And there's no optional-associative
        option.

        -- E

        On Sat, Sep 3, 2016 at 9:05 PM, Erica Sadun via swift-evolution >>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> >>>> >>>> wrote:

            Given: `let x = Optional(3)` then

                `let y = 5 + x ?? 2` will not compile

            but

                `let y = 5 + (x ?? 2)` will.

            Should NilCoalescingPrecedence be raised? The current
            operator precedence chain is:

                BitwiseShiftPrecedence > MultiplicationPrecedence >
                AdditionPrecedence > RangeFormationPrecedence >
                CastingPrecedence > NilCoalescingPrecedence >
                ComparisonPrecedence > LogicalConjunctionPrecedence >
                LogicalDisjunctionPrecedence > TernaryPrecedence >
                AssignmentPrecedence > FunctionArrowPrecedence >
[nothing]

            It seems to me that `NilCoalescingPrecedence` should
            probably be higher than `MultiplicationPrecedence` and
            possibly higher `BitwiseShiftPrecedence` as its job is to
            produce an unwrapped value that can then be operated upon.

            I think CastingPrecedence should be even higher because

                `expression as? T ?? fallback value`

            should be parsed as

                `(expression as? T) ?? (fallback value)`

            I apologize profusely because I know this is beyond last
minute,

            -- E

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

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

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


(Xiaodi Wu) #11

This suggestion has been pitched earlier and I've expressed my opinion in
those earlier threads, but I'll repeat myself here:

I'm hugely opposed to such changes to the precedence table. Those of us
who work with bitwise operators on a regular basis have memorized their
precedence in Swift (and other languages) and rely on such precedence to
write readable, correct code without excessively nested parentheses.

Could you point me towards some examples of such code? I don't write it
very often, so I don't feel I can really evaluate this. (This seems
analogous to the "terms of art" categorization from the API Design
Guidelines threads.) Much of the code I would normally write using bitwise
operators has been replaced with the SetAlgebra protocol methods provided
on OptionSet types.

Gladly. These (which cannot be copied verbatim into Swift, as C operator
precedences are different):
https://graphics.stanford.edu/~seander/bithacks.html

Lest you think I'm giving you a C example because I don't actually use such
things in Swift, here's me using some of these:
https://github.com/xwu/FlowKit/blob/master/Source/BitVector.swift

(Note that this specific example will soon be obsolete with new integer
protocols.)

Any change here would break existing, carefully constructed code, punishing

those who *have* put in the effort to learn the precedence table. To any
other user of Swift, it should come as no surprise that operators *have*
precedence and associativity, and it is not such a burden for a user either
to memorize or to consult a table for these properties when they are unsure
.

There is no way whatsoever to use intuition to arrive at the exact
precedence of `??`, or `as`, or `&&`, or `||`, or `&`, or `|`, or `^` or
`<<`, or `>>`, and there will be no relative precedence that will prove
intuitive to all. (That said, there is a rational basis for the relative
precedence of `&`, `|`, and `^` to each other.) If you believe this
situation to be problematic, then you must conclude that we should remove
relative precedence between any operators except perhaps the basic
arithmetic operators `+`, `-`, `*`, `/`.

I hadn't really noticed until now that && and || have a defined ordering,
and can be mixed. These are operators I *do* use regularly, and mixing them
still makes me uncomfortable / seems unclear. Clang provides a warning for
this: -Wlogical-op-parentheses.

I have a hard time seeing why the bitwise and arithmetic operators should
belong to the same set of precedence groups, such that & and * are BOTH
stronger than + and |. Even if some people are more acquainted with the
bitwise operators' precedences, as you say, why should & be stronger than
+ ? Why should * be stronger than | ?

Just like << is a "close cousin" of exponentiation, & is a
multiplication-like operator and ^ and | are addition-like operators on
Boolean rings. To quote from previous messages in the older thread (from
Felix Cloutier and Steve Canon):

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).
<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".

How about this?

<img src=’/uploads/default/original/1X/5ab62568dbda052c12066b50df31741ee39dca07.png’ width=‘428’ height=‘500’>

(Personally, I'm comfortable with BitwiseShift being stronger than both
the other bitwise and the arithmetic operators, because it's a close cousin
of exponentiation.)

This line of reasoning would be a huge U-turn from the direction of
Swift, which after all just revised the syntax with which custom operator
precedence is defined. Such a feature would be totally out of place in a
language where operators other than those for basic arithmetic are not
supposed to have precedence relations with each other.

(Of course, the relative precedence of arithmetic operators is in some
ways arbitrary as well, though it is inherited from math that everyone
knows. How did you learn it in the first place? You memorized it.)

(from a later email)

The promise of Swift 3 was that going forward, only essential

source-breaking changes would occur; here, nothing about operators has
changed since version 1, yet all of a sudden we are considering how to
fundamentally alter how they work. Existing code *will break*, and
sometimes silently, if such changes are made.

How could the breakage possibly be silent for a change like this? I'm not
proposing to add any new precedence relationships, just to remove/separate
existing ones.

The proposals include both breaking precedence relations and changing them;
changing them will essentially always cause silent changes in existing
code. Removing relations won't; however, it will necessitate sometimes
difficult migrations, as formulas are complicated.

I have, I think, a simple but effective argument in defense of the current
scheme of operator precedences, but since it's midnight here, I'll let
others chime in and return to the discussion later.

···

On Wed, Sep 7, 2016 at 11:48 PM, Jacob Bandes-Storch <jtbandes@gmail.com> wrote:

On Mon, Sep 5, 2016 at 1:19 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

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


(Jacob Bandes-Storch) #12

This suggestion has been pitched earlier and I've expressed my opinion
in those earlier threads, but I'll repeat myself here:

I'm hugely opposed to such changes to the precedence table. Those of us
who work with bitwise operators on a regular basis have memorized their
precedence in Swift (and other languages) and rely on such precedence to
write readable, correct code without excessively nested parentheses.

Could you point me towards some examples of such code? I don't write it
very often, so I don't feel I can really evaluate this. (This seems
analogous to the "terms of art" categorization from the API Design
Guidelines threads.) Much of the code I would normally write using bitwise
operators has been replaced with the SetAlgebra protocol methods provided
on OptionSet types.

Gladly. These (which cannot be copied verbatim into Swift, as C operator
precedences are different):
https://graphics.stanford.edu/~seander/bithacks.html

Lest you think I'm giving you a C example because I don't actually use
such things in Swift, here's me using some of these:
https://github.com/xwu/FlowKit/blob/master/Source/BitVector.swift

(Note that this specific example will soon be obsolete with new integer
protocols.)

Both of these actually seem to make pretty careful use of parentheses in
expressions with mixed-precedence infix operators. In the former, I find
only a small handful of cases where & is mixed with +, *, or / — seemingly
the minority.

The proposals include both breaking precedence relations and changing
them; changing them will essentially always cause silent changes in
existing code. Removing relations won't; however, it will necessitate
sometimes difficult migrations, as formulas are complicated.

I would think (hope) that any removals could easily yield automatic
migrations by adding parens where they were previously implied. I would
only be concerned with the possibility that such a migration would affect
type-checking speed (an area that already needs improvement, and can be
exacerbated by large expressions).

···

On Wed, Sep 7, 2016 at 10:02 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Wed, Sep 7, 2016 at 11:48 PM, Jacob Bandes-Storch <jtbandes@gmail.com> > wrote:

On Mon, Sep 5, 2016 at 1:19 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:


(Xiaodi Wu) #13

This suggestion has been pitched earlier and I've expressed my opinion
in those earlier threads, but I'll repeat myself here:

I'm hugely opposed to such changes to the precedence table. Those of
us who work with bitwise operators on a regular basis have memorized their
precedence in Swift (and other languages) and rely on such precedence to
write readable, correct code without excessively nested parentheses.

Could you point me towards some examples of such code? I don't write it
very often, so I don't feel I can really evaluate this. (This seems
analogous to the "terms of art" categorization from the API Design
Guidelines threads.) Much of the code I would normally write using bitwise
operators has been replaced with the SetAlgebra protocol methods provided
on OptionSet types.

Gladly. These (which cannot be copied verbatim into Swift, as C operator
precedences are different):
https://graphics.stanford.edu/~seander/bithacks.html

Lest you think I'm giving you a C example because I don't actually use
such things in Swift, here's me using some of these:
https://github.com/xwu/FlowKit/blob/master/Source/BitVector.swift

(Note that this specific example will soon be obsolete with new integer
protocols.)

Both of these actually seem to make pretty careful use of parentheses in
expressions with mixed-precedence infix operators.

I do try to be very careful, yes :slight_smile: If I recall correctly, I have actually
hardcoded the order of operations entirely with parentheses, disregarding
Swift's precedence table entirely. The reason for this was twofold: (1) at
the time I first wrote the code, it was the very first time I'd used
Swift's bitwise operators, which have different precedences from C; (2) I
was porting over a numerical recipe from C, and I preserved the parentheses
that were required in that language for my own sanity.

I shared that example in part because I think it illustrates a salient
point here. Chris Lattner--in his comment in the previous thread on the
topic--challenged those proposing this change to show "evidence of common
bugs" or "evidence of actual user confusion." By contrast, my experience
has been (both from reading and writing code) that there's typically very
good code hygiene in instances where mixed precedence operators are used in
practice, and (with respect to writing code) I certainly don't consider
myself an unusually careful programmer.

Clearly, no one disputes that parentheses can be wise in some instances
even when they're not required. Essentially, by proposing removal of
precedence relations, the question you're raising is: should parentheses
always be required [in some subset of circumstances]?

My take on this issue, if I may rephrase, is simply this:

1. When I've been confused about the order of operations as I'm *writing*
code, I've put in parentheses without hesitation. From what I've gathered
from this list, essentially everyone else does this as well. Would removing
precedence relations improve this aspect of using operators (i.e. writing
code)? It does not seem so, since it would make required what people
already use according to their best judgment.

2. When I've been confused about the order of operations as I'm *reading*
code, I simply turn to the precedence table. Three points on the experience
of reading code using operators:
(a) In practice, I've rarely been confused about the order of operations,
since (as mentioned above) I find that those who write the code tend to
clarify for the reader anyway, if only for their own sanity while writing
the code.
(b) When I've found myself confused, as in the case with some of the "Bit
Hacks" linked above, this issue has been the least of my problems, as
formulas that mix these operators have proved difficult to digest even with
all the parentheses put in.
(c) It's well nigh impossible to read the precedence table incorrectly: the
_concept_ of operator precedence must be second-nature to any person who
knows what multiplication is, so it's simply a matter of finding out what
the actual precedence _is_; no ambiguity persists after that information is
supplied.

In the former, I find only a small handful of cases where & is mixed with

+, *, or / — seemingly the minority.

I haven't gone through to tabulate what proportion of those formulas mix
certain categories of operators with others. It's not a particularly useful
piece of information, I'd argue, since no one is implementing this entire
book of recipes and calling each function a roughly equal number of times
in their code. The subset of these recipes that I've studied (or
equivalently, have been interested in using or actually have used) mix
operators pretty frequently. It all comes down to what code you're writing.
In some ways, this gets to another tentpole of my argument to preserve
precedences. In some ways, all arguments about their addition, removal, or
modification, seem to have boiled down to a subjective judgment as to their
current appropriateness or how common their use or misuse might be, but
with strikingly little data for evidence.

Case in point (without meaning to calling you out specifically; just that
our conversation is the freshest in my mind, and it's late and my mind is
foggy): you mentioned that you were content with the current precedence of
bitwise <<, because you could see how it was related to exponentiation; but
you were not happy with the precedence of &, because you could not see how
it was related to multiplication. However, by the same token, I can see
both how << is related to exponentiation and how & is related to
multiplication, and thus I am content with their precedence. The
counter-counterargument in the previous thread was that the relationship
between & and multiplication was not sufficiently obvious or relevant, but
of course that's in turn also a subjective judgment. I can equally argue
that the relationship between << and exponentiation is not sufficiently
relevant either, or that the relationship between & and multiplication is
crucially relevant...

Given that Swift has already revised operator precedence (as compared to
the C family of languages), and given that Swift 4 makes some promise of
avoiding source-breaking changes where possible, the above style of
argumentation seems woefully insufficient to justify another change.
Rather, I could be swayed by objective evidence that users *commonly*
expect some operator to work a certain way, leading to actual erroneous
real-world code. However, it does not seem that such evidence is
forthcoming. By contrast, let's visit some of the not-so-real-world
examples we've seen so far:

Erica thought that `let y = 5 + (x ?? 2)` required one too many sets of
parentheses. Removal of the precedence relation between ?? and arithmetic
operators does nothing to address this gap between Swift's syntax and
Erica's expectations. In fact, we would be making the parentheses mandatory
in more scenarios than are required currently.

Let's revisit some of Vladimir's examples:

I believe that `let result = a || (b) ? isOne() : isTwo()` is implausible
for several reasons. I will give only two. First, I have never encountered
any actual attempt to evaluate four Boolean values in some combination of a
binary operator and the ternary operator. Second, it is unlikely that a
writer of code would try _but fail_ at correctly surrounding the intended
portions of that statement with parentheses (i.e. either `(a || b)` or `(b
? isOne() : isTwo())`), instead ineffectually surrounding `b` with
parentheses.

`s << (x == 10) ? "10" : "not 10"` is impossible. Firstly because no such
`<<` operator is defined in the standard library or core libraries, and I
do not know of any Swift library that does define such an operator.
Secondly because Swift greatly discourages (to my knowledge) the
overloading of operators with entirely different semantics. Thirdly because
even if one such operator is defined but the order of operations is not as
the writer intended, the expression would not compile because of a type
mismatch. Fourthly, because even if two such operators are defined so that
the expression would compile, the result would differ in type (from the
expected type) if the order of operations is not as the writer intended,
and any further non-trivial use of the result would lead to an error at
compile time.

You and I seem to agree that `let i = 4 << 1 + 3` is rather reasonable. But
we needn't use such a subjective judgment. Quite simply, you would not
write such a line of code if you weren't sure of the precedence of <<
relative to +. You'd use parentheses. You wouldn't read such a line of code
just assuming a certain precedence relation if you were foggy on
bitshifting operators; you would look it up. I am not sure how one can make
an unintended error either reading or writing this line of code[*].

[*] Actually, I can see precisely one way to make such an unintended error,
since I have actually done so. It has to do with Swift having moved away
from the classical C-family operator precedence table. I would actually not
be opposed to making changes that guard against such an error.

The proposals include both breaking precedence relations and changing
them; changing them will essentially always cause silent changes in
existing code. Removing relations won't; however, it will necessitate
sometimes difficult migrations, as formulas are complicated.

I would think (hope) that any removals could easily yield automatic
migrations by adding parens where they were previously implied. I would
only be concerned with the possibility that such a migration would affect
type-checking speed (an area that already needs improvement, and can be
exacerbated by large expressions).

Certainly, automatic migrations will help avoid certain errors. Our frail
human brains make certain mistakes possible when adding or removing
parentheses by hand. Things like distributing the minus sign, etc...

···

On Thu, Sep 8, 2016 at 12:17 AM, Jacob Bandes-Storch <jtbandes@gmail.com> wrote:

On Wed, Sep 7, 2016 at 10:02 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Wed, Sep 7, 2016 at 11:48 PM, Jacob Bandes-Storch <jtbandes@gmail.com> >> wrote:

On Mon, Sep 5, 2016 at 1:19 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:


(Pyry Jahkola) #14

To stay slightly on topic of the original Subject line, I'd point out that no one has yet demonstrated an example where the current precedence position of `??` causes bugs at runtime.

The original motivation by Erica simply failed to compile without the parentheses, so it wasn't about confusion so much as about mere convenience. And there exist actual uses where the current precedence brings greater convenience, e.g. when the RHS expression contains arithmetic operations.

Like Wux, I also don't think we have any reason to adjust the precedence of `??` from what it is.

* * *

But then, I also think we should do something to our bitwise ops:

You and I seem to agree that `let i = 4 << 1 + 3` is rather reasonable.

Here, I would actually differ somewhat. If there's something that ought to be higher in precedence than `+` and `*`, IMO that would be actual exponentiation, which could be approximated in a math library by the presently missing `**` operator, for example.

I am not sure how one can make an unintended error either reading or writing this line of code[*].

[*] Actually, I can see precisely one way to make such an unintended error, since I have actually done so. It has to do with Swift having moved away from the classical C-family operator precedence table. I would actually not be opposed to making changes that guard against such an error.

There's a fair amount of C code out in the wild that is going to be migrated into Swift in the near future. And that code would benefit if Swift's precedence rules didn't unexpectedly break from C – that is, not without the programmer's awareness.

The key differences to C that I think will cause confusion in Swift are:

A) Unlike C, Swift defines `<<` and `>>` with the highest precedence.
B) Unlike C, Swift defines `&` at multiplication precedence (`*` etc.).
C) Unlike C, Swift defines both `^` and `|` at equal precedence to each other, and that is the precedence of addition (`+` etc.).

Other than that, we also use equal precedence for all comparison operators but at the same time remove their associativity, so that can't cause runtime problems (as such code would fail to compile). Here's a full table, with the notable differences marked with a red comment:

Infix operators in C
In Swift 3
// skipping unary ops etc

* / %
             << >> // (!)
+ -
      * &* / % & // (!)
<< >>
     + &+ - &- | ^ // (!)
< <= > >=
... ..<
== !=
as as? is
&
??
^
< <= > >= == != === !== ~=

&&
&&

(default precedence diverges here)
?:
?:
= @= // for various `@`
= @= // for various `@`

···

On 08 Sep 2016, at 09:58, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

,
->

I think Jacob has a point in separating bitwise operations into their own hierarchy. I'd recommend doing so all the way, but retaining `&` with a higher "bitwise-multiplicative" precedence:

1) Move `|` and `^` into their own parallel precedence groups named BitwiseOr and BitwiseXor, just above RangeFormation.
2) Move `&` into its own group BitwiseAnd, just above both BitwiseOr and BitwiseXor.
3) Move BitwiseShift (i.e. `<<` and `>>`) just above BitwiseAnd.

That would make it so that rarely occurring*) expressions mixing `^` and `|` would now need parenthesising, but either of them would work together with `&`, `<<`, and `>>` just like they do in C (and just the way binary addition relates to multiplication in the conventions of mathematics).

*) E.g. none found in https://graphics.stanford.edu/~seander/bithacks.html.

Also integer arithmetic would stay clearly separated from bitwise operations, which in my experience is the source of most bugs and confusion. E.g. the expressions `a & b + c`, `a | b + c`, and `a << b - c` all compile in both C and Swift, but are parsed in exactly opposing ways.

The whole hierarchy I'm proposing is illustrated in the attachment below, with all changes found in the top part above RangeFormation.

— Pyry


(Xiaodi Wu) #15

That sounds pretty good to me, actually. It removes one category of

real-world bugs without fundamentally upending Swift operators. Neat
solution regarding separation of | and ^.

···

On Thu, Sep 8, 2016 at 04:08 Pyry Jahkola <pyry.jahkola@iki.fi> wrote:

To stay slightly on topic of the original Subject line, I'd point out
that no one has yet demonstrated an example where the current precedence
position of `??` causes bugs at runtime.

The original motivation by Erica simply failed to compile without the
parentheses, so it wasn't about confusion so much as about mere
convenience. And there exist actual uses where the current precedence
brings greater convenience, e.g. when the RHS expression contains
arithmetic operations.

Like Wux, I also don't think we have any reason to adjust the precedence
of `??` from what it is.

* * *

But then, I also think we should do something to our bitwise ops:

On 08 Sep 2016, at 09:58, Xiaodi Wu via swift-evolution < >> swift-evolution@swift.org> wrote:

You and I seem to agree that `let i = 4 << 1 + 3` is rather reasonable.

Here, I would actually differ somewhat. If there's something that ought
to be higher in precedence than `+` and `*`, IMO that would be actual
exponentiation, which could be approximated in a math library by the
presently missing `**` operator, for example.

I am not sure how one can make an unintended error either reading or
writing this line of code[*].

[*] Actually, I can see precisely one way to make such an unintended
error, since I have actually done so. It has to do with Swift having moved
away from the classical C-family operator precedence table. I would
actually not be opposed to making changes that guard against such an error.

There's a fair amount of C code out in the wild that is going to be
migrated into Swift in the near future. And that code would benefit if
Swift's precedence rules didn't unexpectedly break from C – that is, not
without the programmer's awareness.

The key differences to C that I think will cause confusion in Swift are:

A) Unlike C, Swift defines `<<` and `>>` with the highest precedence.
B) Unlike C, Swift defines `&` at multiplication precedence (`*` etc.).
C) Unlike C, Swift defines both `^` and `|` at equal precedence to each
other, and that is the precedence of addition (`+` etc.).

Other than that, we also use equal precedence for all comparison
operators but at the same time remove their associativity, so that can't
cause runtime problems (as such code would fail to compile). Here's a full
table, with the notable differences marked with a red comment:

*Infix operators in C*
*In Swift 3*
// skipping unary ops etc

* / %
* << >> // (!)*
+ -
      * &* / % *& // (!)*
<< >>
     + &+ - &- *| ^ // (!)*
< <= > >=
... ..<
== !=
as as? is
&
??
^
< <= > >= == != === !== ~=
>
&&
&&
>>
>>
(default precedence diverges here)
?:
?:
= @= // for various `@`
= @= // for various `@`
,
->

I think Jacob has a point in separating bitwise operations into their own
hierarchy. I'd recommend doing so all the way, but retaining `&` with a
higher "bitwise-multiplicative" precedence:

1) Move `|` and `^` into their own parallel precedence groups named
*BitwiseOr* and *BitwiseXor*, just above RangeFormation.
2) Move `&` into its own group *BitwiseAnd*, just above both BitwiseOr
and BitwiseXor.
3) Move *BitwiseShift* (i.e. `<<` and `>>`) just above BitwiseAnd.

That would make it so that rarely occurring*) expressions mixing `^` and
`|` would now need parenthesising, but either of them would work together
with `&`, `<<`, and `>>` just like they do in C (and just the way binary
addition relates to multiplication in the conventions of mathematics).

*) E.g. none found in https://graphics.stanford.
edu/~seander/bithacks.html.

Also integer arithmetic would stay clearly separated from bitwise
operations, which in my experience is the source of most bugs and
confusion. E.g. the expressions `a & b + c`, `a | b + c`, and `a << b - c`
all compile in both C and Swift, but are parsed in exactly opposing ways.

The whole hierarchy I'm proposing is illustrated in the attachment below,
with all changes found in the top part above RangeFormation.

— Pyry


(Vladimir) #16

To stay slightly on topic of the original Subject line, I'd point out that
no one has yet demonstrated an example where the current precedence
position of `??` causes bugs at runtime.

Why do you need to see some precedence(I can create a sample project for you on github) while this construction is so simple and obviously would be used often and *will* cause bugs. I insist that if construction easily allows logical bugs in it - there will be a lot of bugs in such construction in different projects/frameworks/modules.

The original motivation by Erica simply failed to compile without the
parentheses, so it wasn't about confusion so much as about mere

Sorry, but you are just not right. The code compiles without even warning:

Swift Ver. 3.0 (Sep 6, 2016)
Platform: Linux (x86_64)

let foundIndex : Int? = 1
let lastIndex = 2

let nextIndex = foundIndex ?? lastIndex + 1

print(nextIndex) // 1

convenience. And there exist actual uses where the current precedence
brings greater convenience, e.g. when the RHS expression contains
arithmetic operations.

Like Wux, I also don't think we have any reason to adjust the precedence of
`??` from what it is.

After I showed the code compiles, would you change your opinion?

IMO the best solution which will dramatically reduce the number of future logical errors(bugs) in code with mixed operators would be to require(probably, just by raising a compilation warning) parenthesis when operators from different groups are mixed in the same expression. IMO no need in complex rules to make Swift safer. I.e. when you have a = 10 + 5 * 10 - you don't need parenthesis, when you have a = b << 4 - also all is ok, but if you have a = b << 4 + 5 * 10 - you'll have a warning until add parenthesis to make your intention clear. IMO this should prevent almost all logical errors in such expressions and increase the common quality of Swift code(in each project/framework/module).

···

On 08.09.2016 12:08, Pyry Jahkola via swift-evolution wrote:

* * *

But then, I also think we should do something to our bitwise ops:

On 08 Sep 2016, at 09:58, Xiaodi Wu via swift-evolution >> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

You and I seem to agree that `let i = 4 << 1 + 3` is rather reasonable.

Here, I would actually differ somewhat. If there's something that ought to
be higher in precedence than `+` and `*`, IMO that would be actual
exponentiation, which could be approximated in a math library by the
presently missing `**` operator, for example.

I am not sure how one can make an unintended error either reading or
writing this line of code[*].

[*] Actually, I can see precisely one way to make such an unintended
error, since I have actually done so. It has to do with Swift having
moved away from the classical C-family operator precedence table. I would
actually not be opposed to making changes that guard against such an error.

There's a fair amount of C code out in the wild that is going to be
migrated into Swift in the near future. And that code would benefit if
Swift's precedence rules didn't unexpectedly break from C – that is, not
without the programmer's awareness.

The key differences to C that I think will cause confusion in Swift are:

A) Unlike C, Swift defines `<<` and `>>` with the highest precedence.
B) Unlike C, Swift defines `&` at multiplication precedence (`*` etc.).
C) Unlike C, Swift defines both `^` and `|` at equal precedence to each
other, and that is the precedence of addition (`+` etc.).

Other than that, we also use equal precedence for all comparison operators
but at the same time remove their associativity, so that can't cause
runtime problems (as such code would fail to compile). Here's a full table,
with the notable differences marked with a red comment:

*Infix operators in C*
  
*In Swift 3*
// skipping unary ops etc
  
* / %
  
* << >> // (!)*
+ -
  
      * &* / % *& // (!)*
<< >>
  
     + &+ - &- *| ^ // (!)*
< <= > >=
  
... ..<
== !=
  
as as? is
&
  
??
^
  
< <= > >= == != === !== ~=
>
  
&&
  
>>
  
(default precedence diverges here)
?:
  
?:
= @= // for various `@`
  
= @= // for various `@`
,
  
->

I think Jacob has a point in separating bitwise operations into their own
hierarchy. I'd recommend doing so all the way, but retaining `&` with a
higher "bitwise-multiplicative" precedence:

1) Move `|` and `^` into their own parallel precedence groups named
*BitwiseOr* and *BitwiseXor*, just above RangeFormation.
2) Move `&` into its own group *BitwiseAnd*, just above both BitwiseOr and
BitwiseXor.
3) Move *BitwiseShift* (i.e. `<<` and `>>`) just above BitwiseAnd.

That would make it so that rarely occurring*) expressions mixing `^` and
`|` would now need parenthesising, but either of them would work together
with `&`, `<<`, and `>>` just like they do in C (and just the way binary
addition relates to multiplication in the conventions of mathematics).

*) E.g. none found in https://graphics.stanford.edu/~seander/bithacks.html
<https://graphics.stanford.edu/~seander/bithacks.html>.

Also integer arithmetic would stay clearly separated from bitwise
operations, which in my experience is the source of most bugs and
confusion. E.g. the expressions `a & b + c`, `a | b + c`, and `a << b - c`
all compile in both C and Swift, but are parsed in exactly opposing ways.

The whole hierarchy I'm proposing is illustrated in the attachment below,
with all changes found in the top part above RangeFormation.

— Pyry

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


(Pyry Jahkola) #17

Vladimir,

I don't disagree with you about bitwise operations (please read my email again). I'm saying when it comes to `??`, we don't have the evidence necessary for proposing a breaking change.

Why do you need to see some precedence (…) while this construction is so simple and obviously would be used often and *will* cause bugs. (…)

Let me put it differently: I still haven't seen, nor was I able to conceive myself, a realistic example where `??` would cause problems.

The original motivation by Erica simply failed to compile (…)

Sorry, but you are just not right.

Let me remind you that the original motivation from Erica wasn't about which way `foundIndex ?? lastIndex + 1` goes but about whether `let y = 5 + x ?? 2` compiles at all.

The code compiles without even warning:

let foundIndex : Int? = 1
let lastIndex = 2

let nextIndex = foundIndex ?? lastIndex + 1

It certainly does compile. But is it realistic? Look at the variable names: does this code make any sense at all? What is `lastIndex`? What is `lastIndex + 1`? Why not `things.endIndex`? Indeed, why not:

    let foundIndex: Int? = things.index(of: something)
    let nextIndex = foundIndex.map { $0 + 1 } ?? things.endIndex

That said, I give you the benefit of the doubt – I still believe there can be examples where `??` may be confusing. But without seeing any such examples from the real world, I don't think we have a case for changing `??`.

After I showed the code compiles, would you change your opinion?

No, not with these examples.

* * *

Finally:

IMO the best solution (…) would be to require (…) parenthesis when operators from different groups are mixed in the same expression. IMO no need in complex rules to make Swift safer. I.e. when you have a = 10 + 5 * 10 - you don't need parenthesis, when you have a = b << 4 - also all is ok, but if you have a = b << 4 + 5 * 10 - you'll have a warning until add parenthesis to make your intention clear. (…)

Look, changing how `b << 4 + 5 * 10` is compiled is exactly what I was proposing. With my changes, it would become a compiler error. IMO, it ought to be parenthesised explicitly as either

    (b << 4) + 5 * 10

or

    b << (4 + 5 * 10)

depending on which one was intended.

— Pyry

···

On 08 Sep 2016, at 14:21, Vladimir.S <svabox@gmail.com> wrote:


(Andrey Fidrya) #18

Pyry,

Let me put it differently: I still haven't seen, nor was I able to conceive myself, a realistic example where `??` would cause problems.

An example of broken code from real project:

let allParameters: [String: Any?] =
defaultParameters["sendMessage"] ?? [:] +
parameters +
["chat_id": chat_id, "text": text]

I think that the precedence of ?? is counter-intuitive because it's mainly used as
nil-unwrapping operator and there are often multiple optionals in an expression.
It's too easy to make errors like this one:

let value = x ?? defaultX + y ?? defaultY
let value = x ?? (defaultX + y ?? defaultY)

Imho braces should be required in these expressions.

Even this expression is confusing and in my opinion would only benefit from
braces:
let value = x ?? defaultX + modifier

Also, low precedence of ?? is a source of confusion in other languages, for example:
http://www.codeproject.com/Tips/721145/Beware-The-null-coalescing-operator-is-low-in-the
but in C# this operator isn't used as often as in Swift, so it's not a major issue there.

Andrey

···

On 08 Sep 2016, at 15:20, Pyry Jahkola via swift-evolution <swift-evolution@swift.org> wrote:

Vladimir,

I don't disagree with you about bitwise operations (please read my email again). I'm saying when it comes to `??`, we don't have the evidence necessary for proposing a breaking change.

On 08 Sep 2016, at 14:21, Vladimir.S <svabox@gmail.com <mailto:svabox@gmail.com>> wrote:

Why do you need to see some precedence (…) while this construction is so simple and obviously would be used often and *will* cause bugs. (…)

Let me put it differently: I still haven't seen, nor was I able to conceive myself, a realistic example where `??` would cause problems.

The original motivation by Erica simply failed to compile (…)

Sorry, but you are just not right.

Let me remind you that the original motivation from Erica wasn't about which way `foundIndex ?? lastIndex + 1` goes but about whether `let y = 5 + x ?? 2` compiles at all.

The code compiles without even warning:

let foundIndex : Int? = 1
let lastIndex = 2

let nextIndex = foundIndex ?? lastIndex + 1

It certainly does compile. But is it realistic? Look at the variable names: does this code make any sense at all? What is `lastIndex`? What is `lastIndex + 1`? Why not `things.endIndex`? Indeed, why not:

    let foundIndex: Int? = things.index(of: something)
    let nextIndex = foundIndex.map { $0 + 1 } ?? things.endIndex

That said, I give you the benefit of the doubt – I still believe there can be examples where `??` may be confusing. But without seeing any such examples from the real world, I don't think we have a case for changing `??`.

After I showed the code compiles, would you change your opinion?

No, not with these examples.

* * *

Finally:

IMO the best solution (…) would be to require (…) parenthesis when operators from different groups are mixed in the same expression. IMO no need in complex rules to make Swift safer. I.e. when you have a = 10 + 5 * 10 - you don't need parenthesis, when you have a = b << 4 - also all is ok, but if you have a = b << 4 + 5 * 10 - you'll have a warning until add parenthesis to make your intention clear. (…)

Look, changing how `b << 4 + 5 * 10` is compiled is exactly what I was proposing. With my changes, it would become a compiler error. IMO, it ought to be parenthesised explicitly as either

    (b << 4) + 5 * 10

or

    b << (4 + 5 * 10)

depending on which one was intended.

— Pyry

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