Overloading global function = lots of boiler plate

I am writting a set of hashValue functions to make it easier for classes/structs to conform to Hashable and to provide consistency and reliability of implementation. I wanted to cover a lot of use cases and the best I came up with is:


    // MARK: Hash functions (by iterating `hash = 31 &* hash &+ h.hashValue` for each `h`) 


//    /// Returns: An integer that is relatively unique and therefore suitable for hash tables (dictionaries)./
//    /// Hash value for nil or empty `= 1`.
//    static func hashValue(nilLiteral: () = nil) -> Int {
//        return 1
//    }

    /// Returns: An integer that is relatively unique and therefore suitable for hash tables (dictionaries).
    /// Hash value for a hashable `= 31 &+ h.hashValue`.
    static func hashValue1Hashable<T: Hashable>(h: T) -> Int {
        return 31 &+ h.hashValue
    }

    /// Returns: An integer that is relatively unique and therefore suitable for hash tables (dictionaries).
    /// Hash value for two hashables `= 31 &* Function.hashValueHashables(h1) &+ h2.hashValue`.
    static func hashValue2Hashables<T1: Hashable, T2: Hashable>(h1: T1, _ h2: T2) -> Int {
        return 31 &* Function.hashValueHashables(h1) &+ h2.hashValue
    }

    /// Returns: An integer that is relatively unique and therefore suitable for hash tables (dictionaries).
    /// Hash value for an optional array of hashables, the given array defaults to nil wich has a hash value of 1.
    /// Start with hash = 1 and then for each `h` do `hash = 31 &* hash &+ h.hashValue`.
    /// If `hs` is empty or nil then resturns 1.
    static func hashValueHashables<T: Hashable>(hs: [T]? = nil) -> Int {
        guard let hs = hs else {
            return 1
        }
        var hash = 1
        for h in hs { hash = 31 &* hash &+ h.hashValue }
        return hash
    }

    /// Returns: An integer that is relatively unique and therefore suitable for hash tables (dictionaries).
    /// Hash value for an optional array of optional hashables.
    /// Start with hash = 1 and then for each `h` do `hash = 31 &* hash &+ h.hashValue`.
    /// If `hs` is empty or nil then resturn 1.
    /// If any `h` is nil then it is taken as having a hash value of 1.
    static func hashValueHashables<T: Hashable>(hs: [T?]?) -> Int {
        guard let hs = hs else {
            return 1
        }
        var hash = 1
        for h in hs { hash = 31 &* hash &+ (h == nil ? 1 : h!.hashValue) }
        return hash
    }

    /// Returns: An integer that is relatively unique and therefore suitable for hash tables (dictionaries).
    /// Hash value for a 'var arg' array of hashables.
    /// Start with hash = 1 and then for each `h` do `hash = 31 &* hash &+ h.hashValue`.
    static func hashValueHashables<T: Hashable>(hs: T...) -> Int {
        var hash = 1
        for h in hs { hash = 31 &* hash &+ h.hashValue }
        return hash
    }

    /// Returns: An integer that is relatively unique and therefore suitable for hash tables (dictionaries).
    /// Hash value for a 'var arg' array of optional hashables.
    /// Start with hash = 1 and then for each `h` do `hash = 31 &* hash &+ h.hashValue`.
    /// If any `h` is nil then it is taken as having a hash value of 1.
    static func hashValueHashables<T: Hashable>(hs: T?...) -> Int {
        var hash = 1
        for h in hs { hash = 31 &* hash &+ (h == nil ? 1 : h!.hashValue) }
        return hash
    }


Whilst the above works there are a number of things I don't like:


  1. I can't think of a way to write `hashValue` so that `hashValue()` and `hashValu(nil)` both return 1. My attempt is commented out at the top of the listing. Not that the function isn't generic since empty and nil both return 1 regardless of type. I have also tried approximated the function by giving `hashValueHashables<T: Hashable>(hs: [T]? = nil)` a default of `nil`, as shown, but that doesn't work either.
  2. Duplicate functions are needed to cover optional and non-optional variations, leading to potential cut and paste errors.
  3. Duplicate functions are needed to cover array and var arg variations, leading to potential cut and paste errors.
  4. The common specializations `hashValue1Hashable<T: Hashable>(h: T)` and `hashValue2Hashables<T1: Hashable, T2: Hashable>(h1: T1, _ h2: T2)` require unique names which means that they have to be manually selected (making their use unlikely in practice).
  5. You can't write an `arrayLiteral` version, which means that `hashValueHashables([1, 2, 3])` creates a temporary array which is disguarded. (I am assuming that `arrayLiteral` is more efficient, though it is yet another overload.)


Is there a better way of coding this?

It feels painful because you're struggling to make Swift do things it just doesn't do, at least not at the moment. You can't make arrays of Hashables be Hashable, or optional versions of Hashables be Hashable.


You can manually patch in support for specific cases, as you've done, but it's going to be high-maintenance and is never going to be a correct and general solution. Why stop at two layers of optionality and/or sequencing? What if someone has a three-layer structure they need to hash?


I think this is probably a good context for a YAGNI approach. Just fill out the basic system, and if you really need individual extensions for your own code, you can put them in ad hoc as you come across those cases. If the API is for an external client, just supply the basics and perhaps an extension system that lets them roll their own Frankenstein hashing routines.


There's no reason to give these functions different names. Just call them all the same thing and let Swift sort out the priority.


Some may disagree, but I don't like to see varargs used as syntactic sugar for homogeneous sequences (that is, just to free the caller from the need to create an array). Varargs creates an array wrapper itself, so there probably isn't much of a performance reason to prefer it. I agree that things like min(...) and max(...) are nice conveniences, but they're pretty much stand-alone. Here, supporting varags just adds to the multiplicative complexity.


Target sequences instead of arrays specifically. That will let you hash Sets, etc.


In summary:


// MARK: Hash functions (by iterating `hash = 31 &* hash &+ h.hashValue` for each `h`)

func myHashValue<T: Hashable>(h: T) -> Int {
    return 31 &+ h.hashValue
}

func myHashValue<T: Hashable>(h: T?) -> Int {
    return h.map(myHashValue) ?? 1
}

func myHashValue<S: SequenceType where S.Generator.Element: Hashable>(seq: S) -> Int {
    return seq.reduce(1) { (hash, elt) in
        return 31 &* hash &+ myHashValue(elt)
    }
}

Thanks for your suggestions, I am currently trialling the code below:


    static func hashValueHashables<S: SequenceType where S.Generator.Element: Hashable>(hs: S?) -> Int {
        guard let hs = hs else {
            return 1
        }
        return hs.reduce(1) {
            (hash, element) in 31 &* hash &+ element.hashValue
        }
    }

    static func hashValueHashables<T: Hashable>(hs: [T?]?) -> Int { // Can't have a sequence of optional Hashable
        guard let hs = hs else {
            return 1
        }
        return hs.reduce(1) {
            (hash, element) in 31 &* hash &+ (element?.hashValue ?? 1)
        }
    }

    static func hashValueHashables<T: Hashable>(hs: T...) -> Int {
        return hs.reduce(1) {
            (hash, element) in 31 &* hash &+ element.hashValue
        }
    }

    static func hashValueHashables<T: Hashable>(hs: T?...) -> Int {
        return hs.reduce(1) {
            (hash, element) in 31 &* hash &+ (element?.hashValue ?? 1)
        }
    }

    static func hashValueAnyObjects<S: SequenceType where S.Generator.Element: AnyObject>(hs: S?) -> Int {
        guard let hs = hs else {
            return 1
        }
        return hs.reduce(1) {
            (hash, element) in 31 &* hash &+ ObjectIdentifier(element).hashValue
        }
    }

    static func hashValueAnyObjects(hs: [AnyObject?]?) -> Int { // Can't have a sequence of optional AnyObject
        guard let hs = hs else {
            return 1
        }
        return hs.reduce(1) {
            (hash, element) in 31 &* hash &+ (element == nil ? 1 : ObjectIdentifier(element!).hashValue)
        }
    }

    static func hashValueAnyObjects(hs: AnyObject...) -> Int {
        return hs.reduce(1) {
            (hash, element) in 31 &* hash &+ ObjectIdentifier(element).hashValue
        }
    }

    static func hashValueAnyObjects(hs: AnyObject?...) -> Int {
        return hs.reduce(1) {
            (hash, element) in 31 &* hash &+ (element == nil ? 1 : ObjectIdentifier(element!).hashValue)
        }
    }


As you can see I have taken up a lot of your suggestions.


Some random thoughts:

  1. I wanted the vararg overload, this is the one I used most
  2. If you have the vararg overload then optimizations for 1, 2, etc. parameters need a unique name 😟
  3. I wanted the optional overloads because sometimes my fields are optional
  4. You can't have a sequence of optionals 😟
  5. I can't think of a way to automattically flatten arrays of arbitrary nesting, this will have to be a manual operation
  6. It is weird that you need the array variant and the varag varient and that they can't call each other 😟
  7. It is weird that protocols involving self have to be generic constraints, i.e. Hashable above, whereas non-self protocols are types, i.e. AnyObject above 😟


Thanks again.

func f(a: [Int]) -> Int {
  return a.reduce(0, combine: +)
}

func f(a: Int...) -> Int {
  return f(a)
}

f(1,2,3) // 6

With regard to #4, I think you might be able to do it this way:


func hashValue<T: AnyObject, S: SequenceType where S.Generator.Element == Optional<T>>(hs: S) -> Int {
  ...
}


But quite possibly there's some other issue I'm overlooking...

Thanks. When I tried it it didn't work for me. Must have made a typo.

Yes, that looks good. Thanks.

Overloading global function = lots of boiler plate
 
 
Q