[swift-evolution] [Proposal] Custom operators

Maximilian Hünenberger m.huenenberger at me.com
Mon Apr 4 00:06:07 CDT 2016


See inline

> Am 03.04.2016 um 13:26 schrieb Ross O'Brien via swift-evolution <swift-evolution at 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

> 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 at 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 at 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 at swift.org
>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>> 
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
> 
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160404/f55eb392/attachment.html>


More information about the swift-evolution mailing list