Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal for tuple comparison operators #45

Merged
merged 1 commit into from Dec 18, 2015
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
95 changes: 95 additions & 0 deletions proposals/0000-tuple-comparison-operators.md
@@ -0,0 +1,95 @@
# Tuple comparison operators

* Proposal: TBD
* Author(s): [Kevin Ballard](https://github.com/kballard)
* Status: **Review**
* Review manager: TBD

## Introduction

Implement comparison operators on tuples up to some arity.

## Motivation

It's annoying to try and compare tuples of comparable values and discover that
tuples don't support any of the common comparison operators. There's an
extremely obvious definition of `==` and `!=` for tuples of equatable values,
and a reasonably obvious definition of the ordered comparison operators as well
(lexicographical compare).

Beyond just comparing tuples, being able to compare tuples also makes it easier
to implement comparison operators for tuple-like structs, as the relevant
operator can just compare tuples containing the struct properties.

## Proposed solution

The Swift standard library should provide generic implementations of the
comparison operators for all tuples up to some specific arity. The arity should
be chosen so as to balance convenience (all tuples support this) and code size
(every definition adds to the size of the standard library).

When Swift gains support for conditional conformation to protocols, and if Swift
ever gains support for extending tuples, then the tuples up to the chosen arity
should also be conditionally declared as conforming to `Equatable` and
`Comparable`.

If Swift ever gains support for variadic type parameters, then we should
investigate redefining the operators (and protocol conformance) in terms of
variadic types, assuming there's no serious codesize issues.

## Detailed design

The actual definitions will be generated by gyb. The proposed arity here is 6,
which is large enough for most reasonable tuples (but not as large as I'd
prefer), without having massive code increase. After implementing this proposal
for arity 6, a Ninja-ReleaseAssert build increases codesize for
`libswiftCore.dylib` (for both macosx and iphoneos) by 43.6KiB, which is a
1.4% increase.

The generated definitions look like the following (for arity 3):

```swift
@warn_unused_result
public func == <A: Equatable, B: Equatable, C: Equatable>(lhs: (A,B,C), rhs: (A,B,C)) -> Bool {
return lhs.0 == rhs.0 && lhs.1 == rhs.1 && lhs.2 == rhs.2
}

@warn_unused_result
public func != <A: Equatable, B: Equatable, C: Equatable>(lhs: (A,B,C), rhs: (A,B,C)) -> Bool {
return lhs.0 != rhs.0 || lhs.1 != rhs.1 || lhs.2 != rhs.2
}

@warn_unused_result
public func < <A: Comparable, B: Comparable, C: Comparable>(lhs: (A,B,C), rhs: (A,B,C)) -> Bool {
if lhs.0 != rhs.0 { return lhs.0 < rhs.0 }
if lhs.1 != rhs.1 { return lhs.1 < rhs.1 }
return lhs.2 < rhs.2
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reads from left-to-right? Like how version numbers work? But doesn’t that tie to a western way of reading? < has subtleties that == doesn’t I think?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it reads from first-to-last. Even if you write in an RTL language, (Int, Double) still has .0 be the Int and .1 be the Double. And this even matches String behavior; the first character in a String is still the first character in a String regardless of whether the character belongs to an LTR or RTL language; the RTL nature of the language only affects how it's displayed on screen. Presumably if you used a Tuple to represent some data that gets displayed on screen, in an RTL locale you'd display the .0 element on the right and the .1 element on the left.

@warn_unused_result
public func <= <A: Comparable, B: Comparable, C: Comparable>(lhs: (A,B,C), rhs: (A,B,C)) -> Bool {
if lhs.0 != rhs.0 { return lhs.0 < rhs.0 }
if lhs.1 != rhs.1 { return lhs.1 < rhs.1 }
return lhs.2 <= rhs.2
}
@warn_unused_result
public func > <A: Comparable, B: Comparable, C: Comparable>(lhs: (A,B,C), rhs: (A,B,C)) -> Bool {
if lhs.0 != rhs.0 { return lhs.0 > rhs.0 }
if lhs.1 != rhs.1 { return lhs.1 > rhs.1 }
return lhs.2 > rhs.2
}
@warn_unused_result
public func >= <A: Comparable, B: Comparable, C: Comparable>(lhs: (A,B,C), rhs: (A,B,C)) -> Bool {
if lhs.0 != rhs.0 { return lhs.0 > rhs.0 }
if lhs.1 != rhs.1 { return lhs.1 > rhs.1 }
return lhs.2 >= rhs.2
}
```

## Impact on existing code

No existing code should be affected.

## Alternatives considered

I tested building a Ninja-ReleaseAssert build for tuples up to arity 12, but
that had a 171KiB codesize increase (5.5%). I have not tried any other arities.