I’m attempting to build a small runtime using a simple action/reducer pattern but I’m getting a confusing type error: The parameter cannot be eliminated in the result type. Please bind the argument to a module identifier.. I’m unsure how to interpret the error, though it’s something to do with the action type and its usage in higher order functions.
The actual module is bigger than this, but here is the smallest reproducible example:
Ah, of course, it’s the module that needs binding, I was under the impression that perhaps a function or a type needed to be bound to the module. Thank you for this!
Still, I would be interested to learn what causes this quirk, as it does strike me as odd that an anonymous module causes a type error.
I googled the error, seems to exist in OCaml too. Even the creator of OCaml, Xavier Leroy, had a comment on a github issue about it. Something about types and lexical order…?
The module doesn’t actually need to be bound, although binding the module is one way to fix it.
This issue is that every new type needs a name with a path, for example: Module1.Module2.type1. If you create a new type inside of an anonymous module, and then export that module (via a functor in this case), then the type checker doesn’t have a valid path for that type anymore.
Defining the types outside of the anonymous module, and then aliasing them inside the module, will also fix the problem:
type action = SetX(float) | SetY(float)
type state = {x: float, y: float}
module App = Make({
type action = action
type state = state
let reduce = (action: action, state: state) => {
switch action {
| SetX(x) => {...state, x: x}
| SetY(y) => {...state, y: y}
}
}
})
Another way to solve the problem is to simply not export the type implementations by hiding them with a module signature. This will compile:
module type Arg = {
type action
type state
let reduce: (action, state) => state
}
module type S = {
type action
type state
type effects = ref<array<state => state>>
let reduce: (action, state) => state
let useEffect: (effects, state => action) => unit
}
module Make =
(M: Arg)
: (S with type action = M.action and type state = M.state)
=> { /* implementation goes here */ }
The downside to this is that the type constructors won’t be available outside of the module, which probably isn’t what you want in this case.