[erlang-questions] Parameterized module idioms

Paulo SŽergio Almeida psa@REDACTED
Tue Apr 20 13:25:38 CEST 2010


Hi all,

I have not used much parameterized modules, but was very delighted when 
did do, and I think they gives us something, in practice.

Richard O'Keefe wrote:
> I pointed out that modules with parameters give us nothing
> that plain old closures don't, in response to which

Closures must be passed somehow, if they are to be used in a function. 
If I am writing many functions which use some nonrelated parameters, 
some closures are being passed around. That is already too much 
pollution for my tastes.

>> Trivial, but requires a lot of boilerplate code and certainly isn't
>> any easier to understand or debug than parameterized modules. It also
>> becomes impossible to write a useful type spec if you use closures
>> like that.
> 
> (a) Such boilerplate code as is required can be automatically generated.

If some code would need to be generated if I didn't have parameterized 
modules, then parameterized modules already give me something.

Let's look at some examples (from a homemade "database" I wrote some 
time ago).

The more simple use may be for substituting constants with something 
calculated at runtime; e.g.

Instead of

   need_dump(Tab, LogOps) -> LogOps > ?DUMPLIMIT * ets:info(Tab, size).

I can have

   need_dump(Tab, LogOps) -> LogOps > DumpLimit * ets:info(Tab, size).

with no change in the interface of the function.

But it becomes much more interesting when modules themselves are used as 
parameters of other modules.

The first version had a constant directory path defined and used by a 
module pets_lib that served as a sort of general library. Then other 
modules used pets_lib.

For example, a module pets_tm would have somewhere:

   Res = pets_lib:delete_table(Tab),

How do I make the path (as well as many other configuration parameters) 
a value chosen at runtime, with little effort in rewriting the code 
which didn´t contemplate such possibility beforehand? Making pets_lib a 
parameterized module. What is the impact of that on client code? A 
simple change to:

   Res = Lib:delete_table(Tab),

It looks pretty much the same, but now we have this Lib variable. If we 
were using closures, the closure would have to be passed somehow (who 
knows how many levels of invocations) until is was available to the 
function which performs this invocation. But if the pets_lib 
instantiation is a parameter of pets_tm, then I can use statements like 
the above all over pets_tm by doing a simple:

:%s/pets_lib/Lib/g

and making pets_tm parameterized; e.g.

-module(pets_tm, [Lib, DumpLimit]).

Nothing much changes, code is basically reused, we are programming 
mostly with plain old functions. The parameters that represent modules 
can even have pleasant names, as we do not need to worry about module 
namespace pollution in client code and we can rename modules easily 
without that having much impact on client code.

Then we only need to glue modules together at service starting time; e.g.

start_link() ->
   start_link(#pets_config{dir="PETS", gc={100,0.1,0.3},
                           sync_delay=1000}).

init(#pets_config{dir=Dir, gc={MT, CR, PR}, sync_delay=SyncDelay}) ->
   process_flag(trap_exit, true),
   pets_locker:start_link(),
   Lib = pets_lib:new(Dir),
   Tabs = Lib:init(),
   (pets_gc:new(Lib, MT, CR, PR)):start_link(),
   Inserters = 2 * erlang:system_info(schedulers),
   LastTid = (pets_loader:new(Lib, ?FILE_READERS,
              Inserters)):load_tables(Tabs),
   (pets_writer:new(Lib, SyncDelay)):start_link(),
   (pets_tm:new(Lib, ?DUMP_LIMIT)):start_link(LastTid),
   {ok, nostate}.

(Ironically DumpLimit is still a define here in the "main". ;))

I think all this is nice and very practical. If languages like ML have 
first class modules and that is ok, what is the problem in Erlang 
adopting this nice looking concept?

Btw there is something which irritates me in the above: Having to write 
parenthesis as in:

(pets_gc:new(Lib, MT, CR, PR)):start_link(),

it should be possible to just write:

pets_gc:new(Lib, MT, CR, PR):start_link(),

(and I profoundly hate the "new" keyword, but that is another story)

Best Regards,
Paulo
















More information about the erlang-questions mailing list