[erlang-questions] Parameterized module initialization
Ulf Wiger
ulf@REDACTED
Thu Jun 28 12:53:36 CEST 2012
On 28 Jun 2012, at 11:23, Loïc Hoguin wrote:
> Second, you actually make things more confusing. See this small snippet:
>
> X = {utf8_string, 1, 2, 3},
> X:some_function() %% this calls utf8_string:some_function/1
>
> Y = utf8_string,
> Y:some_function() %% this calls utf8_string:some_function/0
>
> This means that you need to know what's in your variable to know the function that will be called. It's fine if your application is small and you know every bits of it. But for bigger systems where these variables might be set in different applications or different nodes, you never know what you'll get. This makes things incredibly hard to read and debug.
Well, this is just as true for funs, and basically also for gen_servers,
where the results of your function calls depend on what state the
server has - which is actually worse in many ways, since the code
is no longer referentially transparent, and thus much, much more
difficult to test.
As it happens, we consider this a feature, since interacting with the
(ever changing) outside world is part of the problem domain that
we want to attack.
I have for many years fought many programmers' urge to abuse
apply/3 in all kinds of settings. It can truly lead to code that is
impossible to read. Even so, I love the meta-programming
capabilities of Erlang, and wouldn't want to live without them.
You just have to learn when to use them in a way that *improves*
readability rather than the other way around.
The question here should be, IMHO, given that we already have
ways to create closures and behaviors, do we really need another
similar feature?
I found the technique described by Joe immensely useful in ErlHive,
where I also dynamically created these tuples in order to achieve
function dispatch with access control. For one thing, it allowed me to
do things like:
blogs_for_which_i_am_admin() ->
Caller = erlhive.user:auth(),
[B || {B,_} <- erlhive.user:via_index(blogs, is_admin, Caller)].
(http://erlhive.svn.sourceforge.net/viewvc/erlhive/trunk/lib/erlhive/examples/blog/src/flexiblog.erl?revision=69&view=markup)
(I.e. dynamically creating an access control environment through
parameterized modules)
and, on the caller side:
fetch() ->
erlhive:with_user(
<<"user1">>,
fun(M) ->
Id = M:apply(erlhive.blog.flexiblog, string_to_id,
["ba/user1/1/1"]),
[_|_] = M:apply(erlhive.blog.flexiblog, read_article, [Id])
end).
(http://erlhive.svn.sourceforge.net/viewvc/erlhive/trunk/lib/erlhive/examples/blog/src/blog_test.erl?revision=69&view=markup)
Where I set up access by calling with_user(), which returned a
parameterized API module, which allowed me to control which
functions could be called, and the library code (above) to inspect
static variables (actually, virtual functions, so they couldn't be subverted)
and learn about the access profile.
The code under the covers was not easy to understand, but I do think
that the user-level code became quite clean.
BR,
Ulf W
PS Elsewhere, I have done similar things with funs, but I don't think
that they are as fit for this particular purpose.
Ulf Wiger, Co-founder & Developer Advocate, Feuerlabs Inc.
http://feuerlabs.com
More information about the erlang-questions
mailing list