[swift-evolution] Some concerns on custom operators

David Sweeris davesweeris at mac.com
Sun Nov 27 18:03:30 CST 2016

On Nov 26, 2016, at 23:52, Robert Widmann <devteam.codafi at gmail.com <mailto:devteam.codafi at gmail.com>> wrote:

> Under the old behavior they must have identical declarations, that includes precedence.  We specifically had to modify the precedences of some stuff in Operadics to match Runes because of this and it worked just fine.
>> On Nov 27, 2016, at 12:43 AM, David Sweeris <davesweeris at mac.com <mailto:davesweeris at mac.com>> wrote:
>>> On Nov 26, 2016, at 22:02, Dave Abrahams <dabrahams at apple.com <mailto:dabrahams at apple.com>> wrote:
>>> on Sat Nov 26 2016, David Sweeris <davesweeris-AT-mac.com <http://davesweeris-at-mac.com/>> wrote:
>>>>> On Nov 26, 2016, at 17:19, Robert Widmann via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>>>> Just gotta field a version of that proposal that doesn’t “look like Haskell” :)
>>>> Is there something wrong with Haskell's approach to imports? I don't
>>>> know how they do it, so I'm unaware of any pros/cons to their
>>>> approach. The ":)" makes me think I'm missing something...
>>>>> Seriously, though, would there be any objection to restoring the old
>>>>> behavior of just silently ignoring perfect duplicates of operator
>>>>> definitions across frameworks sans proposal?
>>>> Yeah, it could silently change how statements get evaluated, if I
>>>> start writing code using one library's operators, then import a 3rd
>>>> library which defines the same operators but with different
>>>> precedences. 
>>> differnt precedences => not perfect duplicates, right?
>> That's a good question... I don't know... The compiler keeps track of functions by their "fully qualified" name, i.e. "MyLib.+(Int, Int)->Int", right? 
>> Swift's syntax only allows us to declare precedence on a per-operator basis. Does the compiler track precedence on a per-function basis anyway, and if so, how would you specify which precedence you want at the call site? Aside from parentheses, I mean.
>> - Dave Sweeris

I don't know what "operatics" or “runes” are. Based on the context I’d guess they’re two parts of the standard library, but I'd like to be sure.

Either way, though, I'm not sure this addresses my primary objection (which I wrote the wrong way around in my earlier email). Suppose a library does this:
infix operator • : MultiplicationPrecedence
extension Double : IntegerArithmetic {...}
func • <T: IntegerArithmetic> (lhs: T, rhs: Array<T>) -> Array<T> { return rhs.map {lhs * $0} }
func + <T: IntegerArithmetic> (lhs: T, rhs: Array<T>) -> Array<T> { return rhs.map {lhs + $0} }
And another library does this, which is an easy copy/paste error to make, since everything still works as long as you only test single-operator expressions:
infix operator • : AdditionPrecedence
//Some convenience functions for getting Ints into your Doubly goodness
func • (lhs: Int, rhs: Array<Double>) -> Array<Double> { return rhs.map {Double(lhs) * $0} }
func + (lhs: Int, rhs: Array<Double>) -> Array<Double> { return rhs.map {Double(lhs) + $0} }
You write your code like this:
import ALib
let x = 1 + 4 • [1.0, 2.0, 3.0] //[5, 9, 13]
Then six months later, for whatever reason — maybe it has some useful type or something — you decide that some other file needs to import ABuggedLib:
import ABuggedLib
import ALib
let x = 1 + 4 • [1.0, 2.0, 3.0] //silently changes to [5, 10, 15], even though neither this line, nor the file it’s in, have been touched in six months
The compiler won’t even give you an "ambiguous statement” error because the function in ABuggedLib takes an Int, which is a better match than the correctly-precedenced generic version in ALib. This “bug" might even be extra maddening, since I think it won’t actually show up until you either clean your project or touch “SomeFile.swift”… Potentially, there could be lot of time between cause (importing ABuggedLib) and effect (x’s value changing).

Dunno, maybe this isn't as big of an issue as I’m making it out to be... I don’t have any solutions for detecting when it happens, other than for IDEs to alert you whenever adding an import statement changes how code that’s already written would be compiled (this isn’t "Xcode-evolution", though, nor would it help people who use other editors).

I guess I’m really just arguing that operators and their precedences should be part of whatever name collision resolution scheme we come up with, and I don’t recall the old behavior doing that (I could be wrong there).

- Dave Sweeris

P.S. Relatedly, what’s the precedence of the dot product function here, anyway? If there’s a rule about “operators always take the highest precedence they can find” or something, I’m unaware of it.
import ALib //declares • with MultiplicationPrecedence
import ABuggedLib //declares • with AdditionPrecedence
//doesn’t need a operator declaration, since it’s already in an imported library
func • <T: IntegerArithmetic> (lhs: Array<T>, rhs: Array<T>) -> Array<T> {
    guard lhs.count == rhs.count else { fatalError("Dimension mis-match") }
    return (0 ..< lhs.count).map {lhs[$0] * rhs[$0]}
Or am I mistaken about not needing to provide my own operator declaration in this case?

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20161127/5fe51867/attachment.html>

More information about the swift-evolution mailing list