Continuing the discussion from Generic numeric module, Problem with types:
I noticed that open seems to be the only way to bring constructors and record fields into scope. I tried:
module M = struct
type t = { foo : int }
let ( = ) a b = a.foo = b.foo
end
let () =
(* None of the following ways to bring "foo" into scope
works in this context: *)
(* let foo = M.foo in *)
(* let foo (x : M.t) = x.foo in *)
(* let open M in *)
(* let open (M : sig type t = M.t end) in *)
(* Instead we have to copy the whole type definition here: *)
let open (M : sig type t = { foo : int } end) in
let _ = { foo = 5 } in
assert (1 = 1)
I find the syntax open (Module_name : sig type type_name = { … } end) unsatisfying to an extent where I would rather want to avoid this.
What is the idiomatic approach?
- Outside a module, work with functions instead of constructors and record fields?
- Design modules for
opening, e.g. by avoiding ambiguous names and operators like=in the example above? - Always provide helper functions in addition to constructors and record fields?
- Always qualify constructors and record fields or use type annotations (see below)?
Am I overlooking an easy approach to this issue? Perhaps it’s not possible to nicely bring specific constructors or record fields into scope? If that is the case, then I probably have to resort to use type annotations or qualification:
- let _ = { foo = 5 } in
+ let (_ : M.t) = { foo = 5 } in
Or:
- let _ = { foo = 5 } in
+ let _ = { M.foo = 5 } in
But this is also a bit unsatisfying when I make large use of specific records or fields in my code.