[Proposal] Custom operators


(Anton Zhilin) #1

Swift 2.2 is out, and I restart discussion on syntax for custom operators.
I insist that this time we should focus less on linguistic aspects.

https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md

Introduction

Replace syntax of operator definition:

infix operator <> { precedence 100 associativity left }

With a directive:

#operator(<>, fixity: infix, associativity: left)

Also replace numeric definition of precedence with separate comparative
precedence definitions:

#precedence(+, lessThan: *)
#precedence(+, equalTo: -)

Swift-evolution thread: link to the discussion thread for that proposal
<https://lists.swift.org/pipermail/swift-evolution>
<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#motivation>
Motivation
<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#problems-with-numeric-definition-of-precedence>Problems
with numeric definition of precedence

In the beginning, operators had nice precedence values: 90, 100, 110, 120,
130, 140, 150, 160.

As time went, new and new operators were introduced. Precedence could not
be simply changed, as this would be a breaking change. Ranges got
precedence 135, as got precedence 132. ?? had precedence greater than <,
but less thanas, so it had to be given precedence 131.

Now it is not possible to insert any custom operator between < and ??. It
is an inevitable consequence of current design: it will be impossible to
insert an operator between two existing ones at some point.
<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#problems-with-a-single-precedence-hierarchy>Problems
with a single precedence hierarchy

Currently, if an operator wants to define precedence by comparison to one
operator, it must do so for all other operators.

In many cases, this is not wished. Example: a & b < c is a common error
pattern. a / b as Double is another one. C++ compilers sometimes emit
warnings on these. Swift does not.

The root of the problem is that precedence is defined between all
operators. If & had precedence defined only by comparison to other bitwise
operators and / – only to arithmetic operators, we would have to place
parentheses in such places, not get subtle bugs, and not ever have to look
at the huge operator precedence table.
<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#problems-with-current-operator-definition-syntax>Problems
with current operator definition syntax

Some argue that current operator syntax is not consistent with other
language constructs. Properties of operators have dictionary semantics and
should be defined as such. It is a rather weak argument right now, but
after reworking of precedence, the new syntax will be more to place. More
reasons are given below.
<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#conflicts-of-operator-definitions>Conflicts
of operator definitions

Consider two operator definitions in different modules.

Module A:

infix operator |> { precedence 137 associativity left }

Module B:

infix operator |> { precedence 138 associativity left }

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#proposed-solution>Proposed
solution
<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#change-syntax-for-operator-definition>Change
syntax for operator definition

#operator(<>, fixity: infix, associativity: left)
#operator(!, fixity: postfix)

First parameter of #operator directive is name of the operator. Then goes
required parameter fixity that can be infix,prefix, or postfix. Then, for
infix operators, goes optional associativity parameter that can be left or
right.
<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#comparative-precedence>Comparative
precedence

Remove precedence property from operator definitions. Instead, introduce
#precedence directive:

#precedence(+, lessThan: *)
#precedence(*, equalTo: /)

Omission of parentheses is allowed only when precedence between the two
operators is defined.

1 + 2 * 3 // ok1 + 2 - 3 // error!
#precedence(-, equalTo: +)1 + 2 - 3 // now ok

Precedence equality can only be defined for operators with same
associativity.
<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#conflict-resolution>Conflict
resolution

Precedence rules can be added freely across modules. Ability to omit
parentheses around more operators will not break any code in included
modules. On the other hand, conflicting precedence rules result in an error:

#precedence(*, lessThan: +) // error, previously defined `+` < `*`

Operator definitions do nut cause conflicts, unless they are infix and one
of them has associativity: left, but another one has associativity: right.

#operator(!, fixity: prefix) // ok, duplicated definitions
#operator(<>, fixity: infix)
#operator(<>, fixity: infix, associativity: left) // ok, now left associative
#operator(+, fixity: infix, associativity: right) // error:
associativity conflict

So, if two modules define a custom operator with somewhat similar semantics
(at least associativity), they can be used together. Prefix and postfix
operators can never have conflicts in definitions. If they define different
precedence by comparison to same operators, then, most probably, they had
completely different semantics, and the situation is similar to conflict of
functions.
<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#detailed-design>Detailed
design

operator keyword and local keywords associativity, precedence, left, right will
be removed.

Directives with following (informal) syntax will be added:

#operator(OPERATOR_NAME, fixity: FIXITY)
#operator(OPERATOR_NAME, fixity: infix, associativity: ASSOCIATIVITY)
#precedence(OPERATOR_NAME, lessThan: OPERATOR_NAME)
#precedence(OPERATOR_NAME, equalTo: OPERATOR_NAME)

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#impact-on-existing-code>Impact
on existing code

Standard library operator declarations will need to be rewritten. Some of
the existing precedence rules will need to be rewritten using #precedence
directive.

More importantly, it needs to be discussed what operator precedence rules
do *not* need to be retained.

User defined operators will need to be rewritten as well. But precedence
will have to be defined by the user. Meanwhile, we can automatically insert
parentheses to user code where needed.
<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#alternatives-considered>Alternatives
considered
<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#leave-current-operator-syntax-but-change-precedence>Leave
current operator syntax (but change precedence)

#precedence does not make sense to be defined inside of operator
definition, as it describes relationship of two operators. If so, then we
are left with the following declaration syntax:

prefix operator ! { }infix operator |> { }infix operator <> {
associativity left }

If body of operator can only contain associativity (in some cases), then
the existence of body itself makes no sense.


(Haravikk) #2

Interesting, I like the idea of changing how precedence is defined, but I’m curious how under the new scheme we would go about inserting a new operator unambiguously? For example:

  #precedence(•, lessThan: *)
  #precedence(~, lessThan: *)

Assuming these are defined in separate modules, how do we determine the order of • and ~?

On a related note, I never encounter precedence issues because I always use parenthesis, since I know I’ll just forget the precedence rules so it’d be a mistake for me to rely on them. If we’re adding operators in precedence hierarchies then that only makes it even harder to learn/remember, so I wonder if we might actually be better served by removing precedence entirely? i.e- the compiler would instead require the use of parenthesis to eliminate ambiguity like so:

  let a = 5 + 6 // Correct, as there aren’t enough operators for ambiguity
  let b = 5 + 6 * 7 + 8 // Incorrect, as it relies on precedence to be meaningful
  let c = (5 + 6) * (7 + 8) // Correct, as parenthesis eliminates ambiguity/the need for precedence

This not only eliminates the need to learn, remember and/or lookup precedence, but it’s clearer and avoids mistakes, and IMO it’s actually more readable despite the added noise.

···

On 3 Apr 2016, at 10:36, Антон Жилин via swift-evolution <swift-evolution@swift.org> wrote:

Swift 2.2 is out, and I restart discussion on syntax for custom operators. I insist that this time we should focus less on linguistic aspects.

https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md

Introduction

Replace syntax of operator definition:

infix operator <> { precedence 100 associativity left }
With a directive:

#operator(<>, fixity: infix, associativity: left)
Also replace numeric definition of precedence with separate comparative precedence definitions:

#precedence(+, lessThan: *)
#precedence(+, equalTo: -)
Swift-evolution thread: link to the discussion thread for that proposal <https://lists.swift.org/pipermail/swift-evolution>
<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#motivation>Motivation

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#problems-with-numeric-definition-of-precedence>Problems with numeric definition of precedence

In the beginning, operators had nice precedence values: 90, 100, 110, 120, 130, 140, 150, 160.

As time went, new and new operators were introduced. Precedence could not be simply changed, as this would be a breaking change. Ranges got precedence 135, as got precedence 132. ?? had precedence greater than <, but less thanas, so it had to be given precedence 131.

Now it is not possible to insert any custom operator between < and ??. It is an inevitable consequence of current design: it will be impossible to insert an operator between two existing ones at some point.

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#problems-with-a-single-precedence-hierarchy>Problems with a single precedence hierarchy

Currently, if an operator wants to define precedence by comparison to one operator, it must do so for all other operators.

In many cases, this is not wished. Example: a & b < c is a common error pattern. a / b as Double is another one. C++ compilers sometimes emit warnings on these. Swift does not.

The root of the problem is that precedence is defined between all operators. If & had precedence defined only by comparison to other bitwise operators and / – only to arithmetic operators, we would have to place parentheses in such places, not get subtle bugs, and not ever have to look at the huge operator precedence table.

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#problems-with-current-operator-definition-syntax>Problems with current operator definition syntax

Some argue that current operator syntax is not consistent with other language constructs. Properties of operators have dictionary semantics and should be defined as such. It is a rather weak argument right now, but after reworking of precedence, the new syntax will be more to place. More reasons are given below.

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#conflicts-of-operator-definitions>Conflicts of operator definitions

Consider two operator definitions in different modules.

Module A:

infix operator |> { precedence 137 associativity left }
Module B:

infix operator |> { precedence 138 associativity left }
<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#proposed-solution>Proposed solution

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#change-syntax-for-operator-definition>Change syntax for operator definition

#operator(<>, fixity: infix, associativity: left)
#operator(!, fixity: postfix)
First parameter of #operator directive is name of the operator. Then goes required parameter fixity that can be infix,prefix, or postfix. Then, for infix operators, goes optional associativity parameter that can be left or right.

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#comparative-precedence>Comparative precedence

Remove precedence property from operator definitions. Instead, introduce #precedence directive:

#precedence(+, lessThan: *)
#precedence(*, equalTo: /)
Omission of parentheses is allowed only when precedence between the two operators is defined.

1 + 2 * 3 // ok
1 + 2 - 3 // error!
#precedence(-, equalTo: +)
1 + 2 - 3 // now ok
Precedence equality can only be defined for operators with same associativity.

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#conflict-resolution>Conflict resolution

Precedence rules can be added freely across modules. Ability to omit parentheses around more operators will not break any code in included modules. On the other hand, conflicting precedence rules result in an error:

#precedence(*, lessThan: +) // error, previously defined `+` < `*`
Operator definitions do nut cause conflicts, unless they are infix and one of them has associativity: left, but another one has associativity: right.

#operator(!, fixity: prefix) // ok, duplicated definitions
#operator(<>, fixity: infix)
#operator(<>, fixity: infix, associativity: left) // ok, now left associative
#operator(+, fixity: infix, associativity: right) // error: associativity conflict
So, if two modules define a custom operator with somewhat similar semantics (at least associativity), they can be used together. Prefix and postfix operators can never have conflicts in definitions. If they define different precedence by comparison to same operators, then, most probably, they had completely different semantics, and the situation is similar to conflict of functions.

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#detailed-design>Detailed design

operator keyword and local keywords associativity, precedence, left, right will be removed.

Directives with following (informal) syntax will be added:

#operator(OPERATOR_NAME, fixity: FIXITY)
#operator(OPERATOR_NAME, fixity: infix, associativity: ASSOCIATIVITY)
#precedence(OPERATOR_NAME, lessThan: OPERATOR_NAME)
#precedence(OPERATOR_NAME, equalTo: OPERATOR_NAME)
<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#impact-on-existing-code>Impact on existing code

Standard library operator declarations will need to be rewritten. Some of the existing precedence rules will need to be rewritten using #precedence directive.

More importantly, it needs to be discussed what operator precedence rules do not need to be retained.

User defined operators will need to be rewritten as well. But precedence will have to be defined by the user. Meanwhile, we can automatically insert parentheses to user code where needed.

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#alternatives-considered>Alternatives considered

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#leave-current-operator-syntax-but-change-precedence>Leave current operator syntax (but change precedence)

#precedence does not make sense to be defined inside of operator definition, as it describes relationship of two operators. If so, then we are left with the following declaration syntax:

prefix operator ! { }
infix operator |> { }
infix operator <> { associativity left }
If body of operator can only contain associativity (in some cases), then the existence of body itself makes no sense.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Taras Zakharko) #3

I think this is a great suggestion! One potential problem I can see (if I understood this correctly) is that modules are allowed to set up their own precedence rules for operators defined elsewhere. I think this might lead to some difficult to debug errors if a developer of one module (who is used to certain conventions) then has to work with a different, independent module (where the conventions are different). This is one area where numerical precedence weights seem to be superior as they at least refer to a common subjective coordinate system.

Maybe one should also have visibility for precedence, for instance having precedence module-internal by default?

Best,

— Taras

···

On 03 Apr 2016, at 11:36, Антон Жилин via swift-evolution <swift-evolution@swift.org> wrote:

Swift 2.2 is out, and I restart discussion on syntax for custom operators. I insist that this time we should focus less on linguistic aspects.

https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md

Introduction

Replace syntax of operator definition:

infix operator <> { precedence 100 associativity left }
With a directive:

#operator(<>, fixity: infix, associativity: left)
Also replace numeric definition of precedence with separate comparative precedence definitions:

#precedence(+, lessThan: *)
#precedence(+, equalTo: -)
Swift-evolution thread: link to the discussion thread for that proposal <https://lists.swift.org/pipermail/swift-evolution>
<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#motivation>Motivation

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#problems-with-numeric-definition-of-precedence>Problems with numeric definition of precedence

In the beginning, operators had nice precedence values: 90, 100, 110, 120, 130, 140, 150, 160.

As time went, new and new operators were introduced. Precedence could not be simply changed, as this would be a breaking change. Ranges got precedence 135, as got precedence 132. ?? had precedence greater than <, but less thanas, so it had to be given precedence 131.

Now it is not possible to insert any custom operator between < and ??. It is an inevitable consequence of current design: it will be impossible to insert an operator between two existing ones at some point.

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#problems-with-a-single-precedence-hierarchy>Problems with a single precedence hierarchy

Currently, if an operator wants to define precedence by comparison to one operator, it must do so for all other operators.

In many cases, this is not wished. Example: a & b < c is a common error pattern. a / b as Double is another one. C++ compilers sometimes emit warnings on these. Swift does not.

The root of the problem is that precedence is defined between all operators. If & had precedence defined only by comparison to other bitwise operators and / – only to arithmetic operators, we would have to place parentheses in such places, not get subtle bugs, and not ever have to look at the huge operator precedence table.

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#problems-with-current-operator-definition-syntax>Problems with current operator definition syntax

Some argue that current operator syntax is not consistent with other language constructs. Properties of operators have dictionary semantics and should be defined as such. It is a rather weak argument right now, but after reworking of precedence, the new syntax will be more to place. More reasons are given below.

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#conflicts-of-operator-definitions>Conflicts of operator definitions

Consider two operator definitions in different modules.

Module A:

infix operator |> { precedence 137 associativity left }
Module B:

infix operator |> { precedence 138 associativity left }
<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#proposed-solution>Proposed solution

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#change-syntax-for-operator-definition>Change syntax for operator definition

#operator(<>, fixity: infix, associativity: left)
#operator(!, fixity: postfix)
First parameter of #operator directive is name of the operator. Then goes required parameter fixity that can be infix,prefix, or postfix. Then, for infix operators, goes optional associativity parameter that can be left or right.

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#comparative-precedence>Comparative precedence

Remove precedence property from operator definitions. Instead, introduce #precedence directive:

#precedence(+, lessThan: *)
#precedence(*, equalTo: /)
Omission of parentheses is allowed only when precedence between the two operators is defined.

1 + 2 * 3 // ok
1 + 2 - 3 // error!
#precedence(-, equalTo: +)
1 + 2 - 3 // now ok
Precedence equality can only be defined for operators with same associativity.

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#conflict-resolution>Conflict resolution

Precedence rules can be added freely across modules. Ability to omit parentheses around more operators will not break any code in included modules. On the other hand, conflicting precedence rules result in an error:

#precedence(*, lessThan: +) // error, previously defined `+` < `*`
Operator definitions do nut cause conflicts, unless they are infix and one of them has associativity: left, but another one has associativity: right.

#operator(!, fixity: prefix) // ok, duplicated definitions
#operator(<>, fixity: infix)
#operator(<>, fixity: infix, associativity: left) // ok, now left associative
#operator(+, fixity: infix, associativity: right) // error: associativity conflict
So, if two modules define a custom operator with somewhat similar semantics (at least associativity), they can be used together. Prefix and postfix operators can never have conflicts in definitions. If they define different precedence by comparison to same operators, then, most probably, they had completely different semantics, and the situation is similar to conflict of functions.

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#detailed-design>Detailed design

operator keyword and local keywords associativity, precedence, left, right will be removed.

Directives with following (informal) syntax will be added:

#operator(OPERATOR_NAME, fixity: FIXITY)
#operator(OPERATOR_NAME, fixity: infix, associativity: ASSOCIATIVITY)
#precedence(OPERATOR_NAME, lessThan: OPERATOR_NAME)
#precedence(OPERATOR_NAME, equalTo: OPERATOR_NAME)
<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#impact-on-existing-code>Impact on existing code

Standard library operator declarations will need to be rewritten. Some of the existing precedence rules will need to be rewritten using #precedence directive.

More importantly, it needs to be discussed what operator precedence rules do not need to be retained.

User defined operators will need to be rewritten as well. But precedence will have to be defined by the user. Meanwhile, we can automatically insert parentheses to user code where needed.

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#alternatives-considered>Alternatives considered

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#leave-current-operator-syntax-but-change-precedence>Leave current operator syntax (but change precedence)

#precedence does not make sense to be defined inside of operator definition, as it describes relationship of two operators. If so, then we are left with the following declaration syntax:

prefix operator ! { }
infix operator |> { }
infix operator <> { associativity left }
If body of operator can only contain associativity (in some cases), then the existence of body itself makes no sense.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Anton Zhilin) #4

Assuming these are defined in separate modules, how do we determine the

order of • and ~?

By default, priority between any two operators is undefined. If two modules
don't know about each other, but the user wishes to prioritize them, then
he will write:

#precedence(•, lessThan: ~)

If • suddenly wishes to cooperate with ~, then it will add directives:

#operator(~, fixity: infix, associativity: left)
#precedence(•, lessThan: ~)

It doesn't matter if ~ or user have already added them: if they do not
contain contradictory information, there will be no conflict.

On a related note, I never encounter precedence issues because I always

use parenthesis, since I know I’ll just forget the precedence rules so it’d
be a mistake for me to rely on them. If we’re adding operators in
precedence hierarchies then that only makes it even harder to
learn/remember, so I wonder if we might actually be better served by
removing precedence entirely?

That's exactly what I'm trying to do! I propose that there should be no
hierarchy. Each pair of operators will have to define priority explicitly,
if they need it.

I'll give an example of what I mean by "removing hierarchy":

#precedence(+, lessThan: *)
#precedence(*, lessThan: ^)
2 ^ 3 + 6 # error
#precedence(+, lessThan: ^)
2 ^ 3 + 6 # now ok

- Anton

···

2016-04-03 13:31 GMT+03:00 Haravikk <swift-evolution@haravikk.me>:

Interesting, I like the idea of changing how precedence is defined, but
I’m curious how under the new scheme we would go about inserting a new
operator unambiguously? For example:

#precedence(•, lessThan: *)
#precedence(~, lessThan: *)

Assuming these are defined in separate modules, how do we determine the
order of • and ~?

On a related note, I never encounter precedence issues because I always
use parenthesis, since I know I’ll just forget the precedence rules so it’d
be a mistake for me to rely on them. If we’re adding operators in
precedence hierarchies then that only makes it even harder to
learn/remember, so I wonder if we might actually be better served by
removing precedence entirely? i.e- the compiler would instead require the
use of parenthesis to eliminate ambiguity like so:

let a = 5 + 6 // Correct, as there aren’t enough operators for ambiguity
let b = 5 + 6 * 7 + 8 // Incorrect, as it relies on precedence to be
meaningful
let c = (5 + 6) * (7 + 8) // Correct, as parenthesis eliminates
ambiguity/the need for precedence

This not only eliminates the need to learn, remember and/or lookup
precedence, but it’s clearer and avoids mistakes, and IMO it’s actually
more readable despite the added noise.

On 3 Apr 2016, at 10:36, Антон Жилин via swift-evolution < > swift-evolution@swift.org> wrote:

Swift 2.2 is out, and I restart discussion on syntax for custom operators.
I insist that this time we should focus less on linguistic aspects.

https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md

Introduction

Replace syntax of operator definition:

infix operator <> { precedence 100 associativity left }

With a directive:

#operator(<>, fixity: infix, associativity: left)

Also replace numeric definition of precedence with separate comparative
precedence definitions:

#precedence(+, lessThan: *)
#precedence(+, equalTo: -)

Swift-evolution thread: link to the discussion thread for that proposal
<https://lists.swift.org/pipermail/swift-evolution>

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#motivation>
Motivation
<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#problems-with-numeric-definition-of-precedence>Problems
with numeric definition of precedence

In the beginning, operators had nice precedence values: 90, 100, 110, 120,
130, 140, 150, 160.

As time went, new and new operators were introduced. Precedence could not
be simply changed, as this would be a breaking change. Ranges got
precedence 135, as got precedence 132. ?? had precedence greater than <,
but less thanas, so it had to be given precedence 131.

Now it is not possible to insert any custom operator between < and ??. It
is an inevitable consequence of current design: it will be impossible to
insert an operator between two existing ones at some point.

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#problems-with-a-single-precedence-hierarchy>Problems
with a single precedence hierarchy

Currently, if an operator wants to define precedence by comparison to one
operator, it must do so for all other operators.

In many cases, this is not wished. Example: a & b < c is a common error
pattern. a / b as Double is another one. C++ compilers sometimes emit
warnings on these. Swift does not.

The root of the problem is that precedence is defined between all
operators. If & had precedence defined only by comparison to other
bitwise operators and / – only to arithmetic operators, we would have to
place parentheses in such places, not get subtle bugs, and not ever have to
look at the huge operator precedence table.

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#problems-with-current-operator-definition-syntax>Problems
with current operator definition syntax

Some argue that current operator syntax is not consistent with other
language constructs. Properties of operators have dictionary semantics and
should be defined as such. It is a rather weak argument right now, but
after reworking of precedence, the new syntax will be more to place. More
reasons are given below.

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#conflicts-of-operator-definitions>Conflicts
of operator definitions

Consider two operator definitions in different modules.

Module A:

infix operator |> { precedence 137 associativity left }

Module B:

infix operator |> { precedence 138 associativity left }

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#proposed-solution>Proposed
solution
<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#change-syntax-for-operator-definition>Change
syntax for operator definition

#operator(<>, fixity: infix, associativity: left)
#operator(!, fixity: postfix)

First parameter of #operator directive is name of the operator. Then goes
required parameter fixity that can be infix,prefix, or postfix. Then, for
infix operators, goes optional associativity parameter that can be left
or right.

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#comparative-precedence>Comparative
precedence

Remove precedence property from operator definitions. Instead, introduce
#precedence directive:

#precedence(+, lessThan: *)
#precedence(*, equalTo: /)

Omission of parentheses is allowed only when precedence between the two
operators is defined.

1 + 2 * 3 // ok1 + 2 - 3 // error!
#precedence(-, equalTo: +)1 + 2 - 3 // now ok

Precedence equality can only be defined for operators with same
associativity.

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#conflict-resolution>Conflict
resolution

Precedence rules can be added freely across modules. Ability to omit
parentheses around more operators will not break any code in included
modules. On the other hand, conflicting precedence rules result in an error:

#precedence(*, lessThan: +) // error, previously defined `+` < `*`

Operator definitions do nut cause conflicts, unless they are infix and
one of them has associativity: left, but another one has associativity:
right.

#operator(!, fixity: prefix) // ok, duplicated definitions
#operator(<>, fixity: infix)
#operator(<>, fixity: infix, associativity: left) // ok, now left associative
#operator(+, fixity: infix, associativity: right) // error: associativity conflict

So, if two modules define a custom operator with somewhat similar
semantics (at least associativity), they can be used together. Prefix and
postfix operators can never have conflicts in definitions. If they define
different precedence by comparison to same operators, then, most probably,
they had completely different semantics, and the situation is similar to
conflict of functions.

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#detailed-design>Detailed
design

operator keyword and local keywords associativity, precedence, left, right will
be removed.

Directives with following (informal) syntax will be added:

#operator(OPERATOR_NAME, fixity: FIXITY)
#operator(OPERATOR_NAME, fixity: infix, associativity: ASSOCIATIVITY)
#precedence(OPERATOR_NAME, lessThan: OPERATOR_NAME)
#precedence(OPERATOR_NAME, equalTo: OPERATOR_NAME)

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#impact-on-existing-code>Impact
on existing code

Standard library operator declarations will need to be rewritten. Some of
the existing precedence rules will need to be rewritten using #precedence
directive.

More importantly, it needs to be discussed what operator precedence rules
do *not* need to be retained.

User defined operators will need to be rewritten as well. But precedence
will have to be defined by the user. Meanwhile, we can automatically insert
parentheses to user code where needed.

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#alternatives-considered>Alternatives
considered
<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#leave-current-operator-syntax-but-change-precedence>Leave
current operator syntax (but change precedence)

#precedence does not make sense to be defined inside of operator
definition, as it describes relationship of two operators. If so, then we
are left with the following declaration syntax:

prefix operator ! { }infix operator |> { }infix operator <> { associativity left }

If body of operator can only contain associativity (in some cases), then
the existence of body itself makes no sense.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Ben Rimmington) #5

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/
NNNN-operator-precedence.md>

Another way to eliminate numerical precedence is by using keywords:

infix operator << {
    associativity none
    exponentiative
}

infix operator * {
    associativity left
    multiplicative
}

infix operator + {
    associativity left
    additive
}

infix operator == {
    associativity none
    comparative
}

infix operator += {
    associativity right
    assignment
}

All operators would need to use one of the existing precedence groups (Table 2):

<https://developer.apple.com/library/ios/documentation/Swift/Reference/
Swift_StandardLibrary_Operators/>

-- Ben


(David Waite) #6

Interesting model!

If I understand correctly: this changes the precedence from being based on a numeric value, to being represented as a bit of a DAG of precedence groups. A precedence group is defined implicitly for each operator, with one group around each set of operators where equalTo has been declared.

The groups are lazily evaluated, so if an expression can be resolved without ambiguity due to lack of reachability between two individual operators in the DAG, there is no issue/error.

Comments:
- I wonder if there are cases in the standard operators which would be better modeled as a non-linear chain. The compiler could warn around usage which is defined by operator precedence, but is commonly considered ambiguous or error prone.

For example, if users commonly get confused about the conjunctive and disjunctive levels (logical ‘and’ and ‘or’) being different levels with precedence, you could just omit the lessThan relationship between the two of them. The compiler would then error on ambiguous cases, prompting the user to use parenthesis.

- I’d prefer instead of operator precedence groups just being implicit by use of #precedence equalTo, that the operators are bound to a group explicitly. Something like
#precedence(+, group: “additive”)
#precedence(“additive”, lessThan: “multiplicative”)

However, this may create more issues than it solves (two frameworks creating their own custom operators, putting them in custom precedence groups, and the consumer decides the two precedence groups are really equivalent)

-DW

···

On Apr 3, 2016, at 3:36 AM, Антон Жилин via swift-evolution <swift-evolution@swift.org> wrote:

Swift 2.2 is out, and I restart discussion on syntax for custom operators. I insist that this time we should focus less on linguistic aspects.

https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md


(Daniel Duan) #7

Антон Жилин via swift-evolution <swift-evolution@...> writes:

Swift 2.2 is out, and I restart discussion on syntax for custom operators.
I insist that this time we should focus less on linguistic aspects.

I have a few thoughts and a question regarding precedence:

1. I strongly agree that the numeric precedence system is not great. From
    a implementation point of view, the way to specify them in your proposal
    essentially gave all visible operators a partial order, in which we can
    draw a directed gragh with operators being the nodes and their relation
    being arcs. A part of the graph might look like: '^' --> '*' --> '+', the
    nodes being the math operators. We can tell '*' has a higher precedence
    than '+', '^' has a higher precedence than '*' and '+', by follwing the
    arcs. If one operator is not reachable from another, and vice versa, then
    composing these two is illegal. We need to teach the compiler this concept.

2. Currently, it's not possible to specify precedence for pre- and postfix
    operators. Chris Lattner has mentioned that the
    following result is not desirable:

        ∆x + y

    … where ∆ has a lower precendence than + while it's required to have no
    space between ∆ and the operand. My understanding is that if spaces were
    to be allowed here, parsing such expression without ambiguity is a
    non-trivial challenge. So, will it be possible to specify precedence for
    pre/postfix operators under your proposal?

3. It may be a good exercise to work out how would each of the builtin
    operators would be defined with this change and mention it (not the entire
    definition, but the fact that it's possible, or reasons why it produces
    any difference) in the proposal.

- Daniel


(Félix Cloutier) #8

That doesn't sound future-proof. If it was in place and Range hadn't existed since the earliest Swift public release, it wouldn't be possible to define its precedence now.

Félix

···

Le 3 avr. 2016 à 11:41:00, Ben Rimmington via swift-evolution <swift-evolution@swift.org> a écrit :

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/
NNNN-operator-precedence.md>

Another way to eliminate numerical precedence is by using keywords:

infix operator << {
   associativity none
   exponentiative
}

infix operator * {
   associativity left
   multiplicative
}

infix operator + {
   associativity left
   additive
}

infix operator == {
   associativity none
   comparative
}

infix operator += {
   associativity right
   assignment
}

All operators would need to use one of the existing precedence groups (Table 2):

<https://developer.apple.com/library/ios/documentation/Swift/Reference/
Swift_StandardLibrary_Operators/>

-- Ben

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


(Haravikk) #9

Ah, I misunderstood then, so a warning/error will be raised if no precedence exists? Thanks for the explanation! I’ll probably still favour overkill brackets, but this makes sense.

In any event I’m on +1 for the more standardised syntax and the switch to using #directives, makes a lot of sense since these are really just customisable compiler symbols.

···

On 3 Apr 2016, at 11:45, Антон Жилин <antonyzhilin@gmail.com> wrote:

> Assuming these are defined in separate modules, how do we determine the order of • and ~?

By default, priority between any two operators is undefined. If two modules don't know about each other, but the user wishes to prioritize them, then he will write:

#precedence(•, lessThan: ~)

If • suddenly wishes to cooperate with ~, then it will add directives:

#operator(~, fixity: infix, associativity: left)
#precedence(•, lessThan: ~)

It doesn't matter if ~ or user have already added them: if they do not contain contradictory information, there will be no conflict.


(Ross O'Brien) #10

There is a problem here of duplicated operators or custom precedence, and
how that gets passed between modules.
Assume there are three modules, A, B and C. B defines a custom operator **.
A and C each define a custom operator ++, and their meanings are different
(though, even if their meanings were the same, I'm not sure if they could
unify).

Module D uses A and B as dependencies and sets a custom precedence on ++
and **. Module E uses B and C and has a different precedence on ++ and **.
You're working on Module F which uses D and E. Which ++ and which
precedence does F get implicitly?

I'm wondering whether we can treat operators the way we recently decided to
treat selectors: if there is an ambiguity, it should be possible not just
to specify which module they came from, but their fixity or argument types.
If module D decides that '++' should refer to 'traditional postfix number
incrementation', and F decides that it should be an infix 'conjoin two
numbers as a string and turn the result into a number (e.g. 5 ++ 4 -> 54)'
then a #selector-like operator signature would come in really handy.

···

On Sun, Apr 3, 2016 at 12:10 PM, Taras Zakharko via swift-evolution < swift-evolution@swift.org> wrote:

I think this is a great suggestion! One potential problem I can see (if I
understood this correctly) is that modules are allowed to set up their own
precedence rules for operators defined elsewhere. I think this might lead
to some difficult to debug errors if a developer of one module (who is used
to certain conventions) then has to work with a different, independent
module (where the conventions are different). This is one area where
numerical precedence weights seem to be superior as they at least refer to
a common subjective coordinate system.

Maybe one should also have visibility for precedence, for instance having
precedence module-internal by default?

Best,

— Taras

On 03 Apr 2016, at 11:36, Антон Жилин via swift-evolution < > swift-evolution@swift.org> wrote:

Swift 2.2 is out, and I restart discussion on syntax for custom operators.
I insist that this time we should focus less on linguistic aspects.

https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md

Introduction

Replace syntax of operator definition:

infix operator <> { precedence 100 associativity left }

With a directive:

#operator(<>, fixity: infix, associativity: left)

Also replace numeric definition of precedence with separate comparative
precedence definitions:

#precedence(+, lessThan: *)
#precedence(+, equalTo: -)

Swift-evolution thread: link to the discussion thread for that proposal
<https://lists.swift.org/pipermail/swift-evolution>

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#motivation>
Motivation
<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#problems-with-numeric-definition-of-precedence>Problems
with numeric definition of precedence

In the beginning, operators had nice precedence values: 90, 100, 110, 120,
130, 140, 150, 160.

As time went, new and new operators were introduced. Precedence could not
be simply changed, as this would be a breaking change. Ranges got
precedence 135, as got precedence 132. ?? had precedence greater than <,
but less thanas, so it had to be given precedence 131.

Now it is not possible to insert any custom operator between < and ??. It
is an inevitable consequence of current design: it will be impossible to
insert an operator between two existing ones at some point.

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#problems-with-a-single-precedence-hierarchy>Problems
with a single precedence hierarchy

Currently, if an operator wants to define precedence by comparison to one
operator, it must do so for all other operators.

In many cases, this is not wished. Example: a & b < c is a common error
pattern. a / b as Double is another one. C++ compilers sometimes emit
warnings on these. Swift does not.

The root of the problem is that precedence is defined between all
operators. If & had precedence defined only by comparison to other
bitwise operators and / – only to arithmetic operators, we would have to
place parentheses in such places, not get subtle bugs, and not ever have to
look at the huge operator precedence table.

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#problems-with-current-operator-definition-syntax>Problems
with current operator definition syntax

Some argue that current operator syntax is not consistent with other
language constructs. Properties of operators have dictionary semantics and
should be defined as such. It is a rather weak argument right now, but
after reworking of precedence, the new syntax will be more to place. More
reasons are given below.

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#conflicts-of-operator-definitions>Conflicts
of operator definitions

Consider two operator definitions in different modules.

Module A:

infix operator |> { precedence 137 associativity left }

Module B:

infix operator |> { precedence 138 associativity left }

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#proposed-solution>Proposed
solution
<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#change-syntax-for-operator-definition>Change
syntax for operator definition

#operator(<>, fixity: infix, associativity: left)
#operator(!, fixity: postfix)

First parameter of #operator directive is name of the operator. Then goes
required parameter fixity that can be infix,prefix, or postfix. Then, for
infix operators, goes optional associativity parameter that can be left
or right.

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#comparative-precedence>Comparative
precedence

Remove precedence property from operator definitions. Instead, introduce
#precedence directive:

#precedence(+, lessThan: *)
#precedence(*, equalTo: /)

Omission of parentheses is allowed only when precedence between the two
operators is defined.

1 + 2 * 3 // ok1 + 2 - 3 // error!
#precedence(-, equalTo: +)1 + 2 - 3 // now ok

Precedence equality can only be defined for operators with same
associativity.

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#conflict-resolution>Conflict
resolution

Precedence rules can be added freely across modules. Ability to omit
parentheses around more operators will not break any code in included
modules. On the other hand, conflicting precedence rules result in an error:

#precedence(*, lessThan: +) // error, previously defined `+` < `*`

Operator definitions do nut cause conflicts, unless they are infix and
one of them has associativity: left, but another one has associativity:
right.

#operator(!, fixity: prefix) // ok, duplicated definitions
#operator(<>, fixity: infix)
#operator(<>, fixity: infix, associativity: left) // ok, now left associative
#operator(+, fixity: infix, associativity: right) // error: associativity conflict

So, if two modules define a custom operator with somewhat similar
semantics (at least associativity), they can be used together. Prefix and
postfix operators can never have conflicts in definitions. If they define
different precedence by comparison to same operators, then, most probably,
they had completely different semantics, and the situation is similar to
conflict of functions.

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#detailed-design>Detailed
design

operator keyword and local keywords associativity, precedence, left, right will
be removed.

Directives with following (informal) syntax will be added:

#operator(OPERATOR_NAME, fixity: FIXITY)
#operator(OPERATOR_NAME, fixity: infix, associativity: ASSOCIATIVITY)
#precedence(OPERATOR_NAME, lessThan: OPERATOR_NAME)
#precedence(OPERATOR_NAME, equalTo: OPERATOR_NAME)

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#impact-on-existing-code>Impact
on existing code

Standard library operator declarations will need to be rewritten. Some of
the existing precedence rules will need to be rewritten using #precedence
directive.

More importantly, it needs to be discussed what operator precedence rules
do *not* need to be retained.

User defined operators will need to be rewritten as well. But precedence
will have to be defined by the user. Meanwhile, we can automatically insert
parentheses to user code where needed.

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#alternatives-considered>Alternatives
considered
<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#leave-current-operator-syntax-but-change-precedence>Leave
current operator syntax (but change precedence)

#precedence does not make sense to be defined inside of operator
definition, as it describes relationship of two operators. If so, then we
are left with the following declaration syntax:

prefix operator ! { }infix operator |> { }infix operator <> { associativity left }

If body of operator can only contain associativity (in some cases), then
the existence of body itself makes no sense.
_______________________________________________
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


(James Campbell) #11

In these case of module operator conflicts perhaps there could be a way we
could import these operators ?

Likes a #importOperators(OtherModule) or some kind of build flag.

···

*___________________________________*

*James⎥Future Prime Minister*

*james@supmenow.com <james@supmenow.com>⎥supmenow.com <http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On Sun, Apr 3, 2016 at 12:26 PM, Ross O'Brien via swift-evolution < swift-evolution@swift.org> wrote:

There is a problem here of duplicated operators or custom precedence, and
how that gets passed between modules.
Assume there are three modules, A, B and C. B defines a custom operator
**. A and C each define a custom operator ++, and their meanings are
different (though, even if their meanings were the same, I'm not sure if
they could unify).

Module D uses A and B as dependencies and sets a custom precedence on ++
and **. Module E uses B and C and has a different precedence on ++ and **.
You're working on Module F which uses D and E. Which ++ and which
precedence does F get implicitly?

I'm wondering whether we can treat operators the way we recently decided
to treat selectors: if there is an ambiguity, it should be possible not
just to specify which module they came from, but their fixity or argument
types. If module D decides that '++' should refer to 'traditional postfix
number incrementation', and F decides that it should be an infix 'conjoin
two numbers as a string and turn the result into a number (e.g. 5 ++ 4 ->
54)' then a #selector-like operator signature would come in really handy.

On Sun, Apr 3, 2016 at 12:10 PM, Taras Zakharko via swift-evolution < > swift-evolution@swift.org> wrote:

I think this is a great suggestion! One potential problem I can see (if I
understood this correctly) is that modules are allowed to set up their own
precedence rules for operators defined elsewhere. I think this might lead
to some difficult to debug errors if a developer of one module (who is used
to certain conventions) then has to work with a different, independent
module (where the conventions are different). This is one area where
numerical precedence weights seem to be superior as they at least refer to
a common subjective coordinate system.

Maybe one should also have visibility for precedence, for instance having
precedence module-internal by default?

Best,

— Taras

On 03 Apr 2016, at 11:36, Антон Жилин via swift-evolution < >> swift-evolution@swift.org> wrote:

Swift 2.2 is out, and I restart discussion on syntax for custom
operators. I insist that this time we should focus less on linguistic
aspects.

https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md

Introduction

Replace syntax of operator definition:

infix operator <> { precedence 100 associativity left }

With a directive:

#operator(<>, fixity: infix, associativity: left)

Also replace numeric definition of precedence with separate comparative
precedence definitions:

#precedence(+, lessThan: *)
#precedence(+, equalTo: -)

Swift-evolution thread: link to the discussion thread for that proposal
<https://lists.swift.org/pipermail/swift-evolution>

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#motivation>
Motivation
<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#problems-with-numeric-definition-of-precedence>Problems
with numeric definition of precedence

In the beginning, operators had nice precedence values: 90, 100, 110,
120, 130, 140, 150, 160.

As time went, new and new operators were introduced. Precedence could not
be simply changed, as this would be a breaking change. Ranges got
precedence 135, as got precedence 132. ?? had precedence greater than <,
but less thanas, so it had to be given precedence 131.

Now it is not possible to insert any custom operator between < and ??.
It is an inevitable consequence of current design: it will be impossible to
insert an operator between two existing ones at some point.

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#problems-with-a-single-precedence-hierarchy>Problems
with a single precedence hierarchy

Currently, if an operator wants to define precedence by comparison to one
operator, it must do so for all other operators.

In many cases, this is not wished. Example: a & b < c is a common error
pattern. a / b as Double is another one. C++ compilers sometimes emit
warnings on these. Swift does not.

The root of the problem is that precedence is defined between all
operators. If & had precedence defined only by comparison to other
bitwise operators and / – only to arithmetic operators, we would have to
place parentheses in such places, not get subtle bugs, and not ever have to
look at the huge operator precedence table.

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#problems-with-current-operator-definition-syntax>Problems
with current operator definition syntax

Some argue that current operator syntax is not consistent with other
language constructs. Properties of operators have dictionary semantics and
should be defined as such. It is a rather weak argument right now, but
after reworking of precedence, the new syntax will be more to place. More
reasons are given below.

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#conflicts-of-operator-definitions>Conflicts
of operator definitions

Consider two operator definitions in different modules.

Module A:

infix operator |> { precedence 137 associativity left }

Module B:

infix operator |> { precedence 138 associativity left }

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#proposed-solution>Proposed
solution
<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#change-syntax-for-operator-definition>Change
syntax for operator definition

#operator(<>, fixity: infix, associativity: left)
#operator(!, fixity: postfix)

First parameter of #operator directive is name of the operator. Then
goes required parameter fixity that can be infix,prefix, or postfix.
Then, for infix operators, goes optional associativity parameter that
can be left or right.

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#comparative-precedence>Comparative
precedence

Remove precedence property from operator definitions. Instead, introduce
#precedence directive:

#precedence(+, lessThan: *)
#precedence(*, equalTo: /)

Omission of parentheses is allowed only when precedence between the two
operators is defined.

1 + 2 * 3 // ok1 + 2 - 3 // error!
#precedence(-, equalTo: +)1 + 2 - 3 // now ok

Precedence equality can only be defined for operators with same
associativity.

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#conflict-resolution>Conflict
resolution

Precedence rules can be added freely across modules. Ability to omit
parentheses around more operators will not break any code in included
modules. On the other hand, conflicting precedence rules result in an error:

#precedence(*, lessThan: +) // error, previously defined `+` < `*`

Operator definitions do nut cause conflicts, unless they are infix and
one of them has associativity: left, but another one has associativity:
right.

#operator(!, fixity: prefix) // ok, duplicated definitions
#operator(<>, fixity: infix)
#operator(<>, fixity: infix, associativity: left) // ok, now left associative
#operator(+, fixity: infix, associativity: right) // error: associativity conflict

So, if two modules define a custom operator with somewhat similar
semantics (at least associativity), they can be used together. Prefix and
postfix operators can never have conflicts in definitions. If they define
different precedence by comparison to same operators, then, most probably,
they had completely different semantics, and the situation is similar to
conflict of functions.

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#detailed-design>Detailed
design

operator keyword and local keywords associativity, precedence, left,
right will be removed.

Directives with following (informal) syntax will be added:

#operator(OPERATOR_NAME, fixity: FIXITY)
#operator(OPERATOR_NAME, fixity: infix, associativity: ASSOCIATIVITY)
#precedence(OPERATOR_NAME, lessThan: OPERATOR_NAME)
#precedence(OPERATOR_NAME, equalTo: OPERATOR_NAME)

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#impact-on-existing-code>Impact
on existing code

Standard library operator declarations will need to be rewritten. Some of
the existing precedence rules will need to be rewritten using #precedence
directive.

More importantly, it needs to be discussed what operator precedence rules
do *not* need to be retained.

User defined operators will need to be rewritten as well. But precedence
will have to be defined by the user. Meanwhile, we can automatically insert
parentheses to user code where needed.

<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#alternatives-considered>Alternatives
considered
<https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md#leave-current-operator-syntax-but-change-precedence>Leave
current operator syntax (but change precedence)

#precedence does not make sense to be defined inside of operator
definition, as it describes relationship of two operators. If so, then we
are left with the following declaration syntax:

prefix operator ! { }infix operator |> { }infix operator <> { associativity left }

If body of operator can only contain associativity (in some cases), then
the existence of body itself makes no sense.
_______________________________________________
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

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


(Anton Zhilin) #12

Right, the proposal in its current form does not really aim to resolve such
conflicts, although it makes them occur less often by "merging" precedence
and associativity when possible.

At first, I also tried to resolve such conflicts by naming operators. But I
found that compiler instructions will be too complex in some cases:

#operator(+, name: numericAdd, fixity: infix, associativity: left)
#operator(+, name: arrayAppend, fixity: infix, associativity: right)

func numericAdd(left: A, right: B) -> C
func numericAdd(left: B, right: C) -> B
func arrayAppend(left: A, right: A) -> B
func arrayAppend(left: B, right: C) -> A

A() + B() + C()

Compiler will have to find by brute-force that this expression can only
represent arrayAppend(A(), arrayAppend(B(), C())), and others result in
errors. To me, this scheme seems pretty absurd.

That said, it might be a good idea to add visibility for operators.
Hiding already imported operators, on the other hand, would be inconsistent
with othr elements of the language. It is currently impossible to hide
contents of module A from C where C imports B and B imports A. Not that I'm
against such a feature in general.

- Anton

···

2016-04-03 14:26 GMT+03:00 Ross O'Brien <narrativium+swift@gmail.com>:

There is a problem here of duplicated operators or custom precedence, and
how that gets passed between modules.
Assume there are three modules, A, B and C. B defines a custom operator
**. A and C each define a custom operator ++, and their meanings are
different (though, even if their meanings were the same, I'm not sure if
they could unify).

Module D uses A and B as dependencies and sets a custom precedence on ++
and **. Module E uses B and C and has a different precedence on ++ and **.
You're working on Module F which uses D and E. Which ++ and which
precedence does F get implicitly?

I'm wondering whether we can treat operators the way we recently decided
to treat selectors: if there is an ambiguity, it should be possible not
just to specify which module they came from, but their fixity or argument
types. If module D decides that '++' should refer to 'traditional postfix
number incrementation', and F decides that it should be an infix 'conjoin
two numbers as a string and turn the result into a number (e.g. 5 ++ 4 ->
54)' then a #selector-like operator signature would come in really handy.


#13

See inline

There is a problem here of duplicated operators or custom precedence, and how that gets passed between modules.
Assume there are three modules, A, B and C. B defines a custom operator **. A and C each define a custom operator ++, and their meanings are different (though, even if their meanings were the same, I'm not sure if they could unify).

Module D uses A and B as dependencies and sets a custom precedence on ++ and **. Module E uses B and C and has a different precedence on ++ and **. You're working on Module F which uses D and E. Which ++ and which precedence does F get implicitly?

We could allow operator precedence overriding to resolve ambiguity. However this overriding should only be module internal since it would override the existing precedences in the other modules.

@AHTOH
Why do you use #keyword ?
I think defining a operator with

    infix operator + {
         associativity: left
    }

is perfectly fine since it is similar to class/struct/enum declaration.

    // and it's precedence
    precedence(+ lessThan *)

Note the missing "," and ":" before and after "lessThan" in order to give both operators the same importance (minor issue).

I feel that

    #precedence(+, lessThan: *)

puts too much importance on the first operator.

Best regards
- Maximilian

···

Am 03.04.2016 um 13:26 schrieb Ross O'Brien via swift-evolution <swift-evolution@swift.org>:

I'm wondering whether we can treat operators the way we recently decided to treat selectors: if there is an ambiguity, it should be possible not just to specify which module they came from, but their fixity or argument types. If module D decides that '++' should refer to 'traditional postfix number incrementation', and F decides that it should be an infix 'conjoin two numbers as a string and turn the result into a number (e.g. 5 ++ 4 -> 54)' then a #selector-like operator signature would come in really handy.

On Sun, Apr 3, 2016 at 12:10 PM, Taras Zakharko via swift-evolution <swift-evolution@swift.org> wrote:
I think this is a great suggestion! One potential problem I can see (if I understood this correctly) is that modules are allowed to set up their own precedence rules for operators defined elsewhere. I think this might lead to some difficult to debug errors if a developer of one module (who is used to certain conventions) then has to work with a different, independent module (where the conventions are different). This is one area where numerical precedence weights seem to be superior as they at least refer to a common subjective coordinate system.

Maybe one should also have visibility for precedence, for instance having precedence module-internal by default?

Best,

— Taras

On 03 Apr 2016, at 11:36, Антон Жилин via swift-evolution <swift-evolution@swift.org> wrote:

Swift 2.2 is out, and I restart discussion on syntax for custom operators. I insist that this time we should focus less on linguistic aspects.

https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md

Introduction

Replace syntax of operator definition:

infix operator <> { precedence 100 associativity left }
With a directive:

#operator(<>, fixity: infix, associativity: left)
Also replace numeric definition of precedence with separate comparative precedence definitions:

#precedence(+, lessThan: *)
#precedence(+, equalTo: -)
Swift-evolution thread: link to the discussion thread for that proposal

Motivation

Problems with numeric definition of precedence

In the beginning, operators had nice precedence values: 90, 100, 110, 120, 130, 140, 150, 160.

As time went, new and new operators were introduced. Precedence could not be simply changed, as this would be a breaking change. Ranges got precedence 135, as got precedence 132. ?? had precedence greater than <, but less thanas, so it had to be given precedence 131.

Now it is not possible to insert any custom operator between < and ??. It is an inevitable consequence of current design: it will be impossible to insert an operator between two existing ones at some point.

Problems with a single precedence hierarchy

Currently, if an operator wants to define precedence by comparison to one operator, it must do so for all other operators.

In many cases, this is not wished. Example: a & b < c is a common error pattern. a / b as Double is another one. C++ compilers sometimes emit warnings on these. Swift does not.

The root of the problem is that precedence is defined between all operators. If & had precedence defined only by comparison to other bitwise operators and / – only to arithmetic operators, we would have to place parentheses in such places, not get subtle bugs, and not ever have to look at the huge operator precedence table.

Problems with current operator definition syntax

Some argue that current operator syntax is not consistent with other language constructs. Properties of operators have dictionary semantics and should be defined as such. It is a rather weak argument right now, but after reworking of precedence, the new syntax will be more to place. More reasons are given below.

Conflicts of operator definitions

Consider two operator definitions in different modules.

Module A:

infix operator |> { precedence 137 associativity left }
Module B:

infix operator |> { precedence 138 associativity left }
Proposed solution

Change syntax for operator definition

#operator(<>, fixity: infix, associativity: left)
#operator(!, fixity: postfix)
First parameter of #operator directive is name of the operator. Then goes required parameter fixity that can be infix,prefix, or postfix. Then, for infix operators, goes optional associativity parameter that can be left or right.

Comparative precedence

Remove precedence property from operator definitions. Instead, introduce #precedence directive:

#precedence(+, lessThan: *)
#precedence(*, equalTo: /)
Omission of parentheses is allowed only when precedence between the two operators is defined.

1 + 2 * 3 // ok
1 + 2 - 3 // error!
#precedence(-, equalTo: +)
1 + 2 - 3 // now ok
Precedence equality can only be defined for operators with same associativity.

Conflict resolution

Precedence rules can be added freely across modules. Ability to omit parentheses around more operators will not break any code in included modules. On the other hand, conflicting precedence rules result in an error:

#precedence(*, lessThan: +) // error, previously defined `+` < `*`
Operator definitions do nut cause conflicts, unless they are infix and one of them has associativity: left, but another one has associativity: right.

#operator(!, fixity: prefix) // ok, duplicated definitions
#operator(<>, fixity: infix)
#operator(<>, fixity: infix, associativity: left) // ok, now left associative
#operator(+, fixity: infix, associativity: right) // error: associativity conflict
So, if two modules define a custom operator with somewhat similar semantics (at least associativity), they can be used together. Prefix and postfix operators can never have conflicts in definitions. If they define different precedence by comparison to same operators, then, most probably, they had completely different semantics, and the situation is similar to conflict of functions.

Detailed design

operator keyword and local keywords associativity, precedence, left, right will be removed.

Directives with following (informal) syntax will be added:

#operator(OPERATOR_NAME, fixity: FIXITY)
#operator(OPERATOR_NAME, fixity: infix, associativity: ASSOCIATIVITY)
#precedence(OPERATOR_NAME, lessThan: OPERATOR_NAME)
#precedence(OPERATOR_NAME, equalTo: OPERATOR_NAME)
Impact on existing code

Standard library operator declarations will need to be rewritten. Some of the existing precedence rules will need to be rewritten using #precedence directive.

More importantly, it needs to be discussed what operator precedence rules do not need to be retained.

User defined operators will need to be rewritten as well. But precedence will have to be defined by the user. Meanwhile, we can automatically insert parentheses to user code where needed.

Alternatives considered

Leave current operator syntax (but change precedence)

#precedence does not make sense to be defined inside of operator definition, as it describes relationship of two operators. If so, then we are left with the following declaration syntax:

prefix operator ! { }
infix operator |> { }
infix operator <> { associativity left }
If body of operator can only contain associativity (in some cases), then the existence of body itself makes no sense.
_______________________________________________
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

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


(Anton Zhilin) #14

In the poposed model, all relations are not transitive. Example:

#precedence(+, lessThan: *)
#precedence(*, lessThan: ^)
1 ^ 2 + 3 // error
#precedence(+, lessThan: ^)
1 ^ 2 + 3 // now ok

Would it be better to have such indirect relations inferred? Or would it
put too much responsibility on the compiler?
Maybe add it to future directions?

Cycles of length >2 are also allowed. This was not added intentionally, but
follows from other specified rules.
Example: ^ (binary) < & (binary) < + < * < ^ (power). OK, & < + is a bit
stretched, otherwise quite logical.

- I wonder if there are cases in the standard operators which would be

better modeled as a non-linear chain.
In the standard library, non-linearity will be primarily used to break a
single hierarchy into multiple small ones. I doubt that any trees or cycles
will form, although that would be good news.

- Anton

···

2016-04-04 8:55 GMT+03:00 David Waite <david@alkaline-solutions.com>:

Interesting model!

If I understand correctly: this changes the precedence from being based on
a numeric value, to being represented as a bit of a DAG of precedence
groups. A precedence group is defined implicitly for each operator, with
one group around each set of operators where equalTo has been declared.

The groups are lazily evaluated, so if an expression can be resolved
without ambiguity due to lack of reachability between two individual
operators in the DAG, there is no issue/error.

Comments:
- I wonder if there are cases in the standard operators which would be
better modeled as a non-linear chain. The compiler could warn around usage
which is defined by operator precedence, but is commonly considered
ambiguous or error prone.

For example, if users commonly get confused about the conjunctive and
disjunctive levels (logical ‘and’ and ‘or’) being different levels with
precedence, you could just omit the lessThan relationship between the two
of them. The compiler would then error on ambiguous cases, prompting the
user to use parenthesis.

- I’d prefer instead of operator precedence groups just being implicit by
use of #precedence equalTo, that the operators are bound to a group
explicitly. Something like
#precedence(+, group: “additive”)
#precedence(“additive”, lessThan: “multiplicative”)

However, this may create more issues than it solves (two frameworks
creating their own custom operators, putting them in custom precedence
groups, and the consumer decides the two precedence groups are really
equivalent)

-DW

On Apr 3, 2016, at 3:36 AM, Антон Жилин via swift-evolution < > swift-evolution@swift.org> wrote:

Swift 2.2 is out, and I restart discussion on syntax for custom operators.
I insist that this time we should focus less on linguistic aspects.

https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md


(Rainer Brockerhoff) #15

Quoting the book:
“Swift’s operator precedences and associativity rules are simpler and
more predictable than those found in C and Objective-C. ”
Excerpt From: Apple Inc. “The Swift Programming Language (Swift 2.2
Prerelease).” iBooks.

A word of caution. I've been programming since 1969 (and this does
include languages like PL/I and B6700 ALGOL, for instance). For the past
35 years or so, mostly C and Objective-C. Nevertheless I've been bitten
far too often by misremembering operator precedences — and I refuse to
tack the rules up on my wall, or print them upside down on my t-shirt.

With advancing experience, as complexity increases and available neurons
decrease, I find myself _always_ using parentheses, or even intermediate
variables. It looks unwieldy but it's easier to reason about and to
debug. Why should I have to remember what `a && b || c` means in
practice in a particular language?

With newly-declared operators (especially not newly-declared by ME!) the
potential for confusion becomes ever-higher.

I caution against proposals that allow excess flexibility in defining
operator precedence. Even the existing numerical precedence values can
be abused, and expanding them by using some sort of tree, or (say)
floating-point values, is just a "foolish consistency".

I'd say, have the established precedence groups defined by name, and
allow adding new operators to only some of these groups. Inside those,
group by associativity - perhaps the few right-associative types should
not allow any new operators to be added.

All IMHO of course.

···

On 4/4/16 15:21, Daniel Duan via swift-evolution wrote:

Антон Жилин via swift-evolution <swift-evolution@...> writes:

Swift 2.2 is out, and I restart discussion on syntax for custom operators.
I insist that this time we should focus less on linguistic aspects.

...[snip]

3. It may be a good exercise to work out how would each of the builtin
    operators would be defined with this change and mention it (not the entire
    definition, but the fact that it's possible, or reasons why it produces
    any difference) in the proposal.

--
Rainer Brockerhoff <rainer@brockerhoff.net>
Belo Horizonte, Brazil
"In the affairs of others even fools are wise
In their own business even sages err."
http://brockerhoff.net/blog/


(Anton Zhilin) #16

Thank you for a helpful answer!
I like the idea of overriding precedence from another module. We won't need
to introduce additional keywords or visibility for operators. I will add it
to the proposal.

I assume you mean precedence inside braces of operator, then operator scope
makes sense. Self operator could be omit as well, following the idea that
we are introducing a new operator and want to compare it to others:

infix operator * {
    associativity: left
    precedenceLess: ^
    precedenceEqual: /
    precedenceGreater: +
}
infix operator / {
    associativity: left
    precedenceLess: ^
    precedenceEqual: *
    precedenceGreater: +
}

Equivalent precedence rules would be allowed for symmetry in the operator
definitions.
We would still be able to reopen the scope and add precedence and
associativity rules.
I agree that this scheme has advantage of being a smaller change.

I'm still concerned about syntax.
Is it OK to have "less, equal, greater" in precedence name?
Is it OK to have both curly brackets and dictionary syntax (a precedent, I
guess)?
Is it OK to leave prefix and postfix operators always with empty braces?

Would it be better to have multiple precedence comparisons at once:
precedenceGreater: +, -, *, /
Or one comparison per line will be more readable?

I will add this to alternatives, but will not swap it with currently stated
syntax for now, waiting for some more response.

What do you think?

- Anton

···

2016-04-04 8:06 GMT+03:00 Maximilian Hünenberger <m.huenenberger@me.com>:

See inline

Am 03.04.2016 um 13:26 schrieb Ross O'Brien via swift-evolution < > swift-evolution@swift.org>:

There is a problem here of duplicated operators or custom precedence, and
how that gets passed between modules.
Assume there are three modules, A, B and C. B defines a custom operator
**. A and C each define a custom operator ++, and their meanings are
different (though, even if their meanings were the same, I'm not sure if
they could unify).

Module D uses A and B as dependencies and sets a custom precedence on ++
and **. Module E uses B and C and has a different precedence on ++ and **.
You're working on Module F which uses D and E. Which ++ and which
precedence does F get implicitly?

We could allow operator precedence overriding to resolve ambiguity.
However this overriding should only be module internal since it would
override the existing precedences in the other modules.

@AHTOH
Why do you use #keyword ?
I think defining a operator with

    infix operator + {
         associativity: left
    }

is perfectly fine since it is similar to class/struct/enum declaration.

    // and it's precedence
    precedence(+ lessThan *)

Note the missing "," and ":" before and after "lessThan" in order to give
both operators the same importance (minor issue).

I feel that

    #precedence(+, lessThan: *)

puts too much importance on the first operator.

Best regards
- Maximilian

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


(Ross O'Brien) #17

How is a non-transitive relation different from a conflict?
If we use:
#precedence(+, lessThan: *)
#precedence(*, lessThan: ^)
#precedence(^, lessThan: +)
Surely that should be an error, even with none of the operators actually
used?

The compiler would be asked to verify two things: that the relationships of
all operators in the program can be expressed with a topological ordering,
and any pair of operators used together in an expression in the program has
an unambiguous precedence in that ordering. (I have no idea how relatable
this comparison actually is, but it seems like it would be a simpler
problem than analysing NSLayoutConstraints for violations.)

···

On Mon, Apr 4, 2016 at 7:49 AM, Антон Жилин <swift-evolution@swift.org> wrote:

In the poposed model, all relations are not transitive. Example:

#precedence(+, lessThan: *)
#precedence(*, lessThan: ^)
1 ^ 2 + 3 // error
#precedence(+, lessThan: ^)
1 ^ 2 + 3 // now ok

Would it be better to have such indirect relations inferred? Or would it
put too much responsibility on the compiler?
Maybe add it to future directions?

Cycles of length >2 are also allowed. This was not added intentionally,
but follows from other specified rules.
Example: ^ (binary) < & (binary) < + < * < ^ (power). OK, & < + is a bit
stretched, otherwise quite logical.

> - I wonder if there are cases in the standard operators which would be
better modeled as a non-linear chain.
In the standard library, non-linearity will be primarily used to break a
single hierarchy into multiple small ones. I doubt that any trees or cycles
will form, although that would be good news.

- Anton

2016-04-04 8:55 GMT+03:00 David Waite <david@alkaline-solutions.com>:

Interesting model!

If I understand correctly: this changes the precedence from being based
on a numeric value, to being represented as a bit of a DAG of precedence
groups. A precedence group is defined implicitly for each operator, with
one group around each set of operators where equalTo has been declared.

The groups are lazily evaluated, so if an expression can be resolved
without ambiguity due to lack of reachability between two individual
operators in the DAG, there is no issue/error.

Comments:
- I wonder if there are cases in the standard operators which would be
better modeled as a non-linear chain. The compiler could warn around usage
which is defined by operator precedence, but is commonly considered
ambiguous or error prone.

For example, if users commonly get confused about the conjunctive and
disjunctive levels (logical ‘and’ and ‘or’) being different levels with
precedence, you could just omit the lessThan relationship between the two
of them. The compiler would then error on ambiguous cases, prompting the
user to use parenthesis.

- I’d prefer instead of operator precedence groups just being implicit by
use of #precedence equalTo, that the operators are bound to a group
explicitly. Something like
#precedence(+, group: “additive”)
#precedence(“additive”, lessThan: “multiplicative”)

However, this may create more issues than it solves (two frameworks
creating their own custom operators, putting them in custom precedence
groups, and the consumer decides the two precedence groups are really
equivalent)

-DW

On Apr 3, 2016, at 3:36 AM, Антон Жилин via swift-evolution < >> swift-evolution@swift.org> wrote:

Swift 2.2 is out, and I restart discussion on syntax for custom
operators. I insist that this time we should focus less on linguistic
aspects.

https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md

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


(Anton Zhilin) #18

Just to clarify, with currently proposed
behaviour, corresponding expressions would be parsed as follows:

a + b * c // a + (b * c)
a * b ^ c // a * (b ^ c)
a ^ b + c // a ^ (b + c)

The compiler would look only at explicitly stated precedence between two
given operators and would not try to use transcendency.

Having said that, at least two opinions were that operators should form a
DAG by precedence. If no objections are stated, I will change the proposal
accordingly, also without future directions.

On, Apr 4, 2016, Ross O'Brien wrote:

···

How is a non-transitive relation different from a conflict?
If we use:
#precedence(+, lessThan: *)
#precedence(*, lessThan: ^)
#precedence(^, lessThan: +)
Surely that should be an error, even with none of the operators actually
used?

The compiler would be asked to verify two things: that the relationships
of all operators in the program can be expressed with a topological
ordering, and any pair of operators used together in an expression in the
program has an unambiguous precedence in that ordering. (I have no idea how
relatable this comparison actually is, but it seems like it would be a
simpler problem than analysing NSLayoutConstraints for violations.)

On Mon, Apr 4, 2016 at 7:49 AM, Антон Жилин <swift-evolution@swift.org> > wrote:

In the poposed model, all relations are not transitive. Example:

#precedence(+, lessThan: *)
#precedence(*, lessThan: ^)
1 ^ 2 + 3 // error
#precedence(+, lessThan: ^)
1 ^ 2 + 3 // now ok

Would it be better to have such indirect relations inferred? Or would it
put too much responsibility on the compiler?
Maybe add it to future directions?

Cycles of length >2 are also allowed. This was not added intentionally,
but follows from other specified rules.
Example: ^ (binary) < & (binary) < + < * < ^ (power). OK, & < + is a bit
stretched, otherwise quite logical.

> - I wonder if there are cases in the standard operators which would be
better modeled as a non-linear chain.
In the standard library, non-linearity will be primarily used to break a
single hierarchy into multiple small ones. I doubt that any trees or cycles
will form, although that would be good news.

- Anton

2016-04-04 8:55 GMT+03:00 David Waite <david@alkaline-solutions.com>:

Interesting model!

If I understand correctly: this changes the precedence from being based
on a numeric value, to being represented as a bit of a DAG of precedence
groups. A precedence group is defined implicitly for each operator, with
one group around each set of operators where equalTo has been declared.

The groups are lazily evaluated, so if an expression can be resolved
without ambiguity due to lack of reachability between two individual
operators in the DAG, there is no issue/error.

Comments:
- I wonder if there are cases in the standard operators which would be
better modeled as a non-linear chain. The compiler could warn around usage
which is defined by operator precedence, but is commonly considered
ambiguous or error prone.

For example, if users commonly get confused about the conjunctive and
disjunctive levels (logical ‘and’ and ‘or’) being different levels with
precedence, you could just omit the lessThan relationship between the two
of them. The compiler would then error on ambiguous cases, prompting the
user to use parenthesis.

- I’d prefer instead of operator precedence groups just being implicit
by use of #precedence equalTo, that the operators are bound to a group
explicitly. Something like
#precedence(+, group: “additive”)
#precedence(“additive”, lessThan: “multiplicative”)

However, this may create more issues than it solves (two frameworks
creating their own custom operators, putting them in custom precedence
groups, and the consumer decides the two precedence groups are really
equivalent)

-DW

On Apr 3, 2016, at 3:36 AM, Антон Жилин via swift-evolution < >>> swift-evolution@swift.org> wrote:

Swift 2.2 is out, and I restart discussion on syntax for custom
operators. I insist that this time we should focus less on linguistic
aspects.

https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md

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


#19

Inline

Thank you for a helpful answer!
I like the idea of overriding precedence from another module. We won't need to introduce additional keywords or visibility for operators. I will add it to the proposal.

I assume you mean precedence inside braces of operator, then operator scope makes sense. Self operator could be omit as well, following the idea that we are introducing a new operator and want to compare it to others:

infix operator * {
    associativity: left
    precedenceLess: ^
    precedenceEqual: /
    precedenceGreater: +
}
infix operator / {
    associativity: left
    precedenceLess: ^
    precedenceEqual: *
    precedenceGreater: +
}

Equivalent precedence rules would be allowed for symmetry in the operator definitions.
We would still be able to reopen the scope and add precedence and associativity rules.
I agree that this scheme has advantage of being a smaller change.

I'm still concerned about syntax.
Is it OK to have "less, equal, greater" in precedence name?

What do you mean by OK? Other operators like == are weird:

precedence(== == <)
// in comparison to
precedence(== equalTo <)

Is it OK to have both curly brackets and dictionary syntax (a precedent, I guess)?

That could be a consistency problem. However I'd like to keep a declaration syntax instead of defining operators through compiler directives.

Is it OK to leave prefix and postfix operators always with empty braces?

I'm fine with that.

Would it be better to have multiple precedence comparisons at once:
precedenceGreater: +, -, *, /
Or one comparison per line will be more readable?

I will add this to alternatives, but will not swap it with currently stated syntax for now, waiting for some more response.

What do you think?

- Anton

After thinking more about it I came to the conclusion that we should have something like "precedence groups" where all operators have the same precedence:

precedenceGroup Additive {
    +, -
}

precedenceGroup Multiplicative {
    *, /
}

// first we could allow an operator to be only
// in one p.Group. Later when we can infer
// precedences we can allow operators to be
// in more than one p.Group (if that is even necessary)

// precedence declaration
precedence(Additive lessThan Multiplicative)

// precedence declarations take
// "precedenceGroup"s and "operator"s.

// It could also work with extensions so you
// can easily insert it in the precedence hierarchy:

infix operator +- {
    associativity: left
}

// more operator declarations ...

extension Additive {
    +-, -+, ++, --
}

// the syntax I used is highly discussable.
// this should mainly be a concept suggestion.
// I think it allows for greater flexibility since you can define
// operator precedences much easier.

With kind regards
- Maximilian

···

Am 04.04.2016 um 08:06 schrieb Антон Жилин <antonyzhilin@gmail.com>:

2016-04-04 8:06 GMT+03:00 Maximilian Hünenberger <m.huenenberger@me.com>:

See inline

Am 03.04.2016 um 13:26 schrieb Ross O'Brien via swift-evolution <swift-evolution@swift.org>:

There is a problem here of duplicated operators or custom precedence, and how that gets passed between modules.
Assume there are three modules, A, B and C. B defines a custom operator **. A and C each define a custom operator ++, and their meanings are different (though, even if their meanings were the same, I'm not sure if they could unify).

Module D uses A and B as dependencies and sets a custom precedence on ++ and **. Module E uses B and C and has a different precedence on ++ and **. You're working on Module F which uses D and E. Which ++ and which precedence does F get implicitly?

We could allow operator precedence overriding to resolve ambiguity. However this overriding should only be module internal since it would override the existing precedences in the other modules.

@AHTOH
Why do you use #keyword ?
I think defining a operator with

    infix operator + {
         associativity: left
    }

is perfectly fine since it is similar to class/struct/enum declaration.

    // and it's precedence
    precedence(+ lessThan *)

Note the missing "," and ":" before and after "lessThan" in order to give both operators the same importance (minor issue).

I feel that

    #precedence(+, lessThan: *)

puts too much importance on the first operator.

Best regards
- Maximilian

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


(Anton Zhilin) #20

Sorry for breaking the thread, next time please mail me a copy :slight_smile:
Link to the proposal:
https://github.com/Anton3/swift-evolution/blob/operator-precedence/proposals/NNNN-operator-precedence.md

1. I strongly agree that the numeric precedence system is not great. From

    a implementation point of view, the way to specify them in your
proposal
    essentially gave all visible operators a partial order, in which we can
    draw a directed gragh with operators being the nodes and their relation
    being arcs. A part of the graph might look like: '^' --> '*' --> '+',
the
    nodes being the math operators. We can tell '*' has a higher precedence
    than '+', '^' has a higher precedence than '*' and '+', by follwing the
    arcs. If one operator is not reachable from another, and vice versa,
then
    composing these two is illegal. We need to teach the compiler this
concept.

Right, that semantics was actually the main driver of the proposal.

2. Currently, it's not possible to specify precedence for pre- and postfix

    operators. Chris Lattner has mentioned that the
    following result is not desirable:
        ∆x + y
    … where ∆ has a lower precendence than + while it's required to have no
    space between ∆ and the operand. My understanding is that if spaces
were
    to be allowed here, parsing such expression without ambiguity is a
    non-trivial challenge. So, will it be possible to specify precedence
for
    pre/postfix operators under your proposal?

No, I currently leave prefix and postfix operators unchanged (apart from
syntax shuffling). I will add this as a future direction.
Prefix and postfix operators would behave the same as infix, but have
higher precedence than any infix operator by default.
This feature, if added, would be non-breaking.

3. It may be a good exercise to work out how would each of the builtin
    operators would be defined with this change and mention it (not the
entire
    definition, but the fact that it's possible, or reasons why it produces
    any difference) in the proposal.

Operators `is`, `as`, `as?`, `as!`, which are not actually Swift custom
operators (they contain letters), will not participate in this exercise.
(Let's not discuss allowing letters in operators here.)
That being said, great idea. I will add example declarations of standard
library operators.

- Anton