Some concerns on custom operators


(Anton Zhilin) #1

1.

   Upon implementation of SE-0077 in Swift 3, some libraries started to
   drop operators entirely: link #1
   <https://github.com/antitypical/Result/issues/191>, link #2
   <https://github.com/typelift/SwiftCheck/pull/179>.
   - Declarations of the same custom operator with different precedence
      groups create a conflict.
      - The conflict can be resolved manually, but the resolution has to be
      made in *every* file that uses the operator, which defeats the reason
      for using operators in the first place.
      - This is a part of a larger problem of conflict resolution, for
      which we don’t currently have a systematic approach.
      - Many libraries dealing with custom operators choose to import Runes
      <https://github.com/thoughtbot/Runes>, which is basically a stockpile
      of operator declarations. But it conflicts with Result, Swiftx and
      Operadics.
   2.

   Even if operator conflicts are resolved, precedencegroups’ names are not
   module-scoped, and don’t support conflict resolution.
   - Many libraries decide to just go ahead and prefix precedencegroups
      with module name.
      - Some developer on Github specifically complained about having to do
      this, but I’ve lost the link.
   3.

   Some precedencegroup names don’t seem perfect to me. This concern may
   not be strong enough to make a source-breaking change, though.
   - LogicalDisjunctionPrecedence -> DisjunctionPrecedence
      - LogicalConjunctionPrecedence -> ConjunctionPrecedence
      - BitwiseShiftPrecedence should be renamed to ExponentiationPrecedence,
      if we decide not to branch bitwise operators off arithmetic.
   4.

   For the mentioned branching, I’m going to post a separate [Pitch] soon.


(Joe Groff) #2

Upon implementation of SE-0077 in Swift 3, some libraries started to drop operators entirely: link #1 <https://github.com/antitypical/Result/issues/191>, link #2 <https://github.com/typelift/SwiftCheck/pull/179>.
Declarations of the same custom operator with different precedence groups create a conflict.
The conflict can be resolved manually, but the resolution has to be made in every file that uses the operator, which defeats the reason for using operators in the first place.
This is a part of a larger problem of conflict resolution, for which we don’t currently have a systematic approach.
Many libraries dealing with custom operators choose to import Runes <https://github.com/thoughtbot/Runes>, which is basically a stockpile of operator declarations. But it conflicts with Result, Swiftx and Operadics.
Even if operator conflicts are resolved, precedencegroups’ names are not module-scoped, and don’t support conflict resolution.
Many libraries decide to just go ahead and prefix precedencegroups with module name.
Some developer on Github specifically complained about having to do this, but I’ve lost the link.

Do you know if bugs have been filed about these issues? IIRC, SE-0077 specified that precedence groups should act like normal named declarations and be scopable. The fact that they aren't sounds like a bug to be fixed.

-Joe

···

On Nov 9, 2016, at 9:25 AM, Anton Zhilin via swift-evolution <swift-evolution@swift.org> wrote:

Some precedencegroup names don’t seem perfect to me. This concern may not be strong enough to make a source-breaking change, though.
LogicalDisjunctionPrecedence -> DisjunctionPrecedence
LogicalConjunctionPrecedence -> ConjunctionPrecedence
BitwiseShiftPrecedence should be renamed to ExponentiationPrecedence, if we decide not to branch bitwise operators off arithmetic.
For the mentioned branching, I’m going to post a separate [Pitch] soon.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(John McCall) #3

  • Upon implementation of SE-0077 in Swift 3, some libraries started to drop operators entirely: link #1, link #2.
    • Declarations of the same custom operator with different precedence groups create a conflict.
    • The conflict can be resolved manually, but the resolution has to be made in every file that uses the operator, which defeats the reason for using operators in the first place.
    • This is a part of a larger problem of conflict resolution, for which we don’t currently have a systematic approach.

It makes sense to me to provide a more module-wide conflict resolution mechanism. Maybe we can have some sort of "internal export" mechanism where a file can introduce imports into other files within a project.

    • Many libraries dealing with custom operators choose to import Runes, which is basically a stockpile of operator declarations. But it conflicts with Result, Swiftx and Operadics.

Won't this just shake itself out pretty soon, assuming these projects have any interest in interoperating?

  • Even if operator conflicts are resolved, precedencegroups’ names are not module-scoped, and don’t support conflict resolution.
    • Many libraries decide to just go ahead and prefix precedencegroups with module name.
    • Some developer on Github specifically complained about having to do this, but I’ve lost the link.

Seems like a bug.

  • Some precedencegroup names don’t seem perfect to me. This concern may not be strong enough to make a source-breaking change, though.
    • LogicalDisjunctionPrecedence -> DisjunctionPrecedence
    • LogicalConjunctionPrecedence -> ConjunctionPrecedence
    • BitwiseShiftPrecedence should be renamed to ExponentiationPrecedence, if we decide not to branch bitwise operators off arithmetic.

Yeah, the time for this feedback was several months ago.

John.

···

On Nov 9, 2016, at 9:25 AM, Anton Zhilin via swift-evolution <swift-evolution@swift.org> wrote:

  • For the mentioned branching, I’m going to post a separate [Pitch] soon.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Dave Abrahams) #4

This is a well-known library interoperability dynamic, and IMO we can't
expect the solution for conflicting libraries to be that you have to get
the library authors to communicate with one another. That effectively
fixes nothing for the poor app developer who integrates these libraries.

···

on Wed Nov 09 2016, John McCall <swift-evolution@swift.org> wrote:

On Nov 9, 2016, at 9:25 AM, Anton Zhilin via swift-evolution <swift-evolution@swift.org> wrote:
• Upon implementation of SE-0077 in Swift 3, some libraries started to drop operators entirely:

link #1, link #2.

• Declarations of the same custom operator with different precedence groups create a conflict.
• The conflict can be resolved manually, but the resolution has to be made in every file that uses

the operator, which defeats the reason for using operators in the first place.

• This is a part of a larger problem of conflict resolution, for which we don’t currently have a

systematic approach.

It makes sense to me to provide a more module-wide conflict resolution
mechanism. Maybe we can have some sort of "internal export" mechanism
where a file can introduce imports into other files within a project.

    • Many libraries dealing with custom operators choose
to import Runes, which is basically a stockpile of operator
declarations. But it conflicts with Result, Swiftx and Operadics.

Won't this just shake itself out pretty soon, assuming these projects
have any interest in interoperating?

--
-Dave


(John McCall) #5

I agree that we need to solve that problem, which is why I suggested an approach
for solving that problem in the previous paragraph. But it's still reasonable for us as
"wardens of the ecosystem" to ask library authors to consider how their libraries
interoperate with their peers.

We can also make a stronger effort to ignore spurious conflicts in the language, of
course, e.g. by only complaining if conflicting precedencegroup declarations would
yield different parsing results; but that logic would get unworkably complex pretty quick.

John.

···

On Nov 9, 2016, at 1:24 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:
on Wed Nov 09 2016, John McCall <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Nov 9, 2016, at 9:25 AM, Anton Zhilin via swift-evolution <swift-evolution@swift.org> wrote:
• Upon implementation of SE-0077 in Swift 3, some libraries started to drop operators entirely:

link #1, link #2.

• Declarations of the same custom operator with different precedence groups create a conflict.
• The conflict can be resolved manually, but the resolution has to be made in every file that uses

the operator, which defeats the reason for using operators in the first place.

• This is a part of a larger problem of conflict resolution, for which we don’t currently have a

systematic approach.

It makes sense to me to provide a more module-wide conflict resolution
mechanism. Maybe we can have some sort of "internal export" mechanism
where a file can introduce imports into other files within a project.

    • Many libraries dealing with custom operators choose
to import Runes, which is basically a stockpile of operator
declarations. But it conflicts with Result, Swiftx and Operadics.

Won't this just shake itself out pretty soon, assuming these projects
have any interest in interoperating?

This is a well-known library interoperability dynamic, and IMO we can't
expect the solution for conflicting libraries to be that you have to get
the library authors to communicate with one another. That effectively
fixes nothing for the poor app developer who integrates these libraries.


(Dave Abrahams) #6

• Upon implementation of SE-0077 in Swift 3, some libraries started to drop operators entirely:

link #1, link #2.

• Declarations of the same custom operator with different precedence groups create a conflict.
• The conflict can be resolved manually, but the resolution has to be made in every file that uses

the operator, which defeats the reason for using operators in the first place.

• This is a part of a larger problem of conflict resolution, for which we don’t currently have a

systematic approach.

It makes sense to me to provide a more module-wide conflict resolution
mechanism. Maybe we can have some sort of "internal export" mechanism
where a file can introduce imports into other files within a project.

    • Many libraries dealing with custom operators choose
to import Runes, which is basically a stockpile of operator
declarations. But it conflicts with Result, Swiftx and Operadics.

Won't this just shake itself out pretty soon, assuming these projects
have any interest in interoperating?

This is a well-known library interoperability dynamic, and IMO we can't
expect the solution for conflicting libraries to be that you have to get
the library authors to communicate with one another. That effectively
fixes nothing for the poor app developer who integrates these libraries.

I agree that we need to solve that problem, which is why I suggested an approach
for solving that problem in the previous paragraph.

Sorry if I didn't read carefully enough.

But it's still reasonable for us as "wardens of the ecosystem" to ask
library authors to consider how their libraries interoperate with
their peers.

Sure; that's part of the job of writing a library.

···

on Wed Nov 09 2016, John McCall <rjmccall-AT-apple.com> wrote:

On Nov 9, 2016, at 1:24 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:
on Wed Nov 09 2016, John McCall <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> > wrote:

On Nov 9, 2016, at 9:25 AM, Anton Zhilin via swift-evolution <swift-evolution@swift.org> wrote:

We can also make a stronger effort to ignore spurious conflicts in the
language, of course, e.g. by only complaining if conflicting
precedencegroup declarations would yield different parsing results;
but that logic would get unworkably complex pretty quick.

John.

--
-Dave


(Anton Zhilin) #7

I don't know of any. At one stage during review a core team insisted on
that, because it is simpler to implement, plus conflicts in custom
operators sphere are unavoidable anyway. But I agree that it can be
considered a bug.

···

2016-11-09 21:56 GMT+03:00 Joe Groff <jgroff@apple.com>:

Do you know if bugs have been filed about these issues? IIRC, SE-0077
specified that precedence groups should act like normal named declarations
and be scopable. The fact that they aren't sounds like a bug to be fixed.


(Anton Zhilin) #8

> • Upon implementation of SE-0077 in Swift 3, some libraries
started to drop operators entirely: link #1, link #2.
> • Declarations of the same custom operator with different
precedence groups create a conflict.
> • The conflict can be resolved manually, but the
resolution has to be made in every file that uses the operator, which
defeats the reason for using operators in the first place.
> • This is a part of a larger problem of conflict
resolution, for which we don’t currently have a systematic approach.

It makes sense to me to provide a more module-wide conflict resolution
mechanism. Maybe we can have some sort of "internal export" mechanism
where a file can introduce imports into other files within a project.

It would also be generally useful for modules that are made use of
throughout the project, like logging or those generic operators and
collections libraries. I can create a proposal for that.

              • Many libraries dealing with custom operators choose to
import Runes, which is basically a stockpile of operator declarations. But
it conflicts with Result, Swiftx and Operadics.

Won't this just shake itself out pretty soon, assuming these projects have
any interest in interoperating?

Maybe. Anyway, "internal import" can make such conflicts not that much of a
problem.

···

2016-11-09 22:20 GMT+03:00 John McCall <rjmccall@apple.com>:

> On Nov 9, 2016, at 9:25 AM, Anton Zhilin via swift-evolution < > swift-evolution@swift.org> wrote:


(Jay) #9

Would it make sense to allow some kind of operator aliasing on import, so
that developers can at least work-around library conflicts?

···

On Wed, 9 Nov 2016 at 21:59 Dave Abrahams via swift-evolution < swift-evolution@swift.org> wrote:

on Wed Nov 09 2016, John McCall <rjmccall-AT-apple.com> wrote:

>> On Nov 9, 2016, at 1:24 PM, Dave Abrahams via swift-evolution < > swift-evolution@swift.org> wrote:
>> on Wed Nov 09 2016, John McCall <swift-evolution@swift.org <mailto: > swift-evolution@swift.org>> > > wrote:
>>
>>>> On Nov 9, 2016, at 9:25 AM, Anton Zhilin via swift-evolution < > swift-evolution@swift.org> wrote:
>>>> • Upon implementation of SE-0077 in Swift 3, some libraries started
to drop operators entirely:
>
>>> link #1, link #2.
>>>> • Declarations of the same custom operator with different precedence
groups create a conflict.
>>>> • The conflict can be resolved manually, but the resolution has to be
made in every file that uses
>>> the operator, which defeats the reason for using operators in the
first place.
>>>> • This is a part of a larger problem of conflict resolution, for
which we don’t currently have a
>>> systematic approach.
>>>
>>> It makes sense to me to provide a more module-wide conflict resolution
>>> mechanism. Maybe we can have some sort of "internal export" mechanism
>>> where a file can introduce imports into other files within a project.
>>>
>>>> • Many libraries dealing with custom operators choose
>>>> to import Runes, which is basically a stockpile of operator
>>>> declarations. But it conflicts with Result, Swiftx and Operadics.
>>>
>>> Won't this just shake itself out pretty soon, assuming these projects
>>> have any interest in interoperating?
>>
>> This is a well-known library interoperability dynamic, and IMO we can't
>> expect the solution for conflicting libraries to be that you have to get
>> the library authors to communicate with one another. That effectively
>> fixes nothing for the poor app developer who integrates these libraries.
>
> I agree that we need to solve that problem, which is why I suggested an
approach
> for solving that problem in the previous paragraph.

Sorry if I didn't read carefully enough.

> But it's still reasonable for us as "wardens of the ecosystem" to ask
> library authors to consider how their libraries interoperate with
> their peers.

Sure; that's part of the job of writing a library.

> We can also make a stronger effort to ignore spurious conflicts in the
> language, of course, e.g. by only complaining if conflicting
> precedencegroup declarations would yield different parsing results;
> but that logic would get unworkably complex pretty quick.
>
> John.

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


(Joe Groff) #10

Definitely. I think something like the import improvements Robert Widmann was proposing a while back are sorely needed, not only for operators but for managing conflicts in general.

-Joe

···

On Nov 10, 2016, at 7:23 AM, Jay Abbott via swift-evolution <swift-evolution@swift.org> wrote:

Would it make sense to allow some kind of operator aliasing on import, so that developers can at least work-around library conflicts?


(Robert Widmann) #11

Just gotta field a version of that proposal that doesn’t “look like Haskell” :slight_smile:

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?

···

On Nov 10, 2016, at 10:54 AM, Joe Groff <jgroff@apple.com> wrote:

On Nov 10, 2016, at 7:23 AM, Jay Abbott via swift-evolution <swift-evolution@swift.org> wrote:

Would it make sense to allow some kind of operator aliasing on import, so that developers can at least work-around library conflicts?

Definitely. I think something like the import improvements Robert Widmann was proposing a while back are sorely needed, not only for operators but for managing conflicts in general.

-Joe


(David Sweeris) #12

Just gotta field a version of that proposal that doesn’t “look like Haskell” :slight_smile:

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. There's probably not any ambiguities if the two libraries don't define the same actual function signatures (like one defines `+(Double, Double)->Double` and one defines `+(Int, Int)->Int`), but that doesn't help if the both define the same operator with the same signature with different precedences. And what if you want to define `+(Double, Int)->String`? Does the + operator take its precedence from the first library or the second?

Or for a slightly more plausible example, what if a library defines `*(Double, Int)->Double` and it does exactly what you'd expect it to do, but for whatever reason it was given the wrong precedence. Suddenly, code like this:
    let x = someDouble * 4 + 3 // 4 and 3 are inferred to be doubles
changes to this:
    let x = someDouble * 4 + 3 // 4 is inferred to be an Int, and 3 is inferred to be a double
This (I think) gives a different answer, purely because of the unexpected change in precedence. This would be especially hard to debug, since `let x = someDouble * 4` by itself would still give the correct answer even though it'd be using the "wrong" function.

What about allowing users to import a library's global functions and operators separately from its types? Or optionally importing the global stuff into a namespace (obviously this would necessitate adding namespace support)?

- Dave Sweeris

···

On Nov 26, 2016, at 17:19, Robert Widmann via swift-evolution <swift-evolution@swift.org> wrote:


(John McCall) #13

Just gotta field a version of that proposal that doesn’t “look like Haskell” :slight_smile:

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?

The definition of "perfect duplicates" is more complex now. It would be easy to ignore duplicates that name the same precedencegroup, but that's probably not what's happening here.

John.

···

On Nov 26, 2016, at 3:19 PM, Robert Widmann <devteam.codafi@gmail.com> wrote:

On Nov 10, 2016, at 10:54 AM, Joe Groff <jgroff@apple.com> wrote:

On Nov 10, 2016, at 7:23 AM, Jay Abbott via swift-evolution <swift-evolution@swift.org> wrote:

Would it make sense to allow some kind of operator aliasing on import, so that developers can at least work-around library conflicts?

Definitely. I think something like the import improvements Robert Widmann was proposing a while back are sorely needed, not only for operators but for managing conflicts in general.

-Joe


(Dave Abrahams) #14

differnt precedences => not perfect duplicates, right?

···

on Sat Nov 26 2016, David Sweeris <davesweeris-AT-mac.com> wrote:

On Nov 26, 2016, at 17:19, Robert Widmann via swift-evolution <swift-evolution@swift.org> wrote:

Just gotta field a version of that proposal that doesn’t “look like Haskell” :slight_smile:

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.

--
-Dave


(Robert Widmann) #15

That is what was happening for me at the time. Operadics was exporting bind (>>-), ap (<*>) and fmap (<^>), and SwiftCheck was pulling in Operadics inline in the non-SPM build. The two modules were literally trying to export the same operators with the same precedencegroup declarations. My definition of “perfectly identical” covers this case.

Just gotta field a version of that proposal that doesn’t “look like Haskell” :slight_smile:

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?

The definition of "perfect duplicates" is more complex now. It would be easy to ignore duplicates that name the same precedencegroup, but that's probably not what's happening here.

In that case there is a nice structural equality that falls out of the current way things are defined, more so than before given that we can use the relative precedences (and given that most libraries don’t set up precedencegroup lattices that are complex as TypeLift does). Essentially, the problem is verifying a bisimulation with alpha-equivalence at all the edges.

···

On Nov 27, 2016, at 9:01 PM, John McCall <rjmccall@apple.com> wrote:

On Nov 26, 2016, at 3:19 PM, Robert Widmann <devteam.codafi@gmail.com> wrote:

John.

On Nov 10, 2016, at 10:54 AM, Joe Groff <jgroff@apple.com> wrote:

On Nov 10, 2016, at 7:23 AM, Jay Abbott via swift-evolution <swift-evolution@swift.org> wrote:

Would it make sense to allow some kind of operator aliasing on import, so that developers can at least work-around library conflicts?

Definitely. I think something like the import improvements Robert Widmann was proposing a while back are sorely needed, not only for operators but for managing conflicts in general.

-Joe


(David Sweeris) #16

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

···

On Nov 26, 2016, at 22:02, Dave Abrahams <dabrahams@apple.com> wrote:

on Sat Nov 26 2016, David Sweeris <davesweeris-AT-mac.com> wrote:

On Nov 26, 2016, at 17:19, Robert Widmann via swift-evolution <swift-evolution@swift.org> wrote:

Just gotta field a version of that proposal that doesn’t “look like Haskell” :slight_smile:

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?


(Anton Zhilin) #17

Would allowing duplicate precedence group declarations solve the problem?
AFAIK, operators are already merged this way.

···

2016-11-28 5:09 GMT+03:00 Robert Widmann via swift-evolution < swift-evolution@swift.org>:

That is what was happening for me at the time. Operadics was exporting
bind (>>-), ap (<*>) and fmap (<^>), and SwiftCheck was pulling in
Operadics inline in the non-SPM build. The two modules were literally
trying to export the same operators with the same precedencegroup
declarations. My definition of “perfectly identical” covers this case.

> The definition of "perfect duplicates" is more complex now. It would be
easy to ignore duplicates that name the same precedencegroup, but that's
probably not what's happening here.

In that case there is a nice structural equality that falls out of the
current way things are defined, more so than before given that we can use
the relative precedences (and given that most libraries don’t set up
precedencegroup lattices that are complex as TypeLift does). Essentially,
the problem is verifying a bisimulation with alpha-equivalence at all the
edges.


(Robert Widmann) #18

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@mac.com> wrote:

On Nov 26, 2016, at 22:02, Dave Abrahams <dabrahams@apple.com> wrote:

on Sat Nov 26 2016, David Sweeris <davesweeris-AT-mac.com> wrote:

On Nov 26, 2016, at 17:19, Robert Widmann via swift-evolution <swift-evolution@swift.org> wrote:

Just gotta field a version of that proposal that doesn’t “look like Haskell” :slight_smile:

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


(David Sweeris) #19

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:
//ALib
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:
//ABuggedLib
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:
//SomeFile.swift
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:
//SomeOtherFileInYourProject.swift
import ABuggedLib
...
//SomeFile.swift
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?

···

On Nov 26, 2016, at 23:52, Robert Widmann <devteam.codafi@gmail.com <mailto:devteam.codafi@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@mac.com <mailto:davesweeris@mac.com>> wrote:

On Nov 26, 2016, at 22:02, Dave Abrahams <dabrahams@apple.com <mailto:dabrahams@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@swift.org <mailto:swift-evolution@swift.org>> wrote:

Just gotta field a version of that proposal that doesn’t “look like Haskell” :slight_smile:

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


(Robert Widmann) #20

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.

Just gotta field a version of that proposal that doesn’t “look like Haskell” :slight_smile:

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.

Operadics and Runes are libraries that export nothing but operator declarations. The former is put out by my org., TypeLift <https://github.com/typelift/Operadics/commit/c65e6355e22282a89d68a8a2d594a32c36c1e7b0>, he latter is put out by ThoughtBot <https://github.com/thoughtbot/Runes>.

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:
//ALib
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:
//ABuggedLib
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} }

I’m gonna cut you off right here: These are not identical declarations and were an error under even the old scheme of things. This is not what we are talking about.

···

On Nov 27, 2016, at 7:03 PM, David Sweeris <davesweeris@mac.com> wrote:
On Nov 26, 2016, at 23:52, Robert Widmann <devteam.codafi@gmail.com <mailto:devteam.codafi@gmail.com>> wrote:

On Nov 27, 2016, at 12:43 AM, David Sweeris <davesweeris@mac.com <mailto:davesweeris@mac.com>> wrote:

On Nov 26, 2016, at 22:02, Dave Abrahams <dabrahams@apple.com <mailto:dabrahams@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@swift.org <mailto:swift-evolution@swift.org>> wrote: