Implementing a hash combiner in Swift - swift

I'm extending a struct conform to Hashable. I'll use the DJB2 hash combiner to accomplish this.
To make it easy to write hash function for other things, I'd like to extend the Hashable protocol so that my hash function can be written like this:
extension MyStruct: Hashable {
public var hashValue: Int {
return property1.combineHash(with: property2).combineHash(with: property3)
}
}
But when I try to write the extension to Hashable that implements `combineHash(with:), like this:
extension Hashable {
func combineHash(with hashableOther:Hashable) -> Int {
let ownHash = self.hashValue
let otherHash = hashableOther.hashValue
return (ownHash << 5) &+ ownHash &+ otherHash
}
}
… then I get this compilation error:
/Users/benjohn/Code/Nice/nice/nice/CombineHash.swift:12:43: Protocol 'Hashable' can only be used as a generic constraint because it has Self or associated type requirements
Is this something that Swift won't let me do, or am I just doing it wrong and getting an unhelpful error message?
Aside A comment from JAL links to a code review of a swift hash function that is also written by Martin who provides the accepted answer below! He mentions a different hash combiner in that discussion, which is based on one in the c++ boost library. The discussion really is worth reading. The alternative combiner has fewer collisions (on the data tested).

Use the method hash(into:) from the Apple Developer Documentation:
https://developer.apple.com/documentation/swift/hashable
struct GridPoint {
var x: Int
var y: Int
}
extension GridPoint: Hashable {
static func == (lhs: GridPoint, rhs: GridPoint) -> Bool {
return lhs.x == rhs.x && lhs.y == rhs.y
}
func hash(into hasher: inout Hasher) {
hasher.combine(x)
hasher.combine(y)
}
}

You cannot define a parameter of type P if P
is a protocol which has Self or associated type requirements.
In this case it is the Equatable protocol from which Hashable
inherits, which has a Self requirement:
public static func ==(lhs: Self, rhs: Self) -> Bool
What you can do is to define a generic method instead:
extension Hashable {
func combineHash<T: Hashable>(with hashableOther: T) -> Int {
let ownHash = self.hashValue
let otherHash = hashableOther.hashValue
return (ownHash << 5) &+ ownHash &+ otherHash
}
}

Related

Cache results of Swift hash(into:) Hashable protocol requirement

I have a class being heavily used in Sets and Dictionaries.
For performance reasons this class implements Hashable in a old way and caches the computed hash:
let hashValue: Int
init(...) {
self.hashValue = ...
}
In Xcode 10.2 I see a warning, that hashValue is deprected and will stop being a protocol requirement soon.
What bothers me is a lack of ability to cache the computed hash anyhow, because hash(into:) does not return anything.
func hash(into hasher: inout Hasher) {
hasher.combine(...)
}
Consider the following example in a playground
class Class: Hashable {
let param: Int
init(param: Int) {
self.param = param
}
static func ==(lhs: Class, rhs: Class) -> Bool {
return lhs.param == rhs.param
}
public func hash(into hasher: inout Hasher) {
print("in hash")
hasher.combine(param)
}
}
var dict = [Class: Int]()
let instance = Class(param: 1)
dict[instance] = 1
dict[instance] = 2
You will see the following logs
in hash
in hash
in hash
I have no idea, why we see 3 calls instead of 2, but we do =).
So, every time you use a same instance as a dictionary key or add this instance into a set, you get a new hash(into:) call.
In my code such an overhead turned out to be very expensive. Does anyone know a workaround?
One option is to create your own Hasher, feed it the "essential components" of your instance and then call finalize() in order to get out an Int hash value, which can be cached.
For example:
class C : Hashable {
let param: Int
private lazy var cachedHashValue: Int = {
var hasher = Hasher()
hasher.combine(param)
// ... repeat for other "essential components"
return hasher.finalize()
}()
init(param: Int) {
self.param = param
}
static func ==(lhs: C, rhs: C) -> Bool {
return lhs.param == rhs.param
}
public func hash(into hasher: inout Hasher) {
hasher.combine(cachedHashValue)
}
}
A couple of things to note about this:
It relies on your "essential components" being immutable, otherwise a new hash value would need calculating upon mutation.
Hash values aren't guaranteed to remain stable across executions of the program, so don't serialise cachedHashValue.
Obviously in the case of storing a single Int this won't be all that effective, but for more expensive instances this could well help improve performance.

Equatable alternatives for Void in Swift

I'm trying to add conditional Equatable conformance to a type say Box<T>, if T is Equatable. Since Swift.Void is not Equatable, Box<Void> is not Equatable.
struct Box<T> {
//...
}
extension Box: Equatable where T: Equatable {
}
I can define a new type like below as a solution:
public struct Empty: Equatable {
}
And then use Box<Empty> instead of Box<Void> and that would work. But, wondering if there any alternatives to introducing a new type.
Update:
I tried this but it doesn't work
struct Box<T> {
//...
}
extension Box: Equatable where T: Equatable {
static func ==(lhs: Box<T>, rhs: Box<T>) -> Bool {
return true
}
}
extension Box where T == Void {
static func ==(lhs: Box<T>, rhs: Box<T>) -> Bool {
return true
}
}
I get an error during compilation: FooBarBaz does not conform to protocol Equatable
enum FooBarBaz: Equatable {
case success(box: Box<Void>)
// case success(box: Box<Int>) compiles as expected.
}
Please note that I'm using Swift 4.1
I get an error during compilation: FooBarBaz does not conform to protocol Equatable
This half-answer focuses on explaining why the approach you've tried yourself will not (yet) work.
There is a limitation, currently, with conditional conformances, that will limit you from using this particular technique to achieve your goal. Citing SE-0143: Conditional conformances, implemented in Swift 4.1:
Multiple conformances
Swift already bans programs that attempt to make the same type conform
to the same protocol twice, e.g.:
...
This existing ban on multiple conformances is extended to conditional
conformances, including attempts to conform to the same protocol in
two different ways.
...
The section overlapping conformances describes some of the
complexities introduced by multiple conformances, to justify their
exclusion from this proposal. A follow-on proposal could introduce
support for multiple conformances, but should likely also cover
related features such as private conformances that are orthogonal to
conditional conformances.
Which wont allow us to construct multiple conditional conformances as e.g.:
struct Box<T> { }
extension Box: Equatable where T == Void {
static func ==(lhs: Box<T>, rhs: Box<T>) -> Bool {
return true
}
}
// error: redundant conformance of 'Box<T>' to protocol 'Equatable'
extension Box: Equatable where T: Equatable {
static func ==(lhs: Box<T>, rhs: Box<T>) -> Bool {
return true
}
}
On the other hand, if we look at your own example:
struct Box<T> { }
extension Box: Equatable where T: Equatable {
static func ==(lhs: Box<T>, rhs: Box<T>) -> Bool {
return true
}
}
extension Box where T == Void {
static func ==(lhs: Box<T>, rhs: Box<T>) -> Bool {
return true
}
}
// error: type 'Void' does not conform to protocol 'Equatable'
func foo<T: Equatable>(_ _: T) { print(T.self, "is Equatable") }
foo(Box<Void>())
Swift accurately identifies that Box<Void> is not Equatable: the extension Box where T == Void will not mean that Box<Void> conforms to Equatable, as it does not leverage a conditional conformance of Box to Equatable when T is Void (it just provides a == method in case T is Void.
Conditional conformances express the notion that a generic type will
conform to a particular protocol only when its type arguments meet
certain requirements.
As a side note, the following example yields expected results:
struct Box<T> { }
extension Box: Equatable where T == () {
static func ==(lhs: Box<T>, rhs: Box<T>) -> Bool {
return true
}
}
func foo<T: Equatable>(_ _: T) { print(T.self, "is Equatable") }
foo(Box<Void>()) // Box<()> is Equatable
whereas, peculiarly, replacing the conditional conformance of Box to Equatable if T == () with the typedef of (), namely Void, crashes the compiler:
struct Box<T> { }
extension Box: Equatable where T == Void {
static func ==(lhs: Box<T>, rhs: Box<T>) -> Bool {
return true
}
}
func foo<T: Equatable>(_ _: T) { print(T.self, "is Equatable") }
foo(Box<Void>()) // compiler crash
Assertion failed: (isActuallyCanonicalOrNull() && "Forming a CanType
out of a non-canonical type!"), function CanType,
file /Users/buildnode/jenkins/workspace/oss-swift-4.1-package-osx/swift/include/swift/AST/Type.h,
line 393.
...
Edit: apparently is a (now resolved) bug:
SR-7101: Compiler crash when implementing a protocol for an enum using generics

How should I compute an hashValue?

I saw several examples of implementations of the Hashable variable hashValue, such as these:
extension Country: Hashable {
var hashValue: Int {
return name.hashValue ^ capital.hashValue ^ visited.hashValue
}
}
var hashValue: Int {
return self.scalarArray.reduce(5381) {
($0 << 5) &+ $0 &+ Int($1)
}
}
var hashValue : Int {
get {
return String(self.scalarArray.map { UnicodeScalar($0) }).hashValue
}
}
and so on.
In some cases OR, XOR or BITWISE operators are used. In other cases, other algorithms, map or filter functions etc.
Now I'm a bit confused. What is the rule of thumb to compute a good hashValue?
In the simplest case with two string variables, should I combine these two with an OR operator?
From Swift 4.2, you can use Hasher
Swift 4.2 implements hashing based on the SipHash family of
pseudorandom functions, specifically SipHash-1-3 and SipHash-2-4, with
1 or 2 rounds of hashing per message block and 3 or 4 rounds of
finalization, respectively.
Now if you want to customize how your type implements Hashable, you
can override the hash(into:) method instead of hashValue. The
hash(into:) method passes a Hasher object by reference, which you call
combine(_:) on to add the essential state information of your type.
// Swift >= 4.2
struct Color: Hashable {
let red: UInt8
let green: UInt8
let blue: UInt8
// Synthesized by compiler
func hash(into hasher: inout Hasher) {
hasher.combine(self.red)
hasher.combine(self.green)
hasher.combine(self.blue)
}
// Default implementation from protocol extension
var hashValue: Int {
var hasher = Hasher()
self.hash(into: &hasher)
return hasher.finalize()
}
}

Extend existing (generic) swift class to be hashable

I have some class that i want to put into a dictionary however that class is does not conform to Hashable i cant use it as a key in a Swift dictionary. Since its a class it can be identified by its location in memory and im happy to use that its identifier, the type itself doesnt fall into the value semantics world anyway.
Therefore i declare an extension to make it so
extension SomeGenericType : Hashable {
public var hashValue: Int {
return unsafeAddressOf(self).hashValue
}
}
This seems ok, however Hashable inherhits from Equatable so i need to implement tha too, my first try:
public func ==(lhs: SomeGenericType, rhs: SomeGenericType) -> Bool {
return unsafeAddressOf(lhs) == unsafeAddressOf(rhs)
}
Errors with
"Reference to generic type 'SomeGenericType' requires arguments in <...>"
...fair enough so lets do that
public func ==<T : SomeGenericType >(lhs: T, rhs: T) -> Bool {
return unsafeAddressOf(lhs) == unsafeAddressOf(rhs)
}
Now it says
"Reference to generic type 'SomeGenericType' requires arguments in <...>"
Hmm so i can make this work for all SomeGenericType's regardless of what type it gets. Maybe we can just put AnyObject in there?
public func ==<T : SomeGenericType<AnyObject>>(lhs: T, rhs: T) -> Bool {
return unsafeAddressOf(lhs) == unsafeAddressOf(rhs)
}
Ok now == is happy but apparantly im not implementing Hashable properly as there is now a error on my hashable extension saying:
"Type 'SomeGenericType<T>' does not conform to protocol 'Equatable'"
Ive tried fiddling with a constrained Extension on SomeGenericType but i cant seem to make a constrained extension of a type adopt another protocol, the language grammar doesnt seem to allow it, so Im in a bit of pickle here
Edit, for reference SomeGenericType is defined as follows:
class SomeGenericType<T> {
}
The correct syntax is
public func ==<T>(lhs: SomeGenericType<T>, rhs: SomeGenericType<T>) -> Bool {
return unsafeAddressOf(lhs) == unsafeAddressOf(rhs)
}
The operands need to be instances of SomeGenericType for the
same type placeholder T.
For Swift 3, use ObjectIdentifier instead of unsafeAddressOf.
This is an older question but I recently run into a similar problem, except I had a struct.
You might want to implement 4 different infix operator functions:
// #1
public func ==<T>(lhs: SomeGenericType<T>, rhs: SomeGenericType<T>) -> Bool {
return lhs === rhs // it's a class right - check the reference
}
// #2
public func !=<T>(lhs: SomeGenericType<T>, rhs: SomeGenericType<T>) -> Bool {
return !(lhs === rhs)
}
// #3
public func ==<T, U>(lhs: SomeGenericType<T>, rhs: SomeGenericType<U>) -> Bool {
return lhs.hashValue == rhs.hashValue
}
// #4
public func !=<T, U>(lhs: SomeGenericType<T>, rhs: SomeGenericType<U>) -> Bool {
return lhs.hashValue != rhs.hashValue
}
Problem solved? The program should use these and not the generic functions from the stdlib.
You could also check everything by it's hashValue. ;)

Type in conformance requirement does not refer to a generic parameter or associated type

In Swift 2 I have the following protocols
protocol Fightable {
// return true if still alive, false if died during fight
func fight (other: Fightable) -> Bool
}
protocol Stats {
var defense: Int { get }
var attack: Int { get }
}
I can implement a protocol extension for Fightable to provide a shared implementation of fight across all value types which conform to Stats if I change the type signature of fight to
func fight (other: Self) -> Bool
and implement an extension as
extension Fightable where Self : Stats {
func fight (other: Self) -> Bool {
return self.defense > other.attack
}
}
The problem with the above implementation is that it requires the value types to be the same (Humans can't fight Goblins). My current goal is to implement a protocol extension that provides a default implementation of fight for any combination of value types as long as they implement Stats.
The following code
extension Fightable where Fightable : Stats {
func fight (other: Fightable) -> Bool {
return self.defense > other.attack
}
}
Produces the error
Type 'Fightable' in conformance requirement does not refer to a generic parameter or associated type
How can I make sure the other Fightable type also conforms to Stats for this extension?
I'm using Xcode 7 beta 1.
I'm sorry but I misunderstood your problem. So if I understand you right (hopefully) it is impossible to get a default implementation of the fight function by a protocol extension (at least with these constraints). Because if you want other conform to Fightable and Stats it isn't the previous function anymore where other could be any Fightable. So it doesn't implement the required function.
As workaround I would suggest (taking your existing code):
protocol Fightable {
// return true if still alive, false if died during fight
func fight (other: Fightable) -> Bool
}
protocol Stats {
var defense: Int { get }
var attack: Int { get }
}
extension Fightable where Self : Stats {
// directly conforming to the protocol
func fight (other: Fightable) -> Bool {
if let otherStat = other as? Stats {
return self.defense > otherStat.attack
}
// providing a good way if other does not conform to Stats
return false
}
}
One way which work's for me is making a typealias in your Fightable protocol. So you can constraint the parameter of the fight function in your protocol extension. Due to this situation you also have to make your fight function generic (Fightable can than only be used as generic constraint).
In code it looks like this:
protocol Fightable {
// make typealias
typealias F = Fightable
// make function generic
func fight<F: Fightable>(other: F) -> Bool
}
protocol Stats {
var defense: Int { get }
var attack: Int { get }
}
// constraint F and Self
extension Fightable where F : Stats, Self: Stats {
func fight(other: F) -> Bool {
return self.defense > other.attack
}
}
// example of an implementation
struct Human: Fightable {
func fight<F: Fightable>(other: F) -> Bool {
return true
}
}