module User = struct
type first_name = FirstName of string
type last_name = LastName of string
let with_first_name str = FirstName str
let with_last_name str = LastName str
let greet (FirstName fn) (LastName ln) = "Hello " ^ fn ^ " " ^ ln ^ "!"
let _ =
print_endline @@ User.greet (FirstName "John") (LastName "Doe");
let jane = User.with_first_name "Jane" in
let doe = User.with_last_name "Doe" in
print_endline @@ User.greet jane doe
I think that is type inference at is best. Ocaml is able to figure out, because there is no other thing in scope, which variants you are referring to. I don’t have a deep understanding, but I know that types and values are on separate namespaces, so the fact that one needs to be scoped does not guarantee that the other needs it
I believe this is a case of “type-directed disambiguation”; basically the typechecker uses contextual information (in this case, the signature of User.greet to resolve constructor (and record label) names.
In the second case, let john = FirstName "..." in ... there isn’t enough “contextual information” to disambiguate FirstName.
Indeed, this is type-directed disambiguation at work. If enabled the warnings name-out-of-scope and disambiguated-name provides more information:
User.greet (FirstName "John")
Warning 40 [name-out-of-scope]: FirstName was selected from type User.first_name.
It is not visible in the current scope, and will not
be selected if the type becomes unknown.
Warning 42 [disambiguated-name]: this use of FirstName relies on type-directed disambiguation,
it will not compile with OCaml 4.00 or earlier.
I would say that it is a good feature to be used without restriction
The only downside I can think of is that it may sometimes complicate moving code around because you can take a piece of code that typechecks in a given context, but once you move it to some other place it no longer does due to insufficient contextual information. But I don’t think this is a big issue in practice.
The type of root is not yet known when the typechecker encounters root.tree in your code.
The typechecker thus cannot use type information to find the tree field.
This is the reason why adding an annotation:
fun (root:O.t) -> ... root.tree ...
removes the need to open the O module.
But you are right that type-directed disambiguation is sensitive to the precise flow of type information inside the OCaml typechecker (aka it is not principal) which is one of the reason why the current implementation of type-directed disambiguation is not considered to be satisfying at the theoretical level.