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

Compilation units as recursive modules #5480

Closed
vicuna opened this issue Jan 17, 2012 · 10 comments
Closed

Compilation units as recursive modules #5480

vicuna opened this issue Jan 17, 2012 · 10 comments

Comments

@vicuna
Copy link

vicuna commented Jan 17, 2012

Original bug ID: 5480
Reporter: @alainfrisch
Status: closed (set by @alainfrisch on 2016-12-12T16:53:51Z)
Resolution: won't fix
Priority: normal
Severity: feature
Category: ~DO NOT USE (was: OCaml general)
Monitored by: @gasche @protz mehdi @diremy

Bug description

A compilation unit X with an implementation x.ml and an interface x.mli is morally interpreted as the following declaration:

module X : sig
[content of x.mli]
end = struct
[content of x.ml]
end

What about interpreting it instead as a recursive module declaration:

module rec X : sig
[content of x.mli]
end = struct
[content of x.ml]
end

This would make it possible to refer, in x.ml, to components defined further down in the same file and even to structural types (and class types, module types) defined in x.mli to avoid code duplication.

@vicuna
Copy link
Author

vicuna commented Jan 17, 2012

Comment author: @hcarty

This is an interesting proposal. What effect would this have on redefined values? For example:

x.mli:
val f : int -> int
val g : int -> int

x.ml:
let g x = x + 1
let f x = g x
let g x = x + 2

Which g is f using in your proposed case? Should one expect it to be the g exposed in the .mli or the g defined prior to f? Would "include X" be required at the top of x.ml to see types only defined in x.mli, or types/values defined later in the module?

@vicuna
Copy link
Author

vicuna commented Jan 17, 2012

Comment author: @alainfrisch

Scoping rules would be the same as for the "module rec" expansion. To refer to a definition further down in the module, you need to prefix with the module name explicitly.

In your example, "f" uses the first "g". To use the second one, you would write
"X.g".

@vicuna
Copy link
Author

vicuna commented Jan 17, 2012

Comment author: @hcarty

Under 3.12.1:

module rec X : sig
type t = A | B | C
val f : t -> string
val g : t -> string
end = struct
type t = A | B | C
let f = X.g
let g = function A -> "a" | B -> "b" | C -> "c"
end

X.(f A) raises an Undefined_recursive_module exception. Would this still be the case under your proposal if X is split into x.ml and x.mli?

If t is not included fully in both the sig and struct sections then g fails with a compilation error. Would this duplication requirement change under your proposal?

@vicuna
Copy link
Author

vicuna commented Jan 17, 2012

Comment author: @gasche

Note that you can remove the exception by eta-expanding f:

let f x = X.g x

The idea is that, under a backpatching semantics, "let f = X.g" would force X at definition time, which results in an undefined function f, while "let f x = X.g" only accesses X.g at f application time, which is after the X module has been fully built. See the manual for more details:
http://caml.inria.fr/pub/docs/manual-ocaml/manual021.html#toc75

@vicuna
Copy link
Author

vicuna commented Jan 18, 2012

Comment author: @alainfrisch

It could be useful to improve the compilation strategy for recursive modules so as to cover more cases, but this is rather independent from the proposal.

Concerning the duplication of type declarations: currently, recursive modules makes it possible to avoid duplication of structural type elements. For instance, your example with polymorphic variants. Or the following:

module rec X : sig
module type S = sig type t = A | B | C end
include S
end = struct
module type S = X.S
module rec Y : S = Y
include Y
end

One could also consider adding a "type-include" mechanism, that would allow to write:

module rec X : sig
type t = A | B | C
end = struct
include type X.t
end

@vicuna
Copy link
Author

vicuna commented Jan 22, 2012

Comment author: @lefessan

I submitted a patch a while ago to improve the compilation strategy for recursive modules (#5286).

Without this patch, I think a lot of programs would not compile if all units were recursive by default. Have you tried to compile some non-trivial applications with this patch ?

@vicuna
Copy link
Author

vicuna commented Jan 23, 2012

Comment author: @alainfrisch

Fabrice, unfortunately, I haven't tried your patch.

At LexiFi, we have used a simpler form of the proposal for some time; in our version, compilation units are type-checked as recursive modules (which allows nice type-level forward references and references from the implementation to the interface) but compiled as usual (compile-time error if we use module recursion to refer to a dynamic component).

@vicuna
Copy link
Author

vicuna commented Jan 23, 2012

Comment author: @xavierleroy

I know for a fact that recursive modules in OCaml are a bit of a hack, in that the typechecking is incomplete and the compilation can fail at run-time in rather mysterious ways. That's tolerable as long as recursive modules are clearly marked as an experimental language extension. But I'm extremely wary of putting them at the heart of OCaml, so to speak, namely in the processing of compilation units. That's just calling for major trouble.

@vicuna
Copy link
Author

vicuna commented Dec 12, 2016

Comment author: @mshinwell

@Frisch Are you happy to close this given Xavier's response, or not?

@vicuna
Copy link
Author

vicuna commented Dec 12, 2016

Comment author: @alainfrisch

Ok, closing.

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

1 participant