@chenglou is probably correct that Belt.Map.Dict
may not be the best tool for this job. But for the sake of learning, here’s how you would solve the problem in the OP:
You need to use a “locally abstract type.” As you can see from the error message, polymorphic type parameters in function signatures aren’t accessible inside of the function bodies. However, you can declare a fresh abstract type alongside the arguments and use that inside the function.
// note type k and v
let foo = (type k v, value: v, projection: v => k) => {
module KeyCmp = Belt.Id.MakeComparable({
type t = k
let cmp = compare
})
let dict = Belt.Map.Dict.empty
let resDict = Belt.Map.Dict.set(dict, projection(value), value, ~cmp=KeyCmp.cmp)
Js.log(resDict)
}
Locally abstract types are a bit of an advanced feature and make maintaining the code more complicated, so I’m not sure this is a good example that justifies using them. It would make more sense to define your KeyCmp
module somewhere before calling your foo
function, and just passing the cmp
value as a parameter. Then no type annotations are needed anymore.
let bar = (value, projection, ~cmp) => {
let dict = Belt.Map.Dict.empty
let resDict = Belt.Map.Dict.set(dict, projection(value), value, ~cmp)
Js.log(resDict)
}
Playground link.
You probably don’t want to be using functors inside functions most of the time. I assume all of your types are defined somewhere in a module already, so it’s much simpler to just call Belt.Id.MakeComparable
alongside those type definitions, and then reference that whenever you need to use it. That’s simpler to maintain, and also makes more sense from a performance perspective.
A side note: if you’re concerned about performance, then the built-in polymorphic compare
function is probably not a good idea. You should provide your own compare function that’s specific for the type you’re using.