Am 03.05.2014 14:28, schrieb monnoroch:
I've been reading all the topics with those "radical" ideas about the GC
and dtors and, honestly, i'd rather call them "insane". After all the
reading and thinking, i came to conclusion, that what Andrey suggests is
to call dtors only on stack-allocated structs. That also implies, that
one can't put those in containers and gc-allocated objects.
Since all of them: containers, structs, classes -- are all first-class
objects they must be all nicely combined in code, without any
unintuitive stuff.
I mean, really, let us look at c++ strings. There are const char*,
std::sting, QString, Poco::String, icu::UnicodeString, and every big
project uses it's own strings implementation that can't be used together
in a sane way. That is what great in D: you just threw the idea of
library-implemented strings away and made it not only "standard", but
special and that what makes it intuitive and simple (unless you interact
with c++). Never seen any non-standart strings for D.
The point is, that every library and every coder add to the project
incompatible and difficult to use together stuff, so every programmer
already has to think of all the problems with other's people code, and
you just can't add same shit to the language. All elements must nicely
interact together and otherwise it's a total disaster.
Back to the dtors: i understand, that all the stuff you propose could
make GC faster, simpler, and cooler, but it sounds insane to anyone, who
uses the language, not develops it, that if you create struct object,
dror will be called, but if you place the copy in a container, it wont.
It's just unanderstandable from user's point of view.

Now, for the solution.

First, we can just fix this shit with arrays of structs and that's it.
That still lives us with false pointers problem: not everything gets
collected. That's no good. So, i propose to think of actually separating
gc-memory management (via GC) and other resources management: via some
new (or maybe old) mechanism.

Let me start with listing of existing solutions:

1) C.
That is the simplest way: fully-manual resource management.
It's obvious, we can't do that in D, because it's supposed to be simpler
for coding, than C.

2) Go.
Actually, this one is not that different: it uses GC for memory only,
and manual management for all the rest (with help of defer operator). We
can't do it either, for the same reasons.

3) C++.
This one is semi-automatic (talking about user code, not some allocator
libraries): you choose the scheme (refcounting, unique reference) and
then it'll do the rest.

4) Rust.
I'm not a pro here, but as i understand, it uses C++ way, and adds
gc-collected pointers, but not sure, so help me here.

5) Python.
GC-only, except one clever case: with statement calls close() method.


Please, if there are any pros in other platforms, add your knowledge to
this list, i would very much love to learn (same, if a made any mistakes).


Now, for D: obviously D has GC-managed heap. First, we should, like in
Go, leave only managing gc-memory to the GC -- this is just rephrasing
Andreys proposal.
The simplest way o manage all other resources would be manual, Go-way:

A a = A();
scope(exit)
     a.~A();

But it's to annoying, to that all the time, so we really want dtors to
save us lost of typing and debugging, but they can't be called all the
time, because we can put stuff in GC-collected objects.

What i propose, is to include new concept in D: scoped objects.
Any object (no matter is it a class or struct instance) can be either
scoped or not.
Dtors for scoped objects are called when out of scope, dtors for
non-scoped objects are not called at all.

It is actually as simple as rewrite code

A a = A();

as

A a = A();
scope(exit)
     a.~A();

For all a's, which are scoped objects.

For me, it is both a simple concept and good rationalization for
difficult dror-gets-called-or-not rules.

That leaves only to determine, what objects are scoped. Well, that is
obviously stack-allocated structs, gc-allocated scope classes and
gc-allocated structs in scope classes.

But that is just my idea. This post has so many words, because it's very
important, that D devs make good decision on that deep problem, and the
key to such decision is information and discussion.

UPD:
Also, about arrays and slices: if we could easily pass them around as
cost ref-s, just like in C++, then we could make them value-types and
they wouldn't require any ref counting. I would suggest, make all "in"
function arguments const refs.


C# and Java also have scoped blocks (using, try-with-resources), similar to Python for resource management.

Ada has controlled types for it, where Finalize() plays the role of C++ destructor.

Additionally all languages with lambda support do offer resource management via implicit control structures. Mostly visible in Lisp and ML dialects. Great in the languages that allow the lambda as last parameter to appear outside of the call.

For example, assume a doTransaction (connection, lambda) function, then

doTransaction(connection) {
  db.insert (my data)
}


The approach taken is similar to implementing C++ templates for generic RAII use cases.

--
Paulo

Reply via email to