Weird error with module type

I’m getting this error and I do not understand why, made this minimal repro:

module M: {
  let f: (~a: option<int>=?) => unit
} = {
  let f = (~a: option<int>=?) => {
    ()
  }
}

which gives this error:

  Signature mismatch:
  ...
  Values do not match:
    let f: (~a: int=?) => unit
  is not included in
    let f: (~a: option<int>=?) => unit

what am I doing wrong and what is it trying to tell me?

The sintax for optional arguments is different for signatures and actual implementation. You don’t specify the option part in the signature.

module M: {
  let f: (~a: int=?) => unit
} = {
  let f = (~a: option<int>=?) => {
    ()
  }
}

M.f(~a=1)

You can read more about signatures and type annotations for functions with labeled arguments here.

2 Likes

Although mxthevs offered a valid solution, I’d like to add a more thorough explanation because I get this question very often (especially from rescript beginners).

The difference between the two signatures lies in the “perspective”.

When you type a function in the implementation: (let f = (~a: option<int>=?) => ...) you specify the types, as the function from the “inside” would perceive the given arguments. Inside the function you need to be able to differentiate, if the argument was given or not, so there is no way around option.

On the other hand, if you specify the signature in a module-type or a resi-file, this is how the function is seen from the “outside”. Therefore, the caller doesn’t need to know or handle any option type. The only thing relevant from the “outside” is, that I may or may not pass an argument of a specific type to the function.

In the same manner it’s also possible and sometimes recommended to use more abstract/generic types in the interface vs the actual implementation, to hide complexity and provide abstractions.

6 Likes