David 天宇 Wong
David 天宇 Wong

Reputation: 4187

when are modules redeclared vs aliased?

I have a very hard time understanding in OCaml when modules and types are aliased versus redeclared.

For example, to use a module A in a file x.ml it seems like I have several path:

Am I missing something?

Upvotes: 2

Views: 78

Answers (1)

ivg
ivg

Reputation: 35210

In open A nothing is defined or declared, this module expression just allows you to use elements from the opened module without referencing the module name. This is a pure namespacing operation that has no reflection in runtime. In other words, it only affects the typing environment.

In module A = X.A you're defining (since 4.02) a module alias. So nothing is redeclared and A, as well as all its components, could be used in the same place where X.A and its components are expected.

The ppx_open extension gives you better control over what you want to alias in the current module. In the end, it rewrites to

open struct 
  type t = Other.t
  module Foo = Other.Foo
  let foo = Other.foo
end

So it is still an open expression and nothing is redefined.

The last example, module B with A := A, is not a valid module expression.

Am I missing something?

I think you're missing the information about sharing constraints. This is what matters, e.g.,

module type X = sig type t val x end
module Foo : X = struct type t = int let x = 42 end
module Bar = Foo (* [Bar.x; Foo.x] typechecks *)
module Bar : X = Foo (* [Bar.x; Foo.x] no longer typechecks, they have distinct types *)
module Bar = struct include Foo end (* [Bar.x; Foo.x] .. typechecks *)
module Bar : sig include module type of Foo end = struct include Foo end 
(* [Bar.x; Foo.x] doesn't typecheck *)

As you can see from the examples above, if you do not specify the signature, OCaml tries to ascribe the strongest signature possible, with the only exception of the module type of, which explicitly prevents signature strengthening, i.e., produces an unstrengthened signature. It doesn't mean that it will erase aliases, e.g.,

# module X = struct type t = int let x = 42 end;;
module X : sig type t = int val x : int end
# module type X_of_X = module type of X;;
module type X_of_X = sig type t = int val x : int end

Notice that the aliasing information is not erased, but it is not strengthened to type t = int = X.t, but it is not necessary because the types of X.t and Y.t are known to be equal as they are aliases to the same type, e.g.,

# module Y : sig include module type of X end = X;;
module Y : sig type t = int val x : int end
# [X.x; Y.x];;
- : int list = [42; 42]

Upvotes: 3

Related Questions