* Q: Module system and separate compilation
@ 1996-03-19 9:29 Jocelyn Serot
1996-03-20 13:49 ` Wolfgang Lux
1996-03-20 14:23 ` Christian Boos
0 siblings, 2 replies; 3+ messages in thread
From: Jocelyn Serot @ 1996-03-19 9:29 UTC (permalink / raw)
To: caml-list
Hello,
I recently switched from Caml light to CSL and have been impressed
both by the resulting speedup of my programs and the usefulness of the
module system. Thanks to X. Leroy and all the co-designers for making
such a tool available !..
I have a question regarding relationships between the module system and
separate file compilation. I will try to explain my pb with a (small ;-))
but hopefully significant example:
Let say i have a module defining a kind of "addition" on a (abstract)
type t. Its signature might be:
module type Foo = sig
type t
val one : t
val add : t -> t -> t
end
I want a module that defines a (restricted) notion of multiplication
on this type t. It signature might be:
module type Bar = sig
type t
val one : t
val double : t -> t
end
My goal is to use any module M that matches the signature Foo to build
an implementation for Bar. This seems a good application for functors:
module MakeBar(M: Foo) =
(struct
type t = M.t
let one = M.one
let double x = M.add x x
end : Bar)
Now, suppose i have an implementation for Foo. For example:
module Foo = struct
type t = int
let one = 1
let add x y = x + y
end
All i have to do build a Bar implemtation is to apply functor:
module Bar = MakeBar(Foo)
open Bar
let two = double one
If the previous (indented) phrases are written in the same file (eg: foobar.ml)
and compiled as a whole, the command
cslc -i foobar.ml
produces the following output:
>module type Foo = sig type t val one : t val add : t -> t -> t end
>module type Bar = sig type t val one : t val double : t -> t end
>module MakeBar : functor(M : Foo) -> Bar
>module Foo : sig type t = int val one : int val add : int -> int -> int end
>module Bar : sig type t = MakeBar(Foo).t val one : t val double : t -> t end
>val two : Bar.t
That's fine (though i dont really understand why the type <t> in the last line
is <MakeBar(Foo).t> and not simply <Bar.t>..)
Now, the pb is that in general both Foo and Bar signatures (as well as
MakeBar functor definition) may be large and i want to put them in
separate files, that is:
(* file "foo.mli": *)
type t
val one : t
val add : t -> t -> t
(* file "foo.ml": *)
type t = int
let one = 1
let add x y = x + y
(* file "bar.mli": *)
type t
val one : t
val double : t -> t
all these files compiles (cslc xxx.ml[i]) and produce corresponding .cmi and
.cmo files.
Now, i suppose i have to put MakeBar def in the "bar.ml" file:
module MakeBar(M: Foo) = (struct
type t = M.t
let one = M.one
let double x = M.add x x
end : Bar)
but this doesnt compile: CSL complains that
"File "bar.ml", line 1, characters 18-21: Unbound module type Foo"
In the CSL HTML manual (node4.html), it is said, however that
"When the compiler encounters a reference to a free module identifier Mod,
it looks in the search path for a file mod.cmi (note lowercasing of first
letter) and loads the compiled interface contained in that file."
In this case, the file "foo.cmi" exists !...
The same complain occurs with the Bar module type. Again the file bar.cmi
exists.
Ok, lets duplicate these sigs in the file bar.ml:
(* file "bar.ml": *)
module type Foo = sig type t val one : t val add : t -> t -> t end
module type Bar = sig type t val one : t val double : t -> t end
module MakeBar(M: Foo) = (struct
type t = M.t
let one = M.one
let double x = M.add x x
end : Bar)
But CSL now complains that:
"The implementation bar.ml does not match the interface bar.cmi:
The field `double' is required but not provided
The field `one' is required but not provided
The field `t' is required but not provided"
The only solution i found is then to rewrite to write bar.mli as:
(* file bar.mli *)
module type Bar = sig
type t
val one : t
val double : t -> t
end
But this seems contradictory with the fact that, as stated in node2.12 of
CSL html manual
"A compilation unit behaves roughly as the module definition
module unit-name : sig unit-interface end = struct unit-implementation end"
Am i missing sth important, or even misusing the module system ?..
In particular, the need for textual duplication of the Foo and Bar signatures
(which have written and compiled in separate files) in the file defining
the functor seems a bit annoying..
Sorry for being so long, and thanks in advance for your help
J. Serot
--
E-mail: Jocelyn.Serot@lasmea.univ-bpclermont.fr .............................
S-mail: LASMEA - URA 1793 CNRS, Universite Blaise Pascal, 63177 Aubiere cedex
Tel: (33) 73.40.73.30 - Fax: (33) 73.40.72.62 ...............................
.... http://wwwlasmea.univ-bpclermont.fr/Personnel/Jocelyn.Serot/Welcome.html
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: Q: Module system and separate compilation
1996-03-19 9:29 Q: Module system and separate compilation Jocelyn Serot
@ 1996-03-20 13:49 ` Wolfgang Lux
1996-03-20 14:23 ` Christian Boos
1 sibling, 0 replies; 3+ messages in thread
From: Wolfgang Lux @ 1996-03-20 13:49 UTC (permalink / raw)
To: Jocelyn Serot; +Cc: caml-list
> [Definition of module signature omitted]
>
> My goal is to use any module M that matches the signature Foo to build
> an implementation for Bar. This seems a good application for functors:
>
> module MakeBar(M: Foo) =
> (struct
> type t = M.t
> let one = M.one
> let double x = M.add x x
> end : Bar)
>
> Now, suppose i have an implementation for Foo. For example:
>
> [Definition of Foo omitted]
>
> All i have to do build a Bar implemtation is to apply functor:
>
> module Bar = MakeBar(Foo)
>
> open Bar
> let two = double one
>
> If the previous (indented) phrases are written in the same file (eg: foobar.ml)
> and compiled as a whole, the command
> cslc -i foobar.ml
> produces the following output:
>
> >module type Foo = sig type t val one : t val add : t -> t -> t end
> >module type Bar = sig type t val one : t val double : t -> t end
> >module MakeBar : functor(M : Foo) -> Bar
> >module Foo : sig type t = int val one : int val add : int -> int -> int end
> >module Bar : sig type t = MakeBar(Foo).t val one : t val double : t -> t end
> >val two : Bar.t
>
> That's fine (though i dont really understand why the type <t> in the last line
> is <MakeBar(Foo).t> and not simply <Bar.t>..)
This is because MakeBar(Foo).t provides more information about the
type t than simply Bar.t. If it simply were Bar.t, two following two
functor applications would yield incompatible types:
Bar = MakeBar(Foo).t
Bar' = MakeBar(Foo).t
i.e, the types Bar.t and Bar'.t would be incompatible. But the type
MakeBar(Foo).t gives you enough information to see that the type
result from the same functor application and CSL will regard them as
compatible types. If you are interested in details, I would recommend
reading X.Leroy papers "Manifest types, modules, and separate
compilation" and "Applicative Functors and Higher Order Modules",
which describe the ideas behind CSL's modules (but in the context of
SML structures). They are available as:
http://pauillac.inria.fr/~xleroy/publi/manifest-types-popl.dvi.gz
http://pauillac.inria.fr/~xleroy/publi/applicative-functors.dvi.gz
>
> Now, the pb is that in general both Foo and Bar signatures (as well as
> MakeBar functor definition) may be large and i want to put them in
> separate files, that is:
>
> [Obvious contents omitted :-)]
>
> all these files compiles (cslc xxx.ml[i]) and produce corresponding .cmi and
> .cmo files.
>
> Now, i suppose i have to put MakeBar def in the "bar.ml" file:
>
> module MakeBar(M: Foo) = (struct
> type t = M.t
> let one = M.one
> let double x = M.add x x
> end : Bar)
>
> [ Remaining redefinition deleted]
>
What are defining here is not the module MakeBar, but a module
Bar.MakeBar
i.e. a nested module!
> "A compilation unit behaves roughly as the module definition
> module unit-name : sig unit-interface end = struct unit-implementation end"
>
> Am i missing sth important, or even misusing the module system ?..
So what you are actually going to write into bar.ml is the following:
type t = Foo.t
let one = Foo.one
let double x = M.add x x
This will yield a structure that matches the signature defined in
"bar.mli".
Now you say: "I have defined a functor and you give me a structure".
But wait the functor is still there; but it is now hidden in the
filesystem.
If you read on in the section about compilation units, it says that
the module expression will be evaluated in an initial environment
where all the interfaces available in the search path are defined. You
could read this as well as follows: The implementation file is the
body of a functor, whose parameters are all the modules available in
the search path, i.e. given the interfaces Bar, Foobar, etc. are in
the search path, the compilation unit Foo is interpreted as:
module Foo(Bar : Bar)(Foobar : Foobar)... :
sig <contents of foo.mli> end =
struct <contents of foo.ml> end
Seen that way functor application will take place when the program is
linked together from the separatly compiled modules.
Regards
Wolfgang
----
Wolfgang Lux
WZH Heidelberg, IBM Germany Internet: lux@heidelbg.ibm.com
+49-6221-59-4546 VNET: LUX at HEIDELBG
+49-6221-59-3500 (fax) EARN: LUX at DHDIBMIP
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: Module system and separate compilation
1996-03-19 9:29 Q: Module system and separate compilation Jocelyn Serot
1996-03-20 13:49 ` Wolfgang Lux
@ 1996-03-20 14:23 ` Christian Boos
1 sibling, 0 replies; 3+ messages in thread
From: Christian Boos @ 1996-03-20 14:23 UTC (permalink / raw)
To: Jocelyn Serot; +Cc: caml-list
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 3162 bytes --]
[French translation below]
Hello,
Here's some comments about your questions :
Jocelyn Serot writes:
>
> (...)
> (* file "bar.ml": *)
>
> module type Foo = sig type t val one : t val add : t -> t -> t end
> module type Bar = sig type t val one : t val double : t -> t end
>
> module MakeBar(M: Foo) = (struct
> type t = M.t
> let one = M.one
> let double x = M.add x x
> end : Bar)
>
> (...)
>
> (* file bar.mli *)
>
> module type Bar = sig
> type t
> val one : t
> val double : t -> t
> end
[1] This is the right way. To do the best, you should add :
'module type Foo = sig ... end '
and 'module MakeBar (M : Foo) : Bar'
in the file "bar.mli".
>
> But this seems contradictory with the fact that, as stated in node2.12 of
> CSL html manual
>
> "A compilation unit behaves roughly as the module definition
> module unit-name : sig unit-interface end = struct unit-implementation end"
>
[2] There's no contradiction. In the file "foo.mli", a module Foo is
implicitly declared. This module has type 'sig type t val one ... end'
which is a sort of "anonymous" module type. In no way have you
declared a module type named Foo, as expected in the "bar.ml" file.
> Am i missing sth important, or even misusing the module system ?..
> In particular, the need for textual duplication of the Foo and Bar signatures
> (which have written and compiled in separate files) in the file defining
> the functor seems a bit annoying..
[3] I agree with you that this duplication is annoying. It's only
justification is that a 'module type XXX = sig ... end' statement in a
.mli is a module type declaration, and that the same statement in a
.ml is a module type definition. This allows one to define abstract
module types, for example. I suggest that the compiler be modified to
automatically generate module type definition from module type
declaration when this declaration is not an abstract one.
This would eliminate the need for duplication in most cases.
-- Christian Boos.
------------------------------------------------------------------
Bonjour,
Je peux faire quelques commentaires sur vos questions.
[1] C'est effectivement comme cela qu'il faut faire. Pour bien faire,
il faudrait encore rajouter la déclaration du type de module 'module
type Foo = sig ... end ' et celle foncteur 'MakeBar' dans le fichier
bar.mli:
module MakeBar (M : Foo) : Bar
[2] Non, je ne vois pas la contradiction : le fichier "foo.mli" declare
un module Foo, qui se trouve avoir un type (sig type t val one
... end), ce type n'étant pas nommé (il n'y a pas création de 'module
type Foo = sig ... end').
[3] Oui, alors là d'accord. Le fait de devoir recopier les 'module
type' dans les implémentations est pénible et source d'erreur. Cela se
justifie parceque dans le .mli il s'agit de déclaration et dans le .ml
d'une définition. Cette séparation permet par exemple de déclarer des
types de modules abstraits ('module type Foo'). Mais la plus part du
temps, il serait plus pratique de générer la définition à partir de la
déclaration du .mli.
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~1996-03-20 16:39 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1996-03-19 9:29 Q: Module system and separate compilation Jocelyn Serot
1996-03-20 13:49 ` Wolfgang Lux
1996-03-20 14:23 ` Christian Boos
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox