caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
From: Xavier Leroy <Xavier.Leroy@inria.fr>
To: Chris Hecker <checker@d6.com>
Cc: caml-list@inria.fr
Subject: Re: [Caml-list] recursive modules redux, & interface files
Date: Wed, 21 Mar 2001 19:41:38 +0100	[thread overview]
Message-ID: <20010321194138.A29405@pauillac.inria.fr> (raw)
In-Reply-To: <4.3.2.7.2.20010318142842.00d85300@shell16.ba.best.com>; from checker@d6.com on Sun, Mar 18, 2001 at 03:05:41PM -0800

> 1.  So, I understand that doing recursive types across modules is
> hard (but I'd say very important for decoupling large software), but
> is it true that I can't even call across modules recursively?  Even
> with mli files to resolve the types?  I find that incredibly hard to
> believe, but I can't get it to work and it seems the docs say it's
> impossible as well (so why am I posting about it, you ask?
> Disbelief that it's not possible, I guess. :).

You're right that cross-module recursion even between functions is not
supported, and you're right that this is not because of typing
difficulties -- the type-checking of such recursions is easy.

This is because value components of modules can be the result of
arbitrary computations, and the only way to ensure that these
computations succeed is by making sure they occur sequentially.
Consider for instance:

    A.mli    val x : int                B.mli    val y : int
    A.ml     let x = B.y + 1            B.ml     let y = A.x * 3

We don't know how to evaluate these definitions correctly -- indeed,
there is no integer solution to this recursive definition.

Other languages avoid this problem in one of two ways.  C and C-like
languages only allow global identifiers to be defined as functions or
compile-time constants, but not results of arbitrary computations.
Haskell and other lazy languages rely on lazy evaluation to perform
on-demand initialization (i.e. compute the definition the first time
it's needed), and aborting computation if a dynamic dependency cycle
is detected (as in the example above).

Java goes pretty much the Haskell way, relying on dynamic class
loading to lazily evaluate static field definitions; originally, it
raised an exception on discovering a dynamic dependency cycle;
nowadays, I think it just pre-initializes static fields to 0 or null,
then compute the initial values based on this (so that in the example
above you'd get x = 1 and y = 3 if the program refers to B.y first,
and y = 0 and x = 1 if the program refers to A.x first :-).

The Haskell solution is semantically cleanest, but lazy evaluation
of module components entails a performance penalty and some increase
in code size (the compiler must emit "am I initialized already?" tests
on every module value access).  (The semantics of Java class
initialization entail similar penalties, although they partially hide
them by relying on JIT compilation -- blech.)

A possible approach for Caml would be to have a link-time analysis
that detects cross-module value recursions that involve only function
definitions, and allow these (because they are safe -- like in C!).
Fabrice Le Fessant posted a patch to OCaml a while ago that does
pretty much this.  I'm still hoping more general, principled solutions
exist, but they sure are hard to design!

> 2. Also, on a related note, why do the interface file and the
> implementation file (or equivalently, I believe, the signature and
> structure) both have to have all the concrete types duplicated?  I can
> see the value of having interfaces that hide some implementation stuff
> and abstract types, but if I've got a big fully-specified variant type
> in a .mli file, it's annoying and error prone (yes, I know the
> compiler will catch it) to have to retype the whole thing into the ml
> file (which I have to do if I want to hide anything else in the ml
> file, meaning I can't just have an ml without an mli).

Yes, it's annoying, and it's more or less a consequence of the
ideology behind the ML module system: that structures define things,
and that signatures can later be ascribed to these structures to hide
or abstract some of these things.  With this ideology, every type that
is declared in a signature -- even if it's a concrete declaration that
includes as much information as a type definition! -- must be matched
by a type definition in the corresponding structure. It's just very
principled and logical -- just a bit inconvenient at times :-)

Another way to think about it is that every structure (or
implementation file) must "stand on its own" and make sense without
the signature (or interface file) that will be ascribed to it later
on.  It makes sense when the signature is not known when the structure
is defined, e.g.

        module M = struct type t = A | B ... end
        (* 5000 lines later: *)
        module M' = (M : sig type t = A | B ... end)

It becomes practically inconvenient when the signature is known at the
time of the structure definition:

        module M : sig type t = A | B ... end =
          struct type t = A | B ... end

Which is the case with interface and implementation files.

In this case, one could envision an automatic completion of the
structure / implementation file so that concrete type specifications
from the signature do not need to be implemented in the structure.
Doing this right is not obvious, though.  First, it's not enough to
say that a concrete type spec does not need to be matched in the
structure.  This would type-check

        module M : sig type t = A | B end = struct end

but not

    module M : sig type t = A | B  val v : t end = struct let v = A end

In other terms, the unmatched concrete type specs in the signature
need to be somehow reintroduced in the structure definition, so that
other parts of the structure may refer to them.  While I think it can
be done in most practical cases, it's a bit of a kludge and I'm not
sure how to do this in all cases.

Is the practical value of this kludge enough to forget that it's a
kludge?  Can't we live with the current duplication of concrete type
definitions in the name of systematic, principled module systems?  
I really don't know.

Best,

- Xavier Leroy
-------------------
To unsubscribe, mail caml-list-request@inria.fr.  Archives: http://caml.inria.fr


  parent reply	other threads:[~2001-03-21 18:41 UTC|newest]

Thread overview: 48+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2001-03-18 23:05 Chris Hecker
2001-03-19  0:01 ` Brian Rogoff
2001-03-19 11:04 ` John Max Skaller
2001-03-19 11:41   ` Chris Hecker
2001-03-20 17:43     ` John Max Skaller
2001-03-21  4:03       ` Chris Hecker
2001-03-21  5:10         ` Patrick M Doane
2001-03-21  9:27           ` Chris Hecker
2001-03-21 18:20           ` John Max Skaller
2001-03-22  0:03             ` Patrick M Doane
2001-03-22  0:22               ` Brian Rogoff
2001-03-22 10:26                 ` [Caml-list] duplication implementation/interface Judicael Courant
2001-03-22 11:16                   ` [Caml-list] about typedefs... (was: duplication implementation/interface) Olivier Andrieu
2001-03-22 17:14                   ` [Caml-list] duplication implementation/interface Brian Rogoff
2001-03-22  9:11               ` [Caml-list] recursive modules redux, & interface files Francois Pottier
2001-03-21 23:24           ` John Prevost
2001-03-22  0:00             ` Patrick M Doane
2001-03-21 18:18         ` John Max Skaller
2001-03-21 18:19         ` John Max Skaller
2001-03-22 11:40   ` Markus Mottl
2001-03-21 18:41 ` Xavier Leroy [this message]
2001-03-22  0:23   ` Patrick M Doane
2001-03-22 12:02   ` Hendrik Tews
2001-03-22 13:01     ` Markus Mottl
2001-03-22 16:56       ` Brian Rogoff
2001-03-22 17:13         ` Daniel de Rauglaudre
2001-03-23 17:30         ` Fergus Henderson
2001-03-23 18:04           ` Brian Rogoff
2001-03-23 20:35             ` [Caml-list] Why People Aren't Using OCAML? (was Haskell) Mattias Waldau
2001-03-26  2:29             ` [Caml-list] recursive modules redux, & interface files Fergus Henderson
2001-03-27 22:11         ` John Max Skaller
2001-03-28  4:30           ` Brian Rogoff
2001-04-05 17:07             ` John Max Skaller
2001-03-27  8:21       ` Hendrik Tews
2001-03-30 10:27   ` [Caml-list] parser combinators Kevin Backhouse
2001-04-08 18:28     ` Daniel de Rauglaudre
2001-03-22 11:55 [Caml-list] recursive modules redux, & interface files Dave Berry
2001-03-22 12:01 ` Markus Mottl
2001-03-27  6:29 ` John Max Skaller
2001-03-22 18:04 Dave Berry
2001-03-23  7:54 ` Tom Hirschowitz
2001-03-23 12:18   ` Fabrice Le Fessant
2001-03-27  8:49   ` Hendrik Tews
2001-03-23 10:33 Dave Berry
2001-03-23 20:33 Don Syme
2001-03-27  9:00 ` Xavier Leroy
2001-03-27 14:38 Don Syme
2001-03-27 17:05 Manuel Fahndrich

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20010321194138.A29405@pauillac.inria.fr \
    --to=xavier.leroy@inria.fr \
    --cc=caml-list@inria.fr \
    --cc=checker@d6.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).