Module types are a way to limit what outside code can “see” about the module. Because T.t is abstract, any code outside of module M is only able to see that M.t is abstract as well.
One way to expose the implementation of M.t is to change M: T to M: T with type t = int:
module type T = {
type t
let log: t => unit
}
module M: T with type t = int = {
type t = int
let log = n => Js.log(n)
}
M.log(1) // This works now.