Mantis Bug Tracker

View Issue Details Jump to Notes ] Issue History ] Print ]
IDProjectCategoryView StatusDate SubmittedLast Update
0002629OCamlOCaml generalpublic2004-06-01 01:232013-08-30 23:28
Reporteradministrator 
Assigned To 
PrioritynormalSeverityfeatureReproducibilityalways
StatusacknowledgedResolutionopen 
PlatformOSOS Version
Product Version 
Target VersionFixed in Version 
Summary0002629: Recursive modules - awkward dynamic semantics
DescriptionHi,

there is a very confusing issue concerning the dynamic semantics of
recursive modules. Please consider the following code:

---------------------------------------------------------------------------
module type S = sig
  val f : unit -> unit
  val g : unit -> unit
end

module MakeA (X : S) = struct
  let g = X.f
  let f () = print_endline "f"
end

module MakeB (X : S) = struct
  include X
end

module rec A : S = MakeA (B)
and B : S = MakeB (A)

let () = B.g ()
---------------------------------------------------------------------------

The above code will fail with an exception "Undefined_recursive_module",
but IMHO it should print "f".

Eta-expanding the definition of "g" in "MakeA" solves the problem.

This seems a little bit awkward to me. I think that this should be
handled transparently, i.e. that intermediate bindings are also updated
when the recursive definition is completely evaluated rather than letting
them point to the dummy function that throws an exception.

What do you think, could this be fixed?

Best regards,
Markus

--
Markus Mottl http://www.oefai.at/~markus [^] markus@oefai.at

Tagsrecmod
Attached Files

- Relationships

-  Notes
(0000219)
administrator (administrator)
2004-06-20 17:06

Dear Markus,

> there is a very confusing issue concerning the dynamic semantics of
> recursive modules. Please consider the following code:
>
> ---------------------------------------------------------------------------
> module type S = sig
> val f : unit -> unit
> val g : unit -> unit
> end
>
> module MakeA (X : S) = struct
> let g = X.f
> let f () = print_endline "f"
> end
>
> module MakeB (X : S) = struct
> include X
> end
>
> module rec A : S = MakeA (B)
> and B : S = MakeB (A)
>
> let () = B.g ()
> ---------------------------------------------------------------------------
>
> The above code will fail with an exception "Undefined_recursive_module",
> but IMHO it should print "f".
> Eta-expanding the definition of "g" in "MakeA" solves the problem.

This is consistent with the operational interpretation of "module rec"
outlined in the reference manual: initialize A and B with default
functions that raise Undefined_recursive_module, compute the functor
applications, then update A and B. The eta-expansion is indeed
necessary to ensure that the reference to B.f in MakeA does not
evaluate to the default function.

> This seems a little bit awkward to me. I think that this should be
> handled transparently, i.e. that intermediate bindings are also updated
> when the recursive definition is completely evaluated rather than letting
> them point to the dummy function that throws an exception.

I see no way to do this while remaining in a call-by-value semantics.
What I plan to do at some point in the future is to implement a
compile-time analysis of "module rec" definitions that will signal an
error on examples such as yours.

Best regards,

- Xavier Leroy

(0000220)
administrator (administrator)
2004-06-20 17:07

Feature wish: compile-time detection of ill-founded "module rec"
(0000221)
administrator (administrator)
2004-06-20 17:52

Dear Xavier,

On Sun, 20 Jun 2004, Xavier Leroy wrote:
> This is consistent with the operational interpretation of "module rec"
> outlined in the reference manual: initialize A and B with default
> functions that raise Undefined_recursive_module, compute the functor
> applications, then update A and B. The eta-expansion is indeed
> necessary to ensure that the reference to B.f in MakeA does not
> evaluate to the default function.

Well, it's certainly in accordance with the specification, but since
the dynamic semantics of recursive modules is still experimental, I
thought it might be interesting to consider alternatives to the current
implementation.

> I see no way to do this while remaining in a call-by-value semantics.
> What I plan to do at some point in the future is to implement a
> compile-time analysis of "module rec" definitions that will signal an
> error on examples such as yours.

It might be possible to use indirections for function values, but this
would probably require too many changes in the runtime and lead to heavy
performance penalties with function calls.

One workaround I had tried was to call a dummy-functor with the recursive
module. This functor just eta-expanded all definitions, and it's result
was included in the recursive module. If I remember correctly (?),
this actually seemed to work to some extent before bug 0002639 (module
thinning) was fixed. Now I have to eta-expand all function definitions
by hand in each recursive module implementation which happens to have
the same interface. This can be quite tedious.

I actually wanted to use this trick ("include"ing recursive modules)
to get some open recursion, i.e. one can plug in additional module
layers that perform intermediate computations (e.g. logging execution of
functions, etc.). Well, it seems choosing objects + inheritance, which I
usually avoid when possible, would have been the better design decision...

Best regards,
Markus

--
Markus Mottl http://www.oefai.at/~markus [^] markus@oefai.at


- Issue History
Date Modified Username Field Change
2005-11-18 10:13 administrator New Issue
2013-08-30 23:28 doligez Tag Attached: recmod


Copyright © 2000 - 2011 MantisBT Group
Powered by Mantis Bugtracker