[swift-evolution] Proposal: Implement == and < for tuples where possible, up to some high arity

plx plxswift at icloud.com
Thu Dec 10 12:00:07 CST 2015


If these are to be added to the standard library it seems prudent to put them in under their own names — e.g. `#<`, and then `#==` for consistency — reserving `<` for any user-defined logic. 

The reason I suggest this is that at least at present, the existing method-dispatch logic seems likely to lead to unintended consequences if e.g. `<` is defined so broadly; if you can paste the below into a playground you can see the kind of non-intuitive behavior that can crop up here:

// proposed function, arity-2
func == <A:Equatable,B:Equatable> (lhs: (A,B), rhs: (A,B)) -> Bool {
  return lhs.0 == rhs.0 && lhs.1 == rhs.1
}

// proposed function, arity-2
func < <A:Comparable,B:Comparable> (lhs: (A,B), rhs: (A,B)) -> Bool {
  if lhs.0 != rhs.0 { return lhs.0 < rhs.0 }
  return lhs.1 < rhs.1
}

// concrete `<` implementation:
func < (lhs: (String,String), rhs: (String,String)) -> Bool {
  switch customComparison(lhs.0, rhs.0) {
  case .OrderedAscending: return true
  case .OrderedDescending: return false
  case .OrderedSame:
    return customComparison(lhs.1, rhs.1) == .OrderedAscending
  }
}

// helper for `<` 
private func customComparison(lhs: String, _ rhs: String) -> NSComparisonResult {
  return (lhs as NSString).compare(rhs, options: [.CaseInsensitiveSearch, .NumericSearch])
}

// trouble begins here:
extension SequenceType {
  
  func extractOrderedPairs<K:Comparable,Q:Comparable>(extractor: (Self.Generator.Element) -> (K,Q)) -> [(K,Q)] {
    return self.map(extractor).sort() {
      (l:(K,Q),r:(K,Q)) -> Bool
      in
      return l < r
    }
  }
  
  func extractOrderedPairs<K,Q>(
    isOrderedBefore: ((K,Q),(K,Q)) -> Bool,
    _ extractor: (Self.Generator.Element) -> (K,Q)) -> [(K,Q)] {
      return self.map(extractor).sort(isOrderedBefore)
  }
  
}

// helper for below:
extension String {
  
  var fileNamePieces: (String,String) {
    get {
      let lastComponent = (self as NSString).lastPathComponent
      return (
        (lastComponent as NSString).stringByDeletingPathExtension,
        (lastComponent as NSString).pathExtension
      )
    }
  }
  
}

let someFileNames = ["Image.png", "Image.jpeg", "Image.bmp", "Essay1.txt", "Essay11.txt", "Essay2.txt"]

let pairsV1 = someFileNames.extractOrderedPairs() { $0.fileNamePieces }
let pairsV2 = someFileNames.extractOrderedPairs(<) { $0.fileNamePieces }
// ^ note the `<` that gets passed-in

var pairV1Mismatches = 0
for index in 0..<(pairsV1.count-1) {
  if !(pairsV1[index] < pairsV1[index + 1]) {
    print("found mismatch: \(pairsV1[index]) !< \(pairsV1[index + 1])!")
    pairV1Mismatches += 1
  }
}
pairV1Mismatches // 1

var pairV2Mismatches = 0
for index in 0..<(pairsV2.count-1) {
  if !(pairsV2[index] < pairsV2[index + 1]) {
    print("found mismatch: \(pairsV2[index]) !< \(pairsV2[index + 1])!")
    pairV2Mismatches += 1
  }
}
pairV2Mismatches // 0

// END SNIPPET

Yes this is very contrived — and hopefully not something someone would write intentionally! — and yes if you understand the various scopes and dispatch rules involved you can work out why this happens, but I don’t think it’s intuitive (and thus it’s easy to stumble upon accidentally). I also think that with additional layers of generics / protocol extensions / customized implementations / etc. involved there’ll be a few more flavors of non-intuitive outcomes one can possibly stumble into.

Whence the suggestion that if these are to go into the standard library, put them in under distinct names, rather than as e.g. `==` and `<`. (FWIW `==` is by itself less problematic, but the comparisons likely customization targets, hence my concerns).

> On Dec 10, 2015, at 1:14 AM, Kevin Ballard via swift-evolution <swift-evolution at swift.org> wrote:
> 
> I've submitted this proposal as https://github.com/apple/swift-evolution/pull/45
> 
> -Kevin Ballard
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution



More information about the swift-evolution mailing list