Belt.Set
does sound like what you want. It was designed to be fast and small when compiled to JavaScript, but I'm not entirely convinced it was designed to be used by people.
Here's an example showing how it can be used with your custom type.
// Create an "Id" module, to basically give the comparison function a type
module FooComparable =
Belt.Id.MakeComparable({
type t = fooVariant;
let cmp = Pervasives.compare;
});
// Define an alias for the set type
type fooSet = Belt.Set.t(fooVariant, FooComparable.identity);
// Initialize a set with one item
let s0 = Belt.Set.fromArray([|Foo|], ~id=(module FooComparable));
// Immutably add to the set
let s1 = Belt.Set.add(s0, Bar);
// Test for the presence of a value
Belt.Set.has(s1, Foo);
Compare this with the standard set data structure, which is probably slightly slower and significantly larger when compiled to JavaScript:
// Create a specialized set module, again basically just to give the
// comparison function a type
module FooSet = Set.Make({
type t = fooVariant;
let compare = Pervasives.compare;
});
// Define an alias for the set type
type fooSet = FooSet.t;
// Initialize a set with one item
let s0 = FooSet.of_list([Foo]);
// Immutably add to the set
let s1 = FooSet.add(Bar, s0);
// Test for the presence of a value
FooSet.mem(Foo, s1);
You can play around with both of these on the Reason playground.
As you can see from the examples, both of these require you to create some kind of module instead of just passing the comparison function directly to the creation function. This is to make sure that the same comparison function is used for all operations on the same set. Otherwise, if for example you have two separate sets with the same type only parameterized by the type contained, and initialized with different comparison function, it would not be obvious what the union or intersection of the two sets should be.
Also note that while Pervasives.compare
is conveniently used as the comparison function here, and might have been used for all sets, thereby seemingly bypassing the issue above, it is not a total function. If you try to compare two functions, for example, it will just crash, and if you try to compare a cyclic data structure it will not terminate. It is therefore necessary to be able to use, and a good idea to use, a custom comparison function for more complex types.