Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Local open can raise type error (as expected) or warning or even no problem (as unexpected) depending on how it is used. #7742

Closed
vicuna opened this issue Feb 22, 2018 · 7 comments

Comments

@vicuna
Copy link

vicuna commented Feb 22, 2018

Original bug ID: 7742
Reporter: kantian
Assigned to: @gasche
Status: resolved (set by @gasche on 2018-02-22T10:54:05Z)
Resolution: not a bug
Priority: normal
Severity: minor
Version: 4.05.0
Category: lexing and parsing

Bug description

Code is better than a long discourse:

Steps to reproduce

module M = struct type t = {x:int} let id x = x end
module N = struct type t = {x:int} let id x = x end

(* type error as expected *)
{M.x = 1} = {N.x = 1};;
Error: The field N.x belongs to the record type N.t
but a field was expected belonging to the record type M.t

(*
Only warning if the module name is put outside the record construct.
The field `x' in the second expression is interpreted as M.x and not N.x,
hence the two values are equal.
*)
M.{x = 1} = N.{x = 1};;
Characters 12-21:
Warning 40: this record of type M.t contains fields that are
not visible in the current scope: x.
They will not be selected if the type becomes unknown.

  • : bool = true

(* same as before but with local open: its worse since there is no warning *)
let open M in {x = 1} = let open N in {x = 1};;

  • : bool = true

(* but there is no problem if we add a function application *)
M.(id {x = 1}) = N.(id {x = 1});;
Error: This expression has type t but an expression was expected of type M.t

let open M in id {x = 1} = let open N in id {x = 1};;
Error: This expression has type N.t but an expression was expected of type M.t

(* and no problem if we let bind the constructions before comparaison *)
let i = M.{x = 1} in
let j = N.{x = 1} in
i = j;;
Error: This expression has type N.t but an expression was expected of type M.t

(* it works as expected in pattern matching *)
function M.{x = 1}, N.{x = 1} -> () | _ -> ();;

  • : M.t * N.t -> unit =
@vicuna
Copy link
Author

vicuna commented Feb 22, 2018

Comment author: @Octachron

Would be so kind to precise which issue(s) do you see with your code samples?

Your second example is an illustration of the left-to-right bias of the typechecker: the first argument of (=) M.{…} N.{…} is typed first, then the second x field is disambiguated using the information that the type of the record is M.t (and -principal will warn that this type based record-disambiguation is not principal).

Your third example can be rewritten as M.( (=) {…} N.{…} ) and indeed this time the field M.x is in scope when disambiguating N.{…}, so there is no reason to emit the warning 40 .

@vicuna
Copy link
Author

vicuna commented Feb 22, 2018

Comment author: @gasche

Indeed, I think that in these examples the language is in fact behaving as specified.

@vicuna vicuna closed this as completed Feb 22, 2018
@vicuna
Copy link
Author

vicuna commented Feb 22, 2018

Comment author: @lpw25

Yeah, I think all of these behaviors are correct, and I would suggest using the -principal option to get the more sensible behavior that you're after.

The third one does look confusing, but really its just an issue with the precedence of = vs let.

@vicuna
Copy link
Author

vicuna commented Feb 22, 2018

Comment author: kantian

Ok, your explications are clear. But it's still confused me a little bit...

@vicuna
Copy link
Author

vicuna commented Feb 22, 2018

Comment author: kantian

Just a little note to precise that the answer to my confusion is 42 :-D

I do not really understand when warning 18 is triggered. With this slight variation on example 3:

M.(let f = (=) {x=1} in f N.{x=1});;

Warning 18 (this type-based record disambiguation is not principal) is not triggerd but Warning 42 (this use of x relies on type-directed disambiguation,
it will not compile with OCaml 4.00 or earlier.) is.

@vicuna
Copy link
Author

vicuna commented Feb 22, 2018

Comment author: @lpw25

Basically, something isn't principal if it depends on arbitrary details of the inference algorithm. For instance, the order that different sides of = are considered, as in your original example 3. In your new example it only depends on that the definition of a let is considered before its body -- this is not considered an arbitrary detail of the algorithm but a reliable part of the type system's declarative specification.

@vicuna
Copy link
Author

vicuna commented Feb 22, 2018

Comment author: kantian

Thanks, that makes sense.

By the way, this type directed disambiguation reminds me this problem1 with merging in git.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants