[erlang-questions] Why do we need modules at all?
Jesper Louis Andersen
jesper.louis.andersen@REDACTED
Tue May 24 11:46:08 CEST 2011
On Tue, May 24, 2011 at 11:05, Kresten Krab Thorup <krab@REDACTED> wrote:
>
> One of the things he's arguing is that modules should be VALUES. A "module" in newspeak is kind of a template (function), which take other module instances as arguments. So you hook up a system by applying such templates (possibly using letrec).
>
This is a great idea. Why? Because it is the path of existential types :-)
Notice that in Erlang, a module is a Value. It is possible to call
Mod:Fun(..). Second, notice that the parameterized module extension
allows us to parameterize a module over some other module.
Specifically we could have
-module(RBTree, [OrderMod]).
where OrderMod is a module which has a function
-opaque t() :: .....
-export_type([t/0]).
-spec order(t(), t()) -> lt | eq | gt.
and so we have generalized our RedBlack trees to be able to use any
ordering relation, not simply the one that is built it.
The style Gilad is advocating is the "Fully functorial style" from the
Standard ML community. A Functor[1] in SML terminology is a function
from module to module ("functor" stems from category theory where it
designates a "function"[2] one level up). In the style, you build up
programs by stitching together smaller modularized parts building
larger and larger modules in the process.
Example: The gen_server is a functor from a module (which obeys the
gen_server behaviour and has handle_* functions) to a specific
instance of a server.
It also gives you quite the path towards the concept of OOP, as can be
observed from the parameterized module extension and its uses in
various places.
In languages with static types, you can of course enforce that functor
applications are type-correct. If you allow modules as values in such
languages you get existential types which is a way to get something
OOP-like into those languages[3].
So after a little tour around: Erlang already has this :P
Modules are an essential part of building large systems. The very
notion that you can pack up functions in a module and ship them as a
unit is quite important. Also the notion you can replace a
module-component at once. I feel that the problem of function
placement in the right module is less of a problem compared to the
hell it would be with one large global namespace. And it is much more
than just a "bunch of functions thrown together".
Specifically, modules allows me to have an *abstract* view of a data
structure. In the above, I have no idea what t() is. I am only allowed
to compare two t()'s to each other with OrderMod:compare(T1, T2). That
is, the only algebraic operation I am allowed to do is this. Other
parts of the program may have a different view of t() and be able to
do more with it, but to me, it is an opaque term. It matters because
someone can go replace t(), and my code does not have to be changed at
all. This is impossible if t() leaks. And I am grateful the dialyzer
can find places where it is leaking.
Likewise, a module can hide the fact it is implemented with a
gen_server. So I can go replace it with a gen_fsm, and nobody has to
alter their code because the API i exported was exactly the API I will
keep stable.
Processes componentize the heap.
Modules componentize the code.
[1] Beware of connotating the word functor in programming with
something specific in computer science. The meaning is different in
Standard ML, in Haskell and in C++.
[2] I am lying. The basic notion is a morphism of which the function
is a special case when considering the category of Sets.
[3] I still don't get why you can't use static typing in Erlang. The
conservative approach is simply to use polymorphic variants for all
message passing and it would essentially work right out of the bat.
--
J.
More information about the erlang-questions
mailing list