Erlang Mailing Lists

Author Message

<  Erlang questions mailing list  ~  Parameterized module idioms

Guest
Posted: Wed Apr 21, 2010 7:21 am Reply with quote
Guest
> The use-case for modules with parameters (if there is one) is
> where there are grounds for believing that there may need to
> be multiple distinct instances of the same module at the same
> time AND where the module parameter cascade is tolerable.

Are there any good examples of this?

What about the motivation described in Richard Carlsson's paper
(http://portal.acm.org/citation.cfm?id=940885) about callback module
usage?

________________________________________________________________
erlang-questions (at) erlang.org mailing list.
See http://www.erlang.org/faq.html
To unsubscribe; mailto:erlang-questions-unsubscribe@erlang.org

Post received from mailinglist
Guest
Posted: Wed Apr 21, 2010 8:39 am Reply with quote
Guest
I would rather focus on chapter 6 in that paper because that is what
will happen when people think that this is "the Erlang way".

Doom and destruction is coming with this Razz

/Mazen

On 21/04/2010 09:19, Tim Fletcher wrote:
>> The use-case for modules with parameters (if there is one) is
>> where there are grounds for believing that there may need to
>> be multiple distinct instances of the same module at the same
>> time AND where the module parameter cascade is tolerable.
>>
> Are there any good examples of this?
>
> What about the motivation described in Richard Carlsson's paper
> (http://portal.acm.org/citation.cfm?id=940885) about callback module
> usage?
>
> ________________________________________________________________
> erlang-questions (at) erlang.org mailing list.
> See http://www.erlang.org/faq.html
> To unsubscribe; mailto:erlang-questions-unsubscribe@erlang.org
>
>

---------------------------------------------------

---------------------------------------------------

WE'VE CHANGED NAMES!

Since January 1st 2010 Erlang Training and Consulting Ltd. has become ERLANG SOLUTIONS LTD.

www.erlang-solutions.com


________________________________________________________________
erlang-questions (at) erlang.org mailing list.
See http://www.erlang.org/faq.html
To unsubscribe; mailto:erlang-questions-unsubscribe@erlang.org

---------------------------------------------------

---------------------------------------------------

WE'VE CHANGED NAMES!

Since January 1st 2010 Erlang Training and Consulting Ltd. has become ERLANG SOLUTIONS LTD.

www.erlang-solutions.com

Post received from mailinglist
Guest
Posted: Wed Apr 21, 2010 10:11 am Reply with quote
Guest
> I would rather focus on chapter 6 in that paper because that is what
> will happen when people think that this is "the Erlang way".
>
> Doom and destruction is coming with this Razz

Agreed, I think it's a feature that's very likely to be over-used.

My previous two questions were a bit devil's advocate-esque. They lead
to: what use cases are parameterized modules really intended for
(i.e., things that are impossible/awkward to do otherwise), and do the
benefits of supporting these use cases outweigh the costs/
disadvantages of adding this feature to the language?

I don't think the use case in that paper is sufficient to introduce
the feature on its own, considering the costs. The use case that
Richard O'Keefe describes previously makes sense, but needs concrete
examples.

Tim

________________________________________________________________
erlang-questions (at) erlang.org mailing list.
See http://www.erlang.org/faq.html
To unsubscribe; mailto:erlang-questions-unsubscribe@erlang.org

Post received from mailinglist
Guest
Posted: Wed Apr 21, 2010 3:35 pm Reply with quote
Guest
Hi again,

Richard O'Keefe wrote:

> If they really *are* nonrelated, they should be separate parameters anyway.
> If they are related, they can be in one data structure (which might be
> a closure). "Pollution" is not a property of closures, but of a coding
> style.

The pollution I mean is the passing around along the invocation chain
from the "root" function up to the "leaf" functions, of several possibly
unrelated parameters.

>> If some code would need to be generated if I didn't have parameterized
>> modules, then parameterized modules already give me something.
>
> What? Automatic code generation isn't anything you need to be aware of,
> let alone involved with.

Ok. I can buy that. It is some extra infrastructure but not my problem.

>> 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 this is the Functional Programming Lesson:
> there *is* a change in the interface of the function!

The interface is what matters to client code, not internals. The
interface has not changed:

Before: I supply an atom and an integer and get a boolean.

After: I supply an atom and an integer and get a boolean.

The client code that performs the invocation of the function does not
see a change in the interface and does not need to be changed. More
precisely: the invocations in the same module (intra-module calls) do
not need to be changed. Invocations in other modules will be changed in
that now the module name in the call is a variable; i.e. from m:f(P) to
M:f(P).

> In the first case, the function uses nothing but its arguments.
> In the second case, the function has an extra parameter (DumpLimit).
> The function is _really_
>
> need_dump(%Hidden%, Tab, LogOps) ->
> LogOps > %Hidden%#%hidden%.DumpLimit * ets:info(Tab, size).
>
> How it's _compiled_ is a separate issue; I'm arguing about what it _means_.

But it almost seems like you are talking about how it is compiled Wink
What the function _uses_ is irrelevant to its interface. It is as if
saying that the values captured by a closure are part of its interface,
because the closure definition uses them.

That now an extra parameter is used is just an implementation choice,
not a fundamental thing in the concept. One could have another
implementation (not that I am saying it would be a good idea) were the
module source is compiled at runtime with the parameters substituted and
then loaded, resulting in a module like

m_instance_1213124

which has the same functions, with the exact same interface, and no
hidden extra parameter. And when a client does

M:f(P)

what _really_ happens could be

m_instance_1213:f(P)

which would not be much different than doing now:

M = lists,
M:sort([3,1,2]).

with no hidden extra parameters being passed. Such an implementation
would have very different consequences, both disadvantages like runtime
instantiation costs and possible errors, and advantages like possible
optimizations using runtime knowledge, and I am not saying it would be
realistic or in the spirit of what we expect. I am just saying that the
interface remains the same, but now the function belongs to a module
that is only computed at runtime and that for intra-module calls even
that is irrelevant.

>> 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?
>
> The problem is that you can't.

Yes I can Wink
I actually could.

> Yes, you *can* replace pets_lib: by Pets_Lib:, but now
> - either you have to pass Pets_Lib around all over the place,
> which doesn't count as "little effort", or
> - you have to pass Pets_Lib as a parameter to the module containing

Exactly: if it is a paramter in the client mode. Then it is little
effort. It really WAS little effort in rewriting my library.

> this call, which transitively affects its callers as well, ...

This is an interesting point, which I don´t know if it was much
discussed here. I tended to notice that to be able to have nice
little-effort changes, client modules end up having parameters. This is
a sort of "viral" phenomena, which we want to contain.

This viral aspect made me think that when building an abstraction, the
module(s) that are exposed to the outside world should NOT be
parameterized, while the modules used internally can be parameterized if
it helps productivity in writing code.

This is what I ended up with, looking at each module definition:

pets.erl:-module(pets).
pets_gc.erl:-module(pets_gc, [Lib, MaxTids, CollectRatio, PurgeRatio]).
pets_lib.erl:-module(pets_lib, [PetsDir]).
pets_loader.erl:-module(pets_loader, [Lib, MaxReaders, MaxInserters]).
pets_locker.erl:-module(pets_locker).
pets_tm.erl:-module(pets_tm, [Lib, DumpLimit]).
pets_writer.erl:-module(pets_writer, [Lib, SyncDelay]).
test.erl:-module(test).

The only module that clients use, "pets", is not parameterized. The
modules used internally are either parameterized:

pets_gc, pets_lib, pets_loader, pets_tm, pets_writer

or not parameterized:

pets_locker
test

> - and you had better first take care to rename any existing occurrences
> of "Pets_Lib" to something else

Of course. But it is easy to: invent a nice name; then check that it
doesn´t occur in any module.

>
> Perhaps we can name this a "module parameter cascade".
>
>> 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.
>
> Looks, as they say, can be deceiving.
>
>> 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.
>
> (a) People seriously underestimate what closures can do.
> (b) This is not an argument for modules with parameters,
> it is an argument for nested functions.

Not sure what you mean. Using closures will have a greater impact on
client code, and also on the implementation code that I am trying to reuse.

>
>> 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
>
> You had better pray desperately to whatever god(s) you recognise
> that there are no other occurrences of Lib, and while you're at

easy: grep Lib *erl and look at the result.

> it, beg forgiveness for breaking the name link between the module
> pets_lib and the module instance variable Lib (which would be
> better as Pets_Lib, so that the only difference between the module
> and the instance variable is capitalisation).

This is a curious point. I am aware that I broke the connection. It was
intentional. Before it was pets_lib only because the application is
called pets. But I see it as my general library from this application. I
preferably wouldn´t want to worry about what the application is going
to be called and to have to change all over from "pets_lib" to
"amnesia_lib" if I decide to rename it. This kind of use is the normal
convention in Erlang, having to worry about global module namespace
pollution. Another advantage of using variable for modules, like "Lib"
above, is having to worry less about that kind of pollution, and what
the application is going to be called. Wink

>> Then we only need to glue modules together at service starting time; e.g.
>
> That is, you are making a change to a module which requires
> - *remote* compensatory changes
> - to possibly *many* service startups
> - which previously never mentioned the module in question at all.

I dont't understand what you mean. But all this gluing is done in the
"main" for the internal modules of the application. As I exemplified, it
was easy to do. The result is also easy to reason about. After
instantiation, everything will behave as if the values passed to the
glued modules had been -define'ed constants; no strange side-effects;
referential transparency; functional style using "POF"s: plain old
functions. (Does this term exist? Wink)

> Let's take the data base example.
>
>> need_dump(Tab, LogOps) -> LogOps > ?DUMPLIMIT * ets:info(Tab, size).
>
> We want to make DUMP_LIMIT something that can be configured at
> run time. But that's easy!
>
> need_dump(Tab, LogOps) ->
> my_config:dump_limit(Tab) * ets:info(Tab, size).
>
> where
> -module(my_config).
> -export([dump_limit/1]).
>
> dump_limit(_Tab) -> ?DUMP_LIMIT.
>
> To change the configured value, load a new version of my_config.
> To select a value at system startup, select which version of the
> configuration module to load.

Here there is a recompilation and selection of module. But if we want to
compute at startup time some value to serve as "parameter" , that value
will have to be stored somewhere in a globally acessible data structure
like ets, to be consulted by my_config:dumplimit/1. That can sometimes
be slow.

Now I remember that one of the "permissible" uses for the process
dictionary is to store "parameters" written once but never changed
later. This kind of use ties code with the process structure and the use
of get/1 can be slow. Parameterized modules can make some of these uses
avoidable.

> The use-case for modules with parameters (if there is one) is
> where there are grounds for believing that there may need to
> be multiple distinct instances of the same module at the same
> time AND where the module parameter cascade is tolerable.

A quite common example would be several instances of a web server
together listening in different ports. But in this case the module(s)
exposed to the client being parameterized, contrary to my "guideline"
above, would lead to the client possibly storing instances in some data
structures to avoid being itself parameterized and containing the
"viral" impact of parameterized modules.

Best regards,
Paulo




________________________________________________________________
erlang-questions (at) erlang.org mailing list.
See http://www.erlang.org/faq.html
To unsubscribe; mailto:erlang-questions-unsubscribe@erlang.org

Post received from mailinglist
Guest
Posted: Wed Apr 21, 2010 11:06 pm Reply with quote
Guest
There's quite a lot to reply to, and I don't have the time right now.
I'll just make a few points.

> Here there is a recompilation and selection of module. But if we
> want to compute at startup time some value to serve as "parameter" ,
> that value will have to be stored somewhere in a globally acessible
> data structure like ets, to be consulted by my_config:dumplimit/1.
> That can sometimes be slow.

If we want to compute at startup time some value to serve as a
"parameter",
that value will have to be stored somewhere,
AND THAT SOMEWHERE CAN BE A MODULE.

There is no law that says a module can't be written by a program.

Here's an actual transcript.

Eshell V5.7.2 (abort with ^G)
1> c(reload).
{ok,reload}
2> reload:put(27).
{module,reloadable}
3> reload:get().
27
4> reload:put(42).
{module,reloadable}
5> reload:get().
42
6> reload:put(137).
{module,reloadable}
7> reload:get().
137
8> reload:put([<<1>>,<<2,3>>,<<4,5,6>>]).
{module,reloadable}
9> reload:get().
[<<1>>,<<2,3>>,<<4,5,6>>]

Now here's the proof-of-concept implementation.
Using compile:forms(..., [binary,...]) we can get the
same effect without touching the file system, and in
a "real" implementation that's what I'd do.

-module(reload).
-export([put/1,get/0]).

put(Datum) ->
{ok,Device} = file:open("reloadable.erl", [write]),
io:format(Device,
"-module(reloadable).~n-export([datum/0]).~ndatum() ->~n
~p .~n",
[Datum]),
ok = file:close(Device),
compile:file("reloadable", []),
code:purge(reloadable),
code:load_file(reloadable).

get() ->
reloadable:datum().

And yes, I do realise that what we have here is a global mutable
variable with an amazingly slow assignment statement,
but that's precisely what a changeable configuration parameter IS.

The overhead of of setting the parameter up (or changing it) is
moderately high (although using compile:forms(..., [binary,...])
would be more efficient as well as safer). But the overhead of
*using* the parameter is the same as the overhead of any
cross-module function call. And if that weren't tolerable, we
wouldn't be using Erlang.

And of course this can be generalised in a whole lot of ways.

>
> Now I remember that one of the "permissible" uses for the process
> dictionary is to store "parameters" written once but never changed
> later. This kind of use ties code with the process structure and the
> use of get/1 can be slow.

Slow? Time for some numbers.

6> getput:k(100000000).
[{variant,constant},{result,100000000},{time,530}]
7> getput:b(100000000).
[{variant,direct},{result,100000000},{time,1730}]
8> getput:t(100000000).
[{variant,dictionary},{result,100000000},{time,2290}]

Here's the code that produced those:

-module(getput).
-export([t/1, b/1, k/1]).

t(N) ->
put(key, 1),
{T0,_} = statistics(runtime),
R = loop(N, 0),
{T1,_} = statistics(runtime),
[{variant,dictionary},{result,R}, {time,T1-T0}].

loop(0, R) -> R;
loop(N, R) -> loop(N-1, R+get(key)).

b(N) ->
{T0,_} = statistics(runtime),
R = loup(N, 0),
{T1,_} = statistics(runtime),
[{variant,direct},{result,R}, {time,T1-T0}].

loup(0, R) -> R;
loup(N, R) -> loup(N-1, R+(N div N)).

k(N) ->
{T0,_} = statistics(runtime),
R = lowp(N, 0),
{T1,_} = statistics(runtime),
[{variant,constant},{result,R}, {time,T1-T0}].

lowp(0, R) -> R;
lowp(N, R) -> lowp(N-1, R+1).

So
- the loop that just adds 1 takes 5.3 ns per iteration
- the loop that adds N div N takes 17.3 ns per iteration
- the loop that uses get() takes 22.9 ns per iteration
We conclude that
- N div N takes 12 ns
- get(key) takes 17.6 ns
and therefore
EITHER my benchmark and interpretation are hopelessly fouled up
OR using get/1 is NOT particularly slow.

To be honest, I incline to the former; how can looking something up
in a hash table be so good compared with an integer division?

While it may be true that get/1 _can_ be slow (I'd need to see the
numbers), you should never just _assume_ that get/1 is slow for
the use you intend to make of it.
>
> A quite common example would be several instances of a web server
> together listening in different ports.

It's not clear why the port should be part of a web server's context
rather than part of its state, or why these instances need to be all
together in a single Erlang node (because if they aren't, module
parameters offer us no convenience), or why if they are all in a single
node "they" shouldn't be "it", a single system listening on several
ports and doing load balancing of some sort.
>


________________________________________________________________
erlang-questions (at) erlang.org mailing list.
See http://www.erlang.org/faq.html
To unsubscribe; mailto:erlang-questions-unsubscribe@erlang.org

Post received from mailinglist
Guest
Posted: Thu Apr 22, 2010 9:51 am Reply with quote
Guest
Taking about numbers, I was interested in the difference
between parametrized modules and closures call overhead.

And the result is that parametrized modules seems to be
nearly two times faster than closure:

1> test:test(100000000).
[{closure,10756202},{module,6001309}]

Here's the code:

----------------------------------------
-module(test).

-export([test/1,
call/3]).

-record(?MODULE, {count = 0}).

test(Count) ->
C = time(fun test_closure/1, Count),
M = time(fun test_module/1, Count),
[{closure, C}, {module, M}].

time(F, A) ->
T1 = erlang:now(),
F(A),
T2 = erlang:now(),
timer:now_diff(T2, T1).

test_closure(Count) ->
C1 = new_closure(42),
C2 = new_closure(1Cool,
C1(call, {C2, Count}).

test_module(Count) ->
M1 = new_module(42),
M2 = new_module(1Cool,
M1:call(M2, Count).

new_closure(Start) -> make_closure(#?MODULE{count = Start}).

new_module(Start) -> #?MODULE{count = Start}.

call_closure(_Other, 0, State) -> State#?MODULE.count;
call_closure(Other, Count, State) ->
NewState = State#?MODULE{count = State#?MODULE.count + 1},
Other(call, {make_closure(NewState), Count - 1}).

call_module(_Other, 0, State) -> State#?MODULE.count;
call_module(Other, Count, State) ->
NewState = State#?MODULE{count = State#?MODULE.count + 1},
Other:call(NewState, Count - 1).

call(Other, Count, State) -> call_module(Other, Count, State).

make_closure(State) ->
fun(call, {Other, Count}) -> call_closure(Other, Count, State) end.
----------------------------------------

And yes I know it's not a real parametrized module,
but the function calls are equivalent, isn't it ?


--
Sebastien Merle.

________________________________________________________________
erlang-questions (at) erlang.org mailing list.
See http://www.erlang.org/faq.html
To unsubscribe; mailto:erlang-questions-unsubscribe@erlang.org

Post received from mailinglist
Guest
Posted: Thu Apr 22, 2010 5:23 pm Reply with quote
Guest
> If we want to compute at startup time some value to serve as a "parameter",
> that value will have to be stored somewhere,
> AND THAT SOMEWHERE CAN BE A MODULE.
>
> There is no law that says a module can't be written by a program.

Your are right, but it is more unpleasant/cumbersome than using
parameterized modules.


> -module(reload).
> -export([put/1,get/0]).
>
> put(Datum) ->
> {ok,Device} = file:open("reloadable.erl", [write]),
> io:format(Device,
> "-module(reloadable).~n-export([datum/0]).~ndatum() ->~n ~p
> .~n",
> [Datum]),
> ok = file:close(Device),
> compile:file("reloadable", []),
> code:purge(reloadable),
> code:load_file(reloadable).

Interesting that this reminds the hypothetical implementation for
parameterized modules I sketched in the previous mail, in which a module
would be compiled with the parameter substituted. But here we use a
well-know name for the module, and do not have to propagate the
generated module name in a variable.


> And yes, I do realise that what we have here is a global mutable
> variable with an amazingly slow assignment statement,
> but that's precisely what a changeable configuration parameter IS.
>
> The overhead of of setting the parameter up (or changing it) is
> moderately high (although using compile:forms(..., [binary,...])
> would be more efficient as well as safer). But the overhead of
> *using* the parameter is the same as the overhead of any
> cross-module function call. And if that weren't tolerable, we
> wouldn't be using Erlang.

In this case I agree that the overhead of using it is negligible. But
while it may be common that a configuration is something done at start
time and we can afford it to be slow (like in my database); not always
may that be the case, and having to resort to a mechamism (like module
compilation) that implies slow (re)configuration may sometimes be a problem.

>> Now I remember that one of the "permissible" uses for the process
>> dictionary is to store "parameters" written once but never changed
>> later. This kind of use ties code with the process structure and the
>> use of get/1 can be slow.
>
> Slow? Time for some numbers.
>
> 6> getput:k(100000000).
> [{variant,constant},{result,100000000},{time,530}]
> 7> getput:b(100000000).
> [{variant,direct},{result,100000000},{time,1730}]
> 8> getput:t(100000000).
> [{variant,dictionary},{result,100000000},{time,2290}]
>
> Here's the code that produced those:
>
> -module(getput).
> -export([t/1, b/1, k/1]).
>
> t(N) ->
> put(key, 1),
> {T0,_} = statistics(runtime),
> R = loop(N, 0),
> {T1,_} = statistics(runtime),
> [{variant,dictionary},{result,R}, {time,T1-T0}].
>
> loop(0, R) -> R;
> loop(N, R) -> loop(N-1, R+get(key)).
>
> b(N) ->
> {T0,_} = statistics(runtime),
> R = loup(N, 0),
> {T1,_} = statistics(runtime),
> [{variant,direct},{result,R}, {time,T1-T0}].
>
> loup(0, R) -> R;
> loup(N, R) -> loup(N-1, R+(N div N)).
>
> k(N) ->
> {T0,_} = statistics(runtime),
> R = lowp(N, 0),
> {T1,_} = statistics(runtime),
> [{variant,constant},{result,R}, {time,T1-T0}].
>
> lowp(0, R) -> R;
> lowp(N, R) -> lowp(N-1, R+1).
>
> So
> - the loop that just adds 1 takes 5.3 ns per iteration
> - the loop that adds N div N takes 17.3 ns per iteration
> - the loop that uses get() takes 22.9 ns per iteration
> We conclude that
> - N div N takes 12 ns
> - get(key) takes 17.6 ns
> and therefore
> EITHER my benchmark and interpretation are hopelessly fouled up
> OR using get/1 is NOT particularly slow.
>
> To be honest, I incline to the former; how can looking something up
> in a hash table be so good compared with an integer division?

I also found these numbers fishy. I thought about it and wondered if a
considerable part of time is being spent dealing with operations on
integers. I have a doubt: do big integers start larger in 64 bits than
in 32? The efficiency guide still just says for small integers: Integer
(-16#7FFFFFF < i <16#7FFFFFF). So either things are still the same or
the guide has not been updated. If things are the same as for 32 bits,
the time may be spent manipulating big integers. To test this hypothesis
I added another variant:

s(N) ->
{T0,_} = statistics(runtime),
R = losp(N, 0),
{T1,_} = statistics(runtime),
[{variant,small_numbers},{result,R}, {time,T1-T0}].

losp(0, R) -> R;
losp(N, R) -> losp(N-1, (R+1) band 255).

This variant also does some manipulation of a parameter, but the result
is always a small integer. Running:

air:tmp psa$ erlc +native getput.erl
air:tmp psa$ erl
Erlang R13B01 (erts-5.7.2) [source] [64-bit] [smp:2:2] [rq:2]
[async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.7.2 (abort with ^G)
1> getput:k(100000000).
[{variant,constant},{result,100000000},{time,610}]
2> getput:s(100000000).
[{variant,small_numbers},{result,0},{time,340}]
3> getput:b(100000000).
[{variant,direct},{result,100000000},{time,3620}]
5> getput:t(100000000).
[{variant,dictionary},{result,100000000},{time,7110}]

Here, the "s" variant is event faster than the "k" one. This version is
also accessing a parameter and doing some computation, but only doing an
increment at most on a big integer. But this does not explain the large
time in the "b" variant. I though the problem could be the "div"
operation. Added the variant:

d(N) ->
{T0,_} = statistics(runtime),
R = lodp(N, 0),
{T1,_} = statistics(runtime),
[{variant,no_div},{result,R}, {time,T1-T0}].

lodp(0, R) -> R;
lodp(N, R) -> lodp(N-1, R + ((N+1) - N)).

which results in:

1> getput:d(100000000).
[{variant,no_div},{result,100000000},{time,730}]


Conclusion: the time for the non-get versions comes from the use of
"div" and the number of times a possibly big integer is manipulated.

To measure more accurately what time "get" takes. I wrote:

-module(m).
-export([v/1, g/1]).

loop(_F, 0) -> ok;
loop(F, N) -> F(), loop(F, N-1).

run(F, N) ->
T1 = now(),
loop(F, N),
T2 = now(),
timer:now_diff(T2, T1) * 1000 div N.

v(N) -> run(fun() -> 1 end, N).

g(N) -> put(key, 1), run(fun() -> get(key) end, N).


Here the "v" function loops a function which just returns 1, and the "g"
version is only different in that a get is performed. The result is the
time elapsed in nanoseconds.

erlc +native m.erl
air:code psa$ erl
Erlang R13B01 (erts-5.7.2) [source] [64-bit] [smp:2:2] [rq:2]
[async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.7.2 (abort with ^G)
1> m:v(100000000).
8
2> m:g(100000000).
73

So, the get takes around 65 nanosecs. To test the use of parameters of
parameterized modules I wrote a module:

-module(mp, [P]).

exactly the same as "m", with an extra function:

p(N) -> run(fun() -> P end, N).

Running again:

air:code psa$ erlc +native mp.erl
air:code psa$ erl
Erlang R13B01 (erts-5.7.2) [source] [64-bit] [smp:2:2] [rq:2]
[async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.7.2 (abort with ^G)
1> M = mp:new(10).
{mp,10}
2> M:v(100000000).
8
3> M:p(100000000).
8
4> M:g(100000000).
71

Conclusion: using a parameter is negligible; using a parameterized
module in this case is negligible compared to what the module did before.

I remain with the opinion I had before, that get is too slow (for this
purpose, compared with using parameters). If I need to lookup up a dozen
parameters to serve a request, there goes almost 1 microsecond of wasted
CPU.

And I use the process dictionary. I basically ignore all the fuss about
how bad it is to use it. It is the fastest hash table we have in Erlang,
appropriate to store large terms with possible substructure sharing,
which would grind ets to a halt or possibly make memory usage explode.

But they are not a substitute for "instantly" acessible parameters.

>
> While it may be true that get/1 _can_ be slow (I'd need to see the
> numbers), you should never just _assume_ that get/1 is slow for
> the use you intend to make of it.

I didn´t. Wink

>> A quite common example would be several instances of a web server
>> together listening in different ports.
>
> It's not clear why the port should be part of a web server's context
> rather than part of its state, or why these instances need to be all
> together in a single Erlang node (because if they aren't, module
> parameters offer us no convenience), or why if they are all in a single
> node "they" shouldn't be "it", a single system listening on several
> ports and doing load balancing of some sort.

Ok. Thinking about it, it was a bad example. It may be common, but in
this case one would expect port numbers to change and plan ahead
accordingly; and as you say, they would belong naturally to its state.

Regards,
Paulo

________________________________________________________________
erlang-questions (at) erlang.org mailing list.
See http://www.erlang.org/faq.html
To unsubscribe; mailto:erlang-questions-unsubscribe@erlang.org

Post received from mailinglist
Guest
Posted: Fri Apr 23, 2010 4:00 am Reply with quote
Guest
A module with parameters is basically a surpassingly strange
way to write a function that returns a record of functions.

Right now, we can do exactly that:

new(V1, ..., Vk) ->
#record{
f1 = fun (...) -> ... end,
...
fm = fun (...) -> ... end
}.

This thing, which I regard as an aggregate of closures,
can be passed around (just as an instance of a module with
parameters can), and the functions can be invoked:

R#record.fi(...)

There are precisely two differences between this application of
the closure idea and modules with parameters.
(A) functions defined in the record like that cannot be recursive
(B) the invocation doesn't look like a remote function call.

We could make it look _more_ like a remote function call if
only my "frames" proposal were adopted, a proposal which has been
around for several years and addresses a lot of issues, and which
has the merit of being basically the same as Joe Armstrong's
"proper structs" proposal.

With frames, we'd write

new(V1, ..., Vk) ->
<{ f1 ~ fun (...) -> ... end
, ...
, fm ~ fun (...) -> ... end
}>.

We now add one new feature:
if E0 evaluates to a frame,
and F is an atom
and F is a key in the frame E0
then E0:F(E1, ..., En) means
case E0 of <{F ~ Fun}> -> Fun(E1, ..., En) end

This fixes (B).

We can fix (A) without requiring cyclic data structures in the
VM with a change to that definition:

case E0 of V0 = <{F ~ Fun}> -> Fun(V0, E1, ..., En) end

and then we'd have to write

<{ f1 ~ fun (M, ...) -> ... M:fk(...) ... end
, ...
, fm ~ fun (M, ...) -> ... M:fj(...) ... end
}>

which is a bit of a pain, but doable.

My argument here is
- we want frames for a lot of other reasons,
- once we have frames, they can do what modules with parameters do
- modules with parameters can't do all the things frames can
- so who needs modules with parameters?

One thing that's easy with frames is to check that
all the functions we want are there:

client(Frame ~ <{ f1 ~ F1, ..., fk ~ Fk }>, ...)
when is_function(F1, n1), ..., is_function(Fk, nk) ->
... use Frame ...



________________________________________________________________
erlang-questions (at) erlang.org mailing list.
See http://www.erlang.org/faq.html
To unsubscribe; mailto:erlang-questions-unsubscribe@erlang.org

Post received from mailinglist
vladdu
Posted: Fri Apr 23, 2010 6:30 am Reply with quote
User Joined: 28 Feb 2005 Posts: 397 Location: Gothenburg, Sweden
Hi,

2010/4/23 Richard O'Keefe <ok@cs.otago.ac.nz>:
> A module with parameters is basically a surpassingly strange
> way to write a function that returns a record of functions.
>
> Right now, we can do exactly that:
>
>
View user's profile Send private message
cheung
Posted: Tue Feb 21, 2012 3:29 am Reply with quote
Guest
Gingrich made his remarks at a rally in the congressional district Nike Air Max 2012 he represented for 20 years, speaking to a few hundred supporters. Nike Air Max LTD 2 He planned several campaign stops across Air Max Georgia on Saturday Nike Air Max 1 with Herman Cain, a fellow Georgian and former contender for the GOP nomination who has since endorsed Gingrich.CNN Cheap Air Max was forced to cancel the debate, scheduled to take place in Atlanta on March 1, after Mitt Romney declined to participate. Nike Air Max 95 Rick Santorum quickly followed suit.The cancellation was a blow to Gingrich, who is banking on a strong showing on Super Tuesday, March 6, in Air Max 95 Cheap Georgia, Ohio and eight other states holding contests that day."The average Georgian is going to say, the average Nike Air Max 90 Ohioan is going to say, `Let me get this straight. They won't come here to debate Nike Air Max but they want my vote?"' Gingrich said, adding, "Anybody who's Nike Air Max Classic BW afraid of debating Newt Gingrich isn't going to be in very good shape to debate Barack Obama."Gingrich, whose sole win Asics Tiger came in South Carolina's Asics Mexico 66 primary Jan. 21, conceded winning Georgia was "crucial" Asics Kaufen to sustaining his presidential bid. His candidacy has struggled since Romney soundly beat him in Florida Jan. 31 and Santorum won contests in Colorado, Minnesota and Missouri on Feb. 7. “You have an opportunity to speak loudly,” Santorum said at an appearance in Mason, Ohio. Asics Onitsuka Tiger“Give a clear contrast between the two leaders who want to lead this country. Give Asics Turnschuhe America a clear choice.” Though Romney was born and raised in Michigan, his wealthy upbringing and work as a private-equity executive has made it hard for him to connect with working-class voters.The event was sponsored by the Günstige Onitsuka Tiger Sneakers Michigan Faith and Freedom Coalition, three Macomb County tea party groups and the county Republican Party. Asics Mexico 66 günstig The rally was punctuated by gospel singing and speeches Asics Tiger Mexico 66 by ministers.
wuji
Posted: Tue Aug 14, 2012 7:29 am Reply with quote
User Joined: 10 Aug 2012 Posts: 654
for information leading to the capture of Miguel Angel and and cheap polo ralph lauren and Omar.The brothers, whose numeric aliases refer to their alleged
within the Zetas at the time of the cartel's creation creation [h4]jordan 6[/h4] creation several years ago, are now allegedly top leaders of
organization that controls drug trafficking in the east and south south [h4]cheap jordans[/h4] south of Mexico. Miguel, or "40," allegedly runs the Zetas
with "3," Heriberto Lazcano.The Zetas began in 1999 when former former jordan 6 former members of the Mexican military signed on to work
security for the Gulf drug cartel. The Zetas went into into knockoff designer *beep* into business for themselves and are now at war with
View user's profile Send private message

Display posts from previous:  

All times are GMT
Page 3 of 3
Goto page Previous  1, 2, 3
This forum is locked: you cannot post, reply to, or edit topics.

Jump to:  

You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You cannot download files in this forum