Skip to content

Warning 40 ("Constructor or label name out of scope") should be an error #6000

@vicuna

Description

@vicuna

Original bug ID: 6000
Reporter: @lpw25
Assigned to: @lpw25
Status: closed (set by @xavierleroy on 2015-12-11T18:26:28Z)
Resolution: fixed
Priority: normal
Severity: minor
Version: 4.01.0+dev
Target version: 4.02.0+dev
Category: typing
Related to: #5759
Monitored by: @gasche @lpw25 @hcarty yminsky

Bug description

We have had record disambiguation on trunk now for a few months,
and I think that it would be wise to look again at the issue of
out of scope labels and constructors before the next major
release.

The issue is around the new ability to use a constructor or label
even though that constructor or label is not in scope, as long as
the type information is available.

For example, the following is allowed:

module M = struct
type foo = Foo
end

let x: M.foo = Foo

Note that previously this would be rejected, forcing the user to
write:

let x = M.Foo

Currently, using this feature produces a warning (40 "Constructor
or label name used out of scope"), however I think that it should
be upgraded to a full error.

This was discussed in #5759, however more issues have emerged
since that discussion, and I think now is a good time to
re-evaluate this descision.

My arguments against allowing out of scope labels and
constructors are as follows:

  • It allows references to constructors to be used in files that
    contain no reference to the file defining those
    constructors. For example, I have already come across the
    following code in the wild:

    (* types.ml *)
    ...
    module Project = struct
    ...
    type project = {
    project_id: string;
    ...
    }
    ...
    end
    ...

    (* data.ml *)
    ...
    open Types
    ...
    module Projects = struct
    open Project
    module Platform = struct

        let project =
          { project_id="platform"; 
            ...
          }
        ...
      end
      ...
      let all = [ Platform.project; ... ]
      ...
    end
    

    (* gantt.ml *)
    ...
    open Data
    ...
    List.map Projects.all
    ~f:(fun proj -> ... proj.project_id ...)
    ...

    Note that gantt.ml uses project_id but contains no reference to
    types.ml where that label is defined. When a bug occurred, it
    was non-trivial to fix due to the difficulty of finding the
    appropriate definition of project_id. This bug caused a lot of
    confusion among less experienced OCaml programmers, and I think
    that it is a very bad idea to allow it.

  • It only works "in the presence of type information". This is an
    unspecifed criteria that most users will probably find
    confusing.

  • It doesn't work with GADTs. If the constructor in question is
    declared using GADT syntax then it cannot be used out of scope.

  • It can't work with exceptions.

  • It won't work with "open types" (Open Extensible Types #5584) or "pattern synonyms"
    which may be added to the language in future.

  • It will confuse users by adding a small element of structural
    typing to an otherwise nominative system. I think that most
    users picture variant constructors as named values, and all
    named values in OCaml obey the same scoping rules. To suddenly
    allow these values to be used as if they were not named values,
    but structural values (like the methods in an object) will
    confuse people.

In return for these issues, all that we gain is the ability to
refer to out of scope constructors and labels without using a
module name, but only under certain difficult to define
conditions.

Last time that this issue was discussed, it only appeared to be
discussed by 4 people. I think perhaps it would be a good idea
to get wider feedback before including a potentially risky
feature like this in a release.

It is also worth pointing out that it would be easier to add this
feature in the future than to remove it later if it causes
problems.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions