[mercury-users] Modules, Submodules and Instances.

Fergus Henderson fjh at cs.mu.OZ.AU
Sat Jan 30 14:50:28 AEDT 1999

On 29-Jan-1999, Ralph Becket <rwab1 at cam.sri.com> wrote:
> Say I want to export a type t as an instance of some typeclass c.
> Currently I have to do something like
> 	:- module foo.
> 	:- interface.
> 	:-	typeclass c(T) where [pred repn(T::in, string::out) is det].
> 	:-	type t.
> 	:-	instance c(t) where [pred(repn/2) is r].
> 	:- implementation.
> 	:-	use_module string.
> 	:-	type t == int.
> 	:-	pred r(t::in, string::out) is det.
> 		r(I,S) :- string__int_to_string(I, S).
> But here I'm releasing information about the implementation of t to
> the outside world.  Now, this is in the same class of minor crime as
> the mode problem, but I wonder whether it would really be that much
> harder to get the compiler to handle putting
> 	:- instance c(t).
> in the interface and requiring
> 	:- instance c(t) where [...].
> in the implementation.  This way, nothing about t is made visible to
> the outside world other than that it implements c.

That would certainly be a good idea.
In fact it's one we've considered ourselves -- I've mentioned it
on the Mercury developers mailing list a couple of times.
The only reason that this is not yet supported is that we
haven't yet implemented it.  (Volunteers for that task would
be welcome, of course! ;-)

> The language reference is somewhat cryptic on the matter of submodules
> and I've got a few questions I'd be grateful to get answers to.
> (3.1) Declaring Submodules
> So,
> 	:- module foo.
> 	...
> 	:- include_module bar, baz.
> defines bar and baz to be separate submodules of foo.  And something
> is a separate submodule of foo iff it is the subject of an
> include_module directive in foo?


> What exactly is the naming convention here?  Under 0.8.1 I take it
> that bar and baz must be implemented in files foo.bar.m and foo.baz.m
> respectively.

That's correct.

> Do you need to module qualify separate submodule names?
> That is, should bar.m begin
> 	:- module foo__bar.
> or is
> 	:- module bar.
> sufficient?

>From the "Separate sub-modules" sub-section of the "Sub-modules" section
of the "Modules" chapter of the Mercury language reference manual:

|	(Note: the module names in the `:- module'
|	and `:- end_module' declaration need not be fully-qualified.)

The file must be named "foo.bar.m", not "bar.m", but so long as
you do that, `:- module bar' is sufficient.
(Some people may consider `:- module foo__bar' to be better style, though.)

>  Also, if bar is implemented in foo.bar.m etc., should the
> include_module declaration in foo.m be written as
> 	:- include_module foo__bar, foo__baz.

I don't think this is explicitly stated in the reference manual,
but module names in `include_module' declarations do not have to
be fully qualified.  In general, names do not ever have to be
fully qualified except where this is explicitly specified.

 	:- include_module bar, baz.

is sufficient.

I will modify the reference manual to explicitly state that
module names in `:- include_module' declarations do not need to be
fully qualified.

> What about use_/import_module declarations for bar and baz?

The language reference manual is quite clear about this: all
module names in use_/import_module declarations must be fully
qualified.  This is stated twice, in the "The module system"
sub-section and also in the "Visibility" sub-section of
the "Modules" chapter.

> (3.2) Does it matter where the include_module declaration goes?
> Is there any difference between placing it in the interface or the
> implementation section?

Yes, there is a difference.  If you put the "include_module" declaration
in the implementation section, then that sub-module will be private;
it can only be used by its parent module and by other sub-modules
of the parent module.

>From the "The module system" section of the "Modules" chapter of the
Mercury language reference manual:

| An `:- implementation.' declaration indicates the start of the
| module's implementation section.  Any entities declared in this section
| are local to the module (and its sub-modules) and cannot be used by
| other modules.

"Entities" here includes sub-modules.

> (3.3) Submodules and Parent Modules.
> Presumably a submodule cannot directly implement something declared in
> the interface of a parent.  That is, one cannot have
> :- module foo.
> :- interface.
> :-	type t.
> :- implementation.
> :-	module bar.		% Nested submodule.
> :-	interface.
> :-	type foo__t == int.
> and indeed the compiler rejects this.

Right.  The correct way to do this would be to use an
equivalence type (or in the case of predicates/functions,
rather than types, you could use a forwarding predicate/function). 
For example:

	:- module foo.
	:- interface.
	:- type t. /* foo__t */
	:- implementation.
	:-	module bar.		% Nested submodule.
	:-	interface.
	:-	type t == int.	/* foo__bar__t */
	:-      end_module bar.
 	:- use_module foo__bar.
	:- type t == bar__t.	/* foo__t == foo__bar__t */

Oh, I see below that you did eventually figure this out.

> But there are all manner of weird things afoot.

There are still a few limitations in the current implementation of
sub-modules modules -- see the "Implementation bugs and limitations"
sub-section in the "sub-modules" section of the "Modules" chapter of the
Mercury language reference manual.  These limitations are the main
reason that we haven't yet made more use of sub-modules in the rest of
the Mercury system.

> Indeed, trying to do
> 	$mmake foo.depend; mmake foo
> on the file foo.m containing
> 	:- module foo.
> 	:- interface.
> 	:-      type t. 
> 	:- implementation.
> 	:-      module bar.             % Nested submodule.
> 	:-      interface.
> 	:-              type t == int. 
> in the presence of a Mercury directory I get
> 	mercury_compile: can't open file `Mercury/int0s/foo.int0'.
> 	For more information, try recompiling with `-E'.
> which is a tad opaque.

See the third item in the "Implementation bugs and limitations"

 |    * When using nested modules, the Mercury build tool Mmake sometimes
 |      tries to build things in the wrong order and hence reports
 |      spurious errors about `.int*' files not being found.  In these
 |      cases, simply typing `mmake' again will usually solve the problem.

> Also, when trying to close the bar nested submodule section I've tried
> all the following
> 	:- 	end_module bar.
> 	:- 	end_module foo__bar.
> 	:- 	end_module foo.
> (the latter trying to close the whole thing) and I get complaints from
> the compiler about the end_module declaration not matching the opening
> module declaration.

Either of the first two is supposed to work.
Unfortunately there was a bug in 0.8 which meant that if the
end-module declaration for a sub-module is the last thing
in the containing module, the compiler would report a spurious error.
This bug was fixed in our development sources on Dec 17.
I've attached the patch.  The work-around, if you need to use 0.8,
is to use separate sub-modules rather than nested sub-modules,
or to ensure that the end_module declaration for sub-modules
is not the last thing in the parent module, e.g. by using an
explicit end_module declaration for the parent too.

> [The point I'm making, so far, is that the manual really needs some
> work if these facilities are going to be used -- and they are *good*
> facilities.  It's taken me quite some time to find heuristics that
> work -- sort of.]

The reference manual is OK as a reference manual, I think.
Most of your questions above were either answered by the
reference manual or where due to confusion resulting from
implementation bugs or limitations.

The reference manual certainly doesn't do a great job as an introductory
tutorial, but I think that should really be the job of a separate document.
Adding some examples to the reference manual is probably a good idea, though.
On the other hand, we also need

	- a separate tutorial document,

	- more work on the implementation, to fix the documented
	  bugs and limitations,

	- and more examples in the samples directory and elsewhere,

and if we had all of those then there might be much less need for more
examples in the reference manual.

> (3.4) A Submodule Cannot Directly Implement Something
> Exported From the Parent Module.
> That is, I can't do
> 	:- module foo.
> 	:- interface.
> 	:-      type t.
> 	:- implementation.
> 	:-      module bar.
> 	:-      interface.
> 	:-              type foo__t == int.
> 	:-      end_module bar.

Adding something along those lines to the language reference manual
is probably a good idea; I'll put that on my list of stuff to do.

> (3.5) Visibility
> Everything in the parent module (interface and implementation) is
> visible to its submodules.  Therefore, if the parent exports type t
> and (some of) its children also export type t, then the parent can
> only reference them via use_module (import_module would lead to a name
> clash).

Mercury supports overloading.  Name clashes are reported only at the point
of use, and only if there is an ambiguity.  Ambiguity resolution considers
not only the name and any explicit module qualifiers, and depending on
the kind of symbol used, it may also consider the arity and the type.

> Nope, just tried
> 	:- module foo.
> 	:- interface.
> 	:-      type t.
> 	:- implementation.
> 	:-      module bar.
> 	:-      interface.
> 	:-              type t == int.
> 	:-      end_module bar.
> 	:-      import_module foo__bar.
> 	:-      type t == bar__t.
> 	:- end_module foo.

Yep, that's perfectly legal, because the use of `foo__bar__t' is made
unambiguous by the explicit `bar__' module qualifier.

The three unqualified occurrences of `t' are all declarations or
definitions, not uses, so they can't lead to any ambiguity.

> Either way, just to clear things up, an include_module directive does
> not need to be qualified, but a use_/import_module directive does.



Fergus Henderson <fjh at cs.mu.oz.au>  |  "Binaries may die
WWW: <http://www.cs.mu.oz.au/~fjh>  |   but source code lives forever"
PGP: finger fjh at        |     -- leaked Microsoft memo.

More information about the users mailing list