How do I implement hash functions for arbitrary record types? Are there "mix" helpers I'm missing?

I asked this on StackOverflow first but perhaps it’s also good to discuss here: reason - How do I implement hash functions for arbitrary record types in ReScript? - Stack Overflow

I’ll copy in my question below.


I’m exploring ReScript for the first time. I want to build a hashmap using a record type as my key type, and I’m looking for guidance on implementing the hash function.

Here’s my ReScript code:

type pointer = { id: string, table: string, spaceId: option<string> }

module PointerHash = Belt.Id.MakeHashable({
  type t = pointer
  let hash = a => 0 /* How do I implement this? */
  let eq = (a, b) => 
  	a.id == b.id &&
  	a.table == b.table &&
  	switch (a.spaceId, b.spaceId) {
      | (Some(aid), Some(bid)) => aid == bid
      | _ => false
    }
})

I looked through the docs and searched online but didn’t find any guidance about how to implement the hash function.

In other programming languages like eg Java that expect you to implement hashCode(), there are ubiquitous tools to support composing existing hash functions.

class Pointer {
  public final id: String
  public final table: String
  public final @Nullable spaceId: String
  /* omitting constructor, etc */
  @Override
  public int hashCode() {
    // Standard helper for implementing hashCode()
    return Objects.hash(this.id, this.table, this.spaceId);
  }
}

I looked at the implementation of Belt.HashMapString to see if there were any hints, and it looks like HashMapString uses caml_hash_mix_string:

external caml_hash_mix_string : seed -> string -> seed  = "caml_hash_mix_string"
external final_mix : seed -> seed = "caml_hash_final_mix"
let hash (s : key) =   
  final_mix  (caml_hash_mix_string 0 s )

What is the most idiomatic way to access and compose “hash mix” functions? Are these available with a nice interface from ReScript?

There’s no built-in facility, so you’ll have to rely on traditional knowledge. However, you likely don’t want a hash map. Try just using a comparator, which is much easier to get right. It’s unclear that a hash map is more performant than the regular mutable map in Belt.