Child modules draft feedback wanted

Romain Lenglet rlenglet@REDACTED
Mon Apr 3 08:35:07 CEST 2006


Richard A. O'Keefe wrote:
[...]
> I also want to repeat that for Prolog and Erlang I have
> several times (and in the case of Prolog, several times in the
> last week) heard a "user" demand for some kind of hierarchical
> structure within a module. "Child modules" were inspired by
> Ada, although they don't have much in common with Ada 95 child
> modules.

And why is there a demand? What problem do they solve that cannot 
be solved otherwise and more or less simply?

[...]
> But a parent/child relationship is one of the things I am
> trying to ADD, not take away.  I repeat, there are people who
> *WANT* hierarchical structure within modules, and, if not
> taken to excess, I do not regard that as unreasonable.  (The
> ersatz Java dotted module names, which I DO regard as
> unreasonable, are an attempt to provide some kind of
> hierarchical structure above the level of modules; I think
> replaceable children can satisfy that need in a much better
> way.)

Java packages are only namespaces. No more. There are not even 
hierarchical relationships between packages.

> 	> But this now GREATLY complicates the implementation.
>
> 	It can very easily be implemented by program transformation
> at compile time. No need to change the runtime.
>
> I completely fail to see how this can possibly be done at
> compile time.
>
> Consider the following:
>
>     -import(foo, [...]).
>
>     f(M) -> M:g(42).
>
> If M happens to be 'foo' at run time, then the call to M:g/1
> should be mapped to M':g/1, where M' is whatever foo is mapped
> to.  But that mapping is private to this module, not global. 
> And if M happens to be 'goo' at run time, then the call to
> M:g/1 should *not* be mapped to M':g/1.

I was considering a solution based on external calls only, not on 
implicit applies. foo:g(42) is an external call, while M:g(42) 
is an implicit apply. The distinction is very clear in Erlang. 
For instance, cf. section 4.2 of the Efficiency Guide.
And both are compiled in a very different way in beam code.
So both kinds of statements can be manipulated differently by the 
compiler, and if only the module name in some external calls 
need to be replaced by a compiler, this is possible and easy.

What I proposed is external calls with "virtual" module names 
that can be replaced at compile time (or load time, or whenever 
after coding).

Just take a look at lib/compiler/src/genop.tab in the OTP sources 
for the list of beam opcodes:
- local calls are compiled into call/2 ops;
- external calls are compiled into call_ext/2 ops;
- implicit apply calls are compiled into normal calls to apply.

Try to decompile that module, and you will see:
-module(test).
-export([start/0,start/1]).
start() ->
    test:start().
start(Test) ->
    Test:start().

$ erlc test.erl
$ erl
1> beam_disasm:file("test.beam").
In the output, you should remark that part:
...
{code,[{function,start,
                             0,
                             undefined,
                             [{label,1},
                              {func_info,{atom,test},
{atom,start},0},
                              {label,2},
                              {call_ext_only,0,
{extfunc,test,start,0}}]},
                   {function,start,
                             1,
                             undefined,
                             [{label,3},
                              {func_info,{atom,test},
{atom,start},1},
                              {label,4},
                              {allocate,0,1},
                              {move,{atom,start},{x,1}},
                              {apply_last,0,0}]},
...
test:start() is compiled into a "call_ext_only", while 
Test:start() is compiled as a call to apply.

So yes, I claim it once again: replacing the module name in 
external calls is easy at compile time. We need only to 
transform the parameters of the call_ext ops.



Out of context, just for the fun, I would like to paraphrase your 
own document:

With the -require construct I propose a solution in which 
- There are modules, just like we had before, and only modules.
- File names never appear in source files, only module names.
- A separate configuration language says how to map logical 
module names in the scope of client modules, and actual module 
names. 
- File names never appear in configuration files, only module 
names.
- By having more than one configuration file, you can have 
several different configurations for a set of modules. 
- The interface between a module and another module is already 
explicit (it is the list of functions exported by every module).

;-)


[...]
> 	As I understand it, the main use of -ifdef, etc. is to select
> 	alternative architectures. Using an ADL, you would simply
> have to write several specs.
>
> Yes, but that could mean repeating hundreds of lines.
> I'm thinking in terms of something like C/MESA.

Hundreds of lines???

[...]
> 	> I am trying
> 	> to design a principled replacement for -include AND I am
> 	> trying to meet a frequently expressed "need" for some kind
> 	> of hierarchical name scope WITHIN a single module.
>
> 	And this contradicts my basic principle (which I state in the
> 	beginning of this email, sorry I should have stated this
> 	before): configuration alternatives ("what can be required")
> 	should be specified as functions in modules.
>
> This contradiction does not exist.  The 'frequently expressed
> "need" for some kind of hierarchical name scope WITHIN a
> single module' has *NOTHING WHATEVER TO DO WITH* configuration
> alternatives.  I believe that one mechanism can help with both
> needs, but they are none-the-less separate and distinct needs.

Please explain what problems do such hierchical namespaces solve?
I thought from your document that you wanted to get rid of the 
preprocessr. Probably I was wrong, and you have other, 
additional motives. But I don't see them.

[...]
> 	I think that it is a bad idea to try to specify a single
> 	construct that would replace all actual usages of -include
> and -ifdef: either the new construct will be as bad as what it
> replaces, or it will not cover all usages.
>
> This seems to me to be a matter of personal taste; I see no
> reason why the claim should be true.

OK, it is mainly a matter of personal taste.

[...]
> 	I agree. But let's solve one problem at a time?!
>
> I am sorry, but -record is the MAJOR problem.  If you want to
> solve one problem at a time, you have to solve -record FIRST. 
> I have already tackled that in two different ways:  "abstract
> patterns" and "frames" alias Joe Armstrong's "proper structs".
>  Neither of those has (yet) been adopted or is likely to be in
> the near future. To move forward, we need to move forward with
> what we have, and that means -record.
>
> 	Why not have a specific construct or convention to get record
> 	information at a module level (not at a source code level,
> like -record).
>
> The Erlang/OTP support people have already made it plain that
> they will not tolerate any replacement for -record that
> *requires* cross-module inlining.

Can you please point me to such a discussion on the Erlang 
mailing-list or elsewhere? 

And who has talked about cross-module inlining in this 
discussion?!

Let's consider the semantics of the proposed new -import_records 
construct (thanks for that name, Ulf, although I thought it 
would be nice to be able to be able to import a list of several 
record defs at a time, just like import, hence the final "s"):

-module(mod1).
-record(record1, {field1, field2}.
...

-module(mod2).
-import_records(mod1, [record1]).
...

Would be strictly equivalent (and can be easily transformed 
into):

-module(mod1).
-record(record1, {field1, field2}).
% Generated automatically by the compiler:
-export([record_info/2]).
record_info(fields, record1) -> [field1, field2];
record_info(size, record1) -> 2.
...

-module(mod2).
% This -record is generated from the result of calls to
% mod1:record_info/2 by the compiler:
-record(record1, {field1, field2}.
% Since importing a record def is just like declaring
% it locally, it would be exported from that module also:
-export([record_info/2]).
record_info(fields, record1) -> [field1, field2];
record_info(size, record1) -> 2.
...

Please point me where you see inlining in that.

> Trying to develop a solution that you know has no chance of
> being adopted is simply an idle pastime like painting flowers
> on toilet paper just before you use it.

You should have become a poet!

> Something that provides a kind of encapsulation, requires
> minimal changes to the run-time system, and *still* allows the
> use of -record with no cross-(full)-module inlining has *some*
> non-zero chance of being useful.

-- 
Romain LENGLET



More information about the erlang-questions mailing list