| Author |
Message |
|
| richardc at it.uu.se |
Posted: Tue Oct 17, 2000 3:55 pm |
|
|
|
Guest
|
On Tue, 17 Oct 2000, Ulf Wiger wrote:
> OK, since noone complained too much, here's a hack of lists.erl,
> where most functions have been enhanced to operate on both eager
> and lazy lists.
And here is my first version of a lazy lists library. Seems to work ok.
Nothing is evaluated unless necessary. Note, though, that my
interpretation of a lazy list is (like in the example that Tobbe gave, due
to Phil Wadler) a fun:
LazyList :: () -> [] | [term() | LazyList]
This makes things easier. The way Ulf implemented them can be seen as
using an explicit push-back buffer for an initial number of known
elements, but this makes programming with fully lazy lists more difficult,
because many more cases must be handled. (In my implementation, you can
push stuff back using e.g. `cons(H, T)' when necessary.) Also, in Ulfs
current implementation, either a list is empty or the first element has
been computed. (This could be fixed, though.)
/Richard
Richard Carlsson (richardc_at_csd.uu.se) (This space intentionally left blank.)
E-mail: Richard.Carlsson_at_csd.uu.se WWW: http://www.csd.uu.se/~richardc/
Post generated using Mail2Forum (http://m2f.sourceforge.net) |
|
|
| Back to top |
|
| Sean.Hinde at one2one.co. |
Posted: Tue Oct 17, 2000 6:12 pm |
|
|
|
Guest
|
Hi,
> > OK, since noone complained too much, here's a hack of lists.erl,
> > where most functions have been enhanced to operate on both eager
> > and lazy lists.
>
> And here is my first version of a lazy lists library. Seems
> to work ok.
Just so I (and I guess others?) understand how all this would be applied to
the original problem (that iterating over very large lists causes large
heaps to be generated)..
If we already have a large list and want to generate another I guess there
would there be no benefit of converting this into a lazy_list first? (would
this make a Huge fun?)
If we already have the list and want to do say a checksum calc on it
(foreach/2) there is no advantage because we still have to run through all
the elements (ok, one at a time)?
So this could be used where the list itself doesn't already exist and we
want to iterate to produce a result which isn't a list? Wouldn't the stack
then fill up with the not yet calculated results of each iteration?
My head is starting to spin. Under what circumstances would this stuff be
very appropriate to use?
Thanks for the code BTW - wow,
Sean
NOTICE AND DISCLAIMER:
This email (including attachments) is confidential. If you have received
this email in error please notify the sender immediately and delete this
email from your system without copying or disseminating it or placing any
reliance upon its contents. We cannot accept liability for any breaches of
confidence arising through use of email. Any opinions expressed in this
email (including attachments) are those of the author and do not necessarily
reflect our opinions. We will not accept responsibility for any commitments
made by our employees outside the scope of our business. We do not warrant
the accuracy or completeness of such information.
Post generated using Mail2Forum (http://m2f.sourceforge.net) |
|
|
| Back to top |
|
| etxuwig at etxb.ericsson. |
Posted: Wed Oct 18, 2000 8:41 am |
|
|
|
Guest
|
On Tue, 17 Oct 2000, Richard Carlsson wrote:
>And here is my first version of a lazy lists library. Seems to work ok.
>Nothing is evaluated unless necessary. Note, though, that my
>interpretation of a lazy list is (like in the example that Tobbe gave, due
>to Phil Wadler) a fun:
>
> LazyList :: () -> [] | [term() | LazyList]
>
>This makes things easier. The way Ulf implemented them can be seen as
>using an explicit push-back buffer for an initial number of known
>elements, but this makes programming with fully lazy lists more difficult,
>because many more cases must be handled. (In my implementation, you can
>push stuff back using e.g. `cons(H, T)' when necessary.) Also, in Ulfs
>current implementation, either a list is empty or the first element has
>been computed. (This could be fixed, though.)
>From one perspective, it does make things easier. However, when we
(at AXD 301) landed on the definition
LazyList :: list() | [term() | fun()]
it was partly because we wanted to stay as close to today's list
syntax as possible - remember *our* objective to minimize the amount
of code we have to rewrite, when switching between a list
representation and a table representation.
It's not immediately obvious to me how this can be achieved when a
lazy list is "merely" a fun.
I will do some thinking about this.
/Uffe
--
Ulf Wiger tfn: +46 8 719 81 95
Strategic Product & System Management mob: +46 70 519 81 95
Ericsson Telecom AB, Datacom Networks and IP Services
Varuv |
|
|
| Back to top |
|
| rv at bluetail.com |
Posted: Wed Oct 18, 2000 10:24 am |
|
|
|
Guest
|
Richard Carlsson <richardc_at_it.uu.se> writes:
>
... Lots on lazy lists and a lazy list package.
>
I think that doing it as you have done making lazy lists as
LazyList :: () -> [] | [term() | LazyList]
makes things much simpler and more consistent.
However you still have the major problem that it is not integrated with
the rest of the whole system (Erlang, OTP and apps). This restricts
what you can do with it, as the rest of the system sees this as funs
not lists.
As Sean Hinde points out if you already have the big list then why
bother?
Unfortunately it would not directly help Ulf Wiger either as the large
lists are generated by calls which generate "proper" lists. To use it
you would have to write new interface functions to generate the lazy
lists and force people to use the lazy libraries. This would still
mean you would have to go through code and modify it, as you would
using th existing "lazy" interface to ETS tables.
Most importantly it can't be used in patterns!!!
As it can't be used interchangably with lists, or even list patterns,
wouldn't it be better to drop the list terminology and instead call them
something else, lazy sequences? This would help to lessen confusion.
I wouldn't even construct them with lists, use a tagged tuple instead.
I know it takes more space but I am only looking a one element at a
time so it isn't really important. This would help to remove a lot of
errors where people inadvertantly used "normal" list operations on them
which could generate very random errors. And no way that I or anyone
else would prefix their list procesing functions with a special clause!
:-)
func(LazyList, ...) when function(LazyList) -> func(LazyList(), ...);
In short if you want to implement a "lazy" sequence be extremely
explicit and not try to make look like lists, which they are not and
don't behave as.
Anyway without rewriting the result of an evaluation you can create an
aweful amount of extra work. Also you could have fun code like
L1 = eval(LazyList),
L2 = eval(LazyList),
and L1 /= L2.
Robert
--
Robert Virding Tel: +46 (0)8 545 55 017
Alteon Web Systems Email: rv_at_bluetail.com
S:t Eriksgatan 44 WWW: http://www.bluetail.com/~rv
SE-112 34 Stockholm, SWEDEN
"Folk s |
|
|
| Back to top |
|
| richardc at it.uu.se |
Posted: Wed Oct 18, 2000 11:09 am |
|
|
|
Guest
|
On Tue, 17 Oct 2000, Sean Hinde wrote:
> > And here is my first version of a lazy lists library. Seems
> > to work ok.
>
> Just so I (and I guess others?) understand how all this would be
> applied to the original problem (that iterating over very large lists
> causes large heaps to be generated)..
>
> If we already have a large list and want to generate another I guess
> there would there be no benefit of converting this into a lazy_list
> first? (would this make a Huge fun?)
Not "Huge" - the function "lazy(L)" takes your normal list and embeds it
in a small fun (no copying done) which will on demand return the next
element. The space usage is comparable to a tuple with a small number of
elements.
> If we already have the list and want to do say a checksum calc on it
> (foreach/2) there is no advantage because we still have to run through
> all the elements (ok, one at a time)?
Yes.
> So this could be used where the list itself doesn't already exist and
> we want to iterate to produce a result which isn't a list? Wouldn't
> the stack then fill up with the not yet calculated results of each
> iteration?
Well, what the result is (list or not) is not important as such, and an
iteration like the following executes in constant space:
sum(L) -> sum(L, 0).
sum(L, A) ->
case L() of
[H | T] -> sum(T, A + H);
[] -> A
end.
What happens is that the "lazy list" will never really be built in memory
as a list - the producer just uses cons cells to pass the next element on
demand (the "tail" is the function to use for getting the element after
that, and so forth), and [] to signal "the end".
> My head is starting to spin. Under what circumstances would this stuff
> be very appropriate to use?
For one thing, if you were to have a "final consumer" of some list (or
"stream"), which would take elements one at a time and use them for
something, bluntly assuming that they are right for its task, and you have
some producer that has passed you a lazy list (stream), and you want to
apply some modification to the elements (map, filter, merge, ...) - but
you don't want to read the whole list, do your thing to it, and send the
result to the consumer - you just want to "attach" the modification
without requesting a single element before the consumer decides that it
wants one. Using a lazy list:
transmogrify(L) ->
L1 = lazy_lists:map(fun my_filter/1, L),
consumer(L1).
Now, the "map" will be applied to each element as the consumer requests
them, and not until then. You can in this way attach any sort of
complicated modification to a lazy list (stream) without having to pass
callback hooks to the consumer through a complicated interface. You just
pass your new lazy list.
/Richard
Richard Carlsson (richardc_at_csd.uu.se) (This space intentionally left blank.)
E-mail: Richard.Carlsson_at_csd.uu.se WWW: http://www.csd.uu.se/~richardc/
Post generated using Mail2Forum (http://m2f.sourceforge.net) |
|
|
| Back to top |
|
| Sean.Hinde at one2one.co. |
Posted: Wed Oct 18, 2000 11:13 am |
|
|
|
Guest
|
> Unfortunately it would not directly help Ulf Wiger either as the large
> lists are generated by calls which generate "proper" lists. To use it
> you would have to write new interface functions to generate the lazy
> lists and force people to use the lazy libraries. This would still
> mean you would have to go through code and modify it, as you would
> using th existing "lazy" interface to ETS tables.
mnesia transactions seem to use pretty much this procedure (generate list,
convert to ets table, traverse ets table to generate new list - or something
similar). Obviously the author had some reason to choose this solution.
Maybe there's a better one which doesn't involve all that copying but also
doesn't have the downside which Ulf and the mnesia author are trying to
avoid (different memory allocation? different GC? different handling of fast
growing heaps? other?)
Sean
NOTICE AND DISCLAIMER:
This email (including attachments) is confidential. If you have received
this email in error please notify the sender immediately and delete this
email from your system without copying or disseminating it or placing any
reliance upon its contents. We cannot accept liability for any breaches of
confidence arising through use of email. Any opinions expressed in this
email (including attachments) are those of the author and do not necessarily
reflect our opinions. We will not accept responsibility for any commitments
made by our employees outside the scope of our business. We do not warrant
the accuracy or completeness of such information.
Post generated using Mail2Forum (http://m2f.sourceforge.net) |
|
|
| Back to top |
|
| rv at bluetail.com |
Posted: Wed Oct 18, 2000 11:36 am |
|
|
|
Guest
|
Richard Carlsson <richardc_at_it.uu.se> writes:
>
>On Tue, 17 Oct 2000, Sean Hinde wrote:
>
>> If we already have a large list and want to generate another I guess
>> there would there be no benefit of converting this into a lazy_list
>> first? (would this make a Huge fun?)
>
>Not "Huge" - the function "lazy(L)" takes your normal list and embeds it
>in a small fun (no copying done) which will on demand return the next
>element. The space usage is comparable to a tuple with a small number of
>elements.
Yes, but you would still already have created the original huge list.
Converting it to a lazy list AFTERWARDS will not gain you anything in
space, actually it will cost you as you replace a cons cell with a
tuple of a few extra elements. That added to the extra execution cost
of first making it lazy and then extracting each cons cell makes it all
seem like a big lose.
This means that using lazy lists is really only practical if you use it
consistently from start to finnish with an object. That is why I
suggested not calling them lazy lists but lazy <something else> as they
aren't interchangeable.
Robert
--
Robert Virding Tel: +46 (0)8 545 55 017
Alteon Web Systems Email: rv_at_bluetail.com
S:t Eriksgatan 44 WWW: http://www.bluetail.com/~rv
SE-112 34 Stockholm, SWEDEN
"Folk s |
|
|
| Back to top |
|
| richardc at it.uu.se |
Posted: Wed Oct 18, 2000 12:00 pm |
|
|
|
Guest
|
On Wed, 18 Oct 2000, Robert Virding wrote:
> I think that doing it as you have done making lazy lists as
>
> LazyList :: () -> [] | [term() | LazyList]
>
> makes things much simpler and more consistent.
>
> However you still have the major problem that it is not integrated
> with the rest of the whole system (Erlang, OTP and apps). This
> restricts what you can do with it, as the rest of the system sees this
> as funs not lists.
Yes, it is not a list. Should not even try to be a list. (I just
implemented them using cons cells because I liked the notation, but that
is trivial to change.)
I see all this as a question of interfaces between functions. As I just
wrote in reply to Seans question, a lazy data structure allows you to
attach computations without passing an explicit callback hook. In some
applications and programming styles, this might be precisely what you
want. In others not. I think that functions now operating on normal lists
should *not* be augmented to also handle lazy lists, simply because it
mucks up their otherwise simple and straightforward interface using normal
lists. If one wants lazy data structures, one should have completely
disjoint functions for operating on them. The programmer should be able to
know what might and might not happen.
I am actually tiring of the term "lazy lists", so I'm now switching to
"streams", and I hope that no-one will confuse this with Erlangs I/O
streams (which are a process communication protocol).
> As Sean Hinde points out if you already have the big list then why
> bother?
Only if you want to use an existing list with a function that wants a
stream as input.
> Unfortunately it would not directly help Ulf Wiger either as the large
> lists are generated by calls which generate "proper" lists. To use it
> you would have to write new interface functions to generate the lazy
> lists and force people to use the lazy libraries. This would still
> mean you would have to go through code and modify it, as you would
> using th existing "lazy" interface to ETS tables.
Yes. But I think that the sort of "damage control" that Ulf wanted will in
the end make the code deteriorate into something unreadable, bug-prone and
unpredictable. Better rewrite the code once and for all into something
that can be read and understood, if you decide that you want to use this
sort of technique.
> Most importantly it can't be used in patterns!!!
Well, do you want to? If you want to look at the first N elements of a
stream, you just extract them and then do the matching. There is an
infinite number of possible abstract data types that you cannot do
matching on. Give us O'Keefes abstract patterns, and the whole problem
might go away.
> As it can't be used interchangably with lists, or even list patterns,
> wouldn't it be better to drop the list terminology and instead call
> them something else, lazy sequences? This would help to lessen
> confusion.
I agree. I'll stick to "streams" from now on.
> I wouldn't even construct them with lists, use a tagged tuple instead.
> I know it takes more space but I am only looking a one element at a
> time so it isn't really important. This would help to remove a lot of
> errors where people inadvertantly used "normal" list operations on
> them which could generate very random errors.
Yes, you may well be right. (Me, I don't make errors like that. .
> And no way that I or anyone else would prefix their list procesing
> functions with a special clause!
>
> func(LazyList, ...) when function(LazyList) -> func(LazyList(), ...);
No, as I said, I do absolutely not want to augment code handling normal
lists with extra cases for continuations. That way lies madness!
> In short if you want to implement a "lazy" sequence be extremely
> explicit and not try to make look like lists, which they are not and
> don't behave as.
OK, boss.
> Anyway without rewriting the result of an evaluation you can create an
> aweful amount of extra work. Also you could have fun code like
>
> L1 = eval(LazyList),
> L2 = eval(LazyList),
>
> and L1 /= L2.
Yes, this is the major snag, so to speak. One would like the advantages of
computing things at most once, without the disadvantages of having to
explicitly pass around the "updated stream" to anyone who might want to
read it again from the same point.
As you also said in your "philosophical questions" letter, this laziness
stuff is already handled well by process communication (as e.g., I/O
streams), which also makes it clear who computes what and when. "Lazy
list" streams should only be used if they offer something that makes the
task at hand a lot easier, and you are prepared for the effects of lazy
evaluation.
The advantages of "lazy list" streams over message passing are then 1)
locality (no copying of data between processes) - but this might go away
*if* we get an Erlang implementation with a global heap, and 2) the simple
but powerful interface, allowing you to modify a stream as you like, but
also postponing the computation to another time (and possibly another
process). Actually, I can't say I have a huge use for them myself, but I
like to explore the idea. Real uses may crop up later.
/Richard
Richard Carlsson (richardc_at_csd.uu.se) (This space intentionally left blank.)
E-mail: Richard.Carlsson_at_csd.uu.se WWW: http://www.csd.uu.se/~richardc/
Post generated using Mail2Forum (http://m2f.sourceforge.net) |
|
|
| Back to top |
|
| richardc at it.uu.se |
Posted: Wed Oct 18, 2000 12:13 pm |
|
|
|
Guest
|
On Wed, 18 Oct 2000, Robert Virding wrote:
> Richard Carlsson <richardc_at_it.uu.se> writes:
> >
> >On Tue, 17 Oct 2000, Sean Hinde wrote:
> >
> >> If we already have a large list and want to generate another I guess
> >> there would there be no benefit of converting this into a lazy_list
> >> first? (would this make a Huge fun?)
> >
> >Not "Huge" - the function "lazy(L)" takes your normal list and embeds it
> >in a small fun (no copying done) which will on demand return the next
> >element. The space usage is comparable to a tuple with a small number of
> >elements.
>
> Yes, but you would still already have created the original huge list.
> Converting it to a lazy list AFTERWARDS will not gain you anything in
> space, actually it will cost you as you replace a cons cell with a
> tuple of a few extra elements. That added to the extra execution cost
> of first making it lazy and then extracting each cons cell makes it all
> seem like a big lose.
I'm sorry for being unclear - that was not what I was trying to convey -
only that if you do have a normal list and you *want* to use it as a
stream (for whatever reason) you do not get a big space penalty for it. Of
course, you cannot make an existing huge list vanish by wrapping it in a
closure.
/Richard
Richard Carlsson (richardc_at_csd.uu.se) (This space intentionally left blank.)
E-mail: Richard.Carlsson_at_csd.uu.se WWW: http://www.csd.uu.se/~richardc/
Post generated using Mail2Forum (http://m2f.sourceforge.net) |
|
|
| Back to top |
|
| etxuwig at etxb.ericsson. |
Posted: Wed Oct 18, 2000 12:39 pm |
|
|
|
Guest
|
On Wed, 18 Oct 2000, Richard Carlsson wrote:
>Yes. But I think that the sort of "damage control" that Ulf wanted
>will in the end make the code deteriorate into something unreadable,
>bug-prone and unpredictable. Better rewrite the code once and for
>all into something that can be read and understood, if you decide
>that you want to use this sort of technique.
I wrote the following in a previous post.
"Of course, these rewrites are usually warranted for other reasons,
but timing is always an issue in production projects. If we can, we'd
like to schedule complete rewrites to some other time than during the
system test phase -- which is a fairly common place to find these
problems."
(http://www.erlang.org/ml-archive/erlang-questions/200010/msg00135.html)
In practically all the circumstances where we've come across the poor
memory management behaviour that would make the code a candidate for
this type of reprogramming, we've also recommended quite assertively
that the code be rewritten, usually for a variety of other reasons as
well. Always, we get the same response: "sure we'd like to rewrite the
code, but there simply isn't time."
This is the reality of complex product development projects in a
fiercely competitive environment: you can very seldomly afford a major
rewrite of code, so techniques for partial rewrites become very
interesting.
We *have* performed major rewrites on a number of occasions - even
performed a total overhaul of a major part of the system. In all
cases where we've done this, it's been a success, but it is also a
huge undertaking - even in Erlang.
>No, as I said, I do absolutely not want to augment code handling normal
>lists with extra cases for continuations. That way lies madness!
We must be thinking of totally different problems. For the problems
I'm trying to solve, augmenting the list metaphor seems like the
logical way to go - certainly not madness.
Anyway, after talking to Bj |
|
|
| Back to top |
|
| thomasl at bluetail.com |
Posted: Wed Oct 18, 2000 1:12 pm |
|
|
|
Guest
|
| > Anyway, after talking to Bj |
|
|
| Back to top |
|
| hakan at erix.ericsson.se |
Posted: Wed Oct 18, 2000 2:59 pm |
|
|
|
Guest
|
On Wed, 18 Oct 2000, Sean Hinde wrote:
Sean> > Unfortunately it would not directly help Ulf Wiger either as the large
Sean> > lists are generated by calls which generate "proper" lists. To use it
Sean> > you would have to write new interface functions to generate the lazy
Sean> > lists and force people to use the lazy libraries. This would still
Sean> > mean you would have to go through code and modify it, as you would
Sean> > using th existing "lazy" interface to ETS tables.
Sean>
Sean> mnesia transactions seem to use pretty much this procedure (generate list,
Sean> convert to ets table, traverse ets table to generate new list - or something
Sean> similar). Obviously the author had some reason to choose this solution.
Similar?
Updates in a Mnesia transaction are stored in a temporary ets
table. At commit time, the transaction coordinator iterates over the
transaction storage once. During this iteration several new lists are
generated (one list per storage type and node). These lists (embedded
in records) are then sent to the other involved nodes, possibly logged
to file and finally written to ets/dets depending on the storage
type. Ok, there are lots of copying involved here but I don't think
that this part of Mnesia could be made so much more efficient without
new features in erts.
Sean> Maybe there's a better one which doesn't involve all that copying but also
Sean> doesn't have the downside which Ulf and the mnesia author are trying to
Sean> avoid (different memory allocation? different GC? different handling of fast
Sean> growing heaps? other?)
I don't know if Mnesia would benefit of lazyness, since the entire
application by nature is a large side effect. But there are other,
less sexy, things that it would benefit of.
Mnesia would benefit of hash tables locally allocated on the process
heap. The effect of this would be twofold. Firstly, it would avoid the
copying of data between the process heap and the ets storage, for each
access of the table. Secondly it would make the disposal of the
temporary hash table much cheaper.
Allocating tables on the process heap would also enable experiments
with more adaptive data structures, where plain lists could
transparently be transformed into tables, trees, tuples etc. depending
on (deduced or explicitly stated) access patterns and data sizes.
A memory allocation stategy with separate heaps (garbage collected or
not) for each table would give yet other set of characteristics than
the current implementation with a global table heap.
Some clever multi module flow analysis, would enable a wide range of
optimisations, such as avoidance of external calls, destructive
updates of records, etc. etc. Currently, there is an unfair
performance cost for well structured applications, as the compiler
only operates at one module at the time and many optimizations only
are performed within one function. Modules that not are intended to
be used from other applications should be for free.
/H |
|
|
| Back to top |
|
| thomasl at bluetail.com |
Posted: Wed Oct 18, 2000 4:08 pm |
|
|
|
Guest
|
> Some clever multi module flow analysis, would enable a wide range of
> optimisations, such as avoidance of external calls, destructive
> updates of records, etc. etc. Currently, there is an unfair
> performance cost for well structured applications, as the compiler
> only operates at one module at the time and many optimizations only
> are performed within one function. Modules that not are intended to
> be used from other applications should be for free.
Coming soon ... currently at the research stage, with a clunkily
running prototype. And yes, I am a firm believer in the power of
cross-module optimization.
One solution is to abandon hot code loading, or to modify it so
that some modules must be loaded as a group.
(See also: ftp://ftp.csd.uu.se/pub/papers/reports/0154.ps.gz for
some background.)
OK, since this is a favorite topic of mine, hit 'n' or stay
around for a longish discussion.
Here we go.
I know of two implementations of "module merging" that can also group
modules into a big 'super module': one by Richard Carlsson and one by
me. Module merging is intended to be safe with the current hot code
loading semantics, but you can also (basically) just 'cat' all the
modules to be merged into a single big file and compile that.
The devil is in the details, however. First of all, you have to handle
apply/spawn/spawn_link and similar stuff. Second, exports have to be
accomodated somehow (in the best of worlds, a file should be able to
export several interfaces, but right now it can't). But the main
problem at this time, if we work at the source level, is that of
records:
- We don't want to expand records into tuples, since that carries less
information for the compiler to work with. So we have to keep the
record operations around.
- Two record definitions in different modules can have the same name.
(An example of this is mnesia, where the name 'state' is used for
several different record definitions.)
- This means the files can't be straightforwardly merged since the names
may clash and the compiler can't handle that.
- On the other hand, we can't simply give each record definition a unique
names, since the same record can be used in several places (file.hrl, say,
included here and there) and all those uses must have the same name.
The current solution is this: records are renamed to have a name
that is unique _per_unique_definition_, not per occurrence. This is done by
constructing an MD5 hash of the fields and their initializers, and
appending that to the name. All equivalent record definitions (same name,
same fields) will then get the same name.
Thus, if your record looks like
-record(state, {supervisor, pid_tab}).
You then get a new, unique name based on the MD5 of the definition:
'state 2d7265636f72642873746174652c207b73757065727669736f722c7069645f7461627d'
(The 'state ' prefix is retained just to keep things debuggable -- the
MD5 is sufficient.)
Now, if the same record definition occurs in several places then all those
definitions will have the same renaming.
There are drawback: first, printing the record looks weird (the tag is
'record <md5hash>'); second, the _entire_ system must use this MD5
renaming scheme. Otherwise, some modules will still use the old naming
scheme.
Still, that's not too onerous. And when records are Done Right, this problem
will go away.
Wishlist:
- records to have unique names over the entire system, etc.
* source-to-source transform has to go
- export many interfaces
* would also be nice to have -apply([f/1]), -spawn([g/2]), etc
to make it easier to see what is exported just for those purposes;
this would also get rid of the comment "internal export".
- better support for detecting module names in code (for instance,
gen_tcp stores an atom in a table, which is later used as a module
name ... there's little hope of detecting that and changing the
name into optimized_gen_tcp, say)
- no _undocumented_ magic BIFs in os.erl and similar (+ some way to
detect those programmatically), since renaming os.erl into optimized_os.erl
will fool the runtime system.
Thomas
--
Thomas Lindgren thomasl+junk_at_bluetail.com
Alteon Websystems Sweden http://www.bluetail.com
"The need of a constantly expanding market for its products chases the
bourgeoisie over the entire surface of the globe. It must nestle
everywhere, settle everywhere, establish connections everywhere."
-- Communist Manifesto
Post generated using Mail2Forum (http://m2f.sourceforge.net) |
|
|
| Back to top |
|
| wuji |
Posted: Mon Sep 17, 2012 1:40 am |
|
|
|
User
Joined: 10 Aug 2012
Posts: 654
|
the expense of our villain, Rex, and not at the the cheap authentic jordans the expense of those suffering from the disease."The joke, "From
man to another, I hope you get Lou Gehrig's disease," disease," cheap replica designer *beep* disease," shocked movie-going patients and advocates, who say it crossed
line."I didn't expect to go to a movie and sit sit cheap authentic jordans sit with an audience laughing at the expense of people
ALS," said Randy Pipkin, who was diagnosed in 2005. "I "I [h4]replica designer bags for sale[/h4] "I think the message this film sends out is a
slap in the face to people dying from this horrific horrific [h4]authentic jordans[/h4] horrific disease."Lou Gehrig's disease, also known as ALS, progressively robs
of their ability to move, speak, eat and breathe. There There authentic jordans There is currently no cure.The punch line prompted an online |
|
|
| Back to top |
|
|
|
All times are GMT
|
|
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
|
|
|