The key of a hash table in OCaml can have any type that can be compared for equality and can be hashed to an integer. The "vanilla" interface uses the built-in polymorphic comparison to compare for equality, and a built-in polymorphic hash function.
The built-in polymorphic comparison function fails for function types and for cyclic values.
There is also a functorial interface that lets you define your own equality and hash functions. So you can even have a hash table with keys that contain functions if you do a little extra work (assuming you don't expect to compare the functions for equality).
It is not difficult to make a hash table with tuples as keys:
# let my_table = Hashtbl.create 64;;
val my_table : ('_weak1, '_weak2) Hashtbl.t = <abstr>
# Hashtbl.add my_table (1, 2) "one two";;
- : unit = ()
# Hashtbl.add my_table (3, 4) "three four";;
- : unit = ()
# Hashtbl.find my_table (1, 2);;
- : string = "one two"