Ah, now I see the misconception:
resi files define the public interface for the res file of the same name.
Fore example:
// A.resi
type x
let b: string
// A.res
type x = string
let a = "!"
let b = "Hi"++a
A.resi defines the “shape” of the module A.res, how it is perceived from the outside: There is a type called x, which we don’t know the actual implementation of and there is a string value named b. Because of the interface file, the value a is not accessible from other modules.
It’s somehow similar to node’s module exports.
On the other hand, the module A.res must implement everything, which is stated in A.resi!
module type is a “first class citizen” just like type.
In your case you specify in the interface file Shared.resi, that the module Shared.res contains a module type Id of the given shape.
But your actual implementation defines a module called UUID and uses the module type Id, which is not specified yet in the implementation.
So, you either want this:
// Shared.resi
module UUID: {
type t
let fromString: (~s: string) => t
let toString: (~id: t) => string
let generate: () => t
}
// Shared.res
module UUID = {
type t = string
let fromString = (~s) => s
let generate = () => fromString(~s=Uuid.V4.make())
let toString = (~id) => id
}
Or you define the module type in your implementation:
// Shared.resi
module type Id = {
type t
let fromString: (~s: string) => t
let toString: (~id: t) => string
let generate: () => t
}
module UUID: Id
// Shared.res
module type Id = {
type t
let fromString: (~s: string) => t
let toString: (~id: t) => string
let generate: () => t
}
module UUID = {
type t = string
let fromString = (~s) => s
let generate = () => fromString(~s=Uuid.V4.make())
let toString = (~id) => id
}
Defining a module type Id makes sense, if you plan on having several modules implementing this interface.
Edit: Is there a text or wording in the docs, which you feel might have mislead you? Or should be improved for better understanding?