Skip to content

Instantly share code, notes, and snippets.

@seanwoodward
Last active March 19, 2024 04:36
Show Gist options
  • Save seanwoodward/44e2f5aca0dda994e9595eee58750f65 to your computer and use it in GitHub Desktop.
Save seanwoodward/44e2f5aca0dda994e9595eee58750f65 to your computer and use it in GitHub Desktop.
Tuple struct using Parameter Packs to facilitate things like using a tuple as keys in dictionaries or values in sets and heaps.
#if swift(>=5.9)
import Foundation
public struct Tuple<T, each U> {
private let head: T
private let tail: (repeat (each U))
public var values: (T, repeat each U) {
(head, repeat each tail)
}
public init(_ head: T, _ tail: repeat each U) {
self.head = head
self.tail = (repeat (each tail))
}
}
extension Tuple: Sendable where T: Sendable, repeat (each U): Sendable {}
extension Tuple: Hashable where T: Hashable, repeat (each U): Hashable {
public func hash(into hasher: inout Hasher) {
hasher.combine(self.head)
repeat (hasher.combine(each self.tail))
}
}
extension Tuple: Equatable where T: Equatable, repeat (each U): Equatable {
private struct NotEqual: Error {}
public static func == (lhs: Tuple<T, repeat (each U)>, rhs: Tuple<T, repeat (each U)>) -> Bool {
guard lhs.head == rhs.head else { return false }
func isEqual<V: Equatable>(_ left: V, _ right: V) throws {
guard left == right else { throw NotEqual() }
return
}
do {
repeat try isEqual(each lhs.tail, each rhs.tail)
} catch {
return false
}
return true
}
}
extension Tuple: Comparable where T: Comparable, repeat (each U): Comparable {
// https://github.com/apple/swift-evolution/blob/1b0b339bc3072a83b5a6a529ae405a0f076c7d5d/proposals/0015-tuple-comparison-operators.md
private struct NotOrderBefore: Error { }
// throwing short circuits the checks
public static func < (lhs: Tuple<T, repeat (each U)>, rhs: Tuple<T, repeat (each U)>) -> Bool {
func isOrderedBeforeThrows<V: Comparable>(_ left: V, _ right: V) throws {
if left != right {
guard left < right else { throw NotOrderBefore() }
}
}
do {
try isOrderedBeforeThrows(lhs.head, rhs.head)
repeat try isOrderedBeforeThrows(each lhs.tail, each rhs.tail)
} catch {
return false
}
return true
}
// exhaustive
// public static func < (lhs: Tuple<T, repeat (each U)>, rhs: Tuple<T, repeat (each U)>) -> Bool {
// var results: [(notEqual: Bool, orderedBefore: Bool)] = [ (lhs.head != rhs.head, lhs.head < rhs.head)]
//
// func isOrderedBefore<V: Comparable>(_ left: V, _ right: V) {
// results.append((left != right, left < right))
// }
//
// repeat isOrderedBefore(each lhs.tail, each rhs.tail)
//
// return results.first(where: { $0.notEqual})?.orderedBefore ?? results.last!.orderedBefore
// }
}
extension Tuple: CustomStringConvertible {
public var description: String {
func describe<V>(_ value: V) {
if let _ = value as? any StringProtocol {
let valueString = String(describing: value)
// allows the presentation of a string value with quotes
// e.g head = "red", the description will be "red" with quotes
// not just, red, without quotes.
descriptions.append("\(valueString.debugDescription)")
} else {
descriptions.append("\(String(describing: value))")
}
}
var descriptions: [String] = []
describe(self.head)
repeat (describe(each tail))
return descriptions.count == 1 ? "\(descriptions[0])" : "(\(descriptions.joined(separator: ", ")))"
}
}
extension Tuple: CustomDebugStringConvertible {
public var debugDescription: String {
"values = \(self.description)"
}
}
#endif
@randomeizer
Copy link

Neat. I need to dig into them a bit I think.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment