On 08.12.2010 23:45, Jonathan M Davis wrote:
On Wednesday, December 08, 2010 14:14:57 Simon Buerger wrote:
For Every lib its a design descision if containers should be value- or
reference-types. In C++ STL they are value-types (i.e. the
copy-constructor does a real copy), while in tango and phobos the
descision was to go for reference-types afaik, but I would like to be
able to write value-types too, which isn't possible (in a really good
way) currently. Following points would need some love (by-value
containers are probably not the only area, where these could be useful)
It's extremely rare in my experience that it makes any sense to copy a container
on a regular basis. Having an easy means of creating a deep copy of a container
or copying the elements from one container to another efficiently would be good,
but having containers be value types is almost always a bad idea. It's just not
a typical need to need to copy containers - certainly not enough to have them be
copied just because you passed them to a function or returned them from one. I
think that reference types for containers is very much the correct decision.
There should be good ways to copy containers, but copying shouldn't be the
default for much of anything in the way of containers.
From a pragmatic viewpoint you are right, copying containers is rare.
But on the other hand, classes imply a kind of identity, so that a set
is a different obejct then an other object with the very same
elements. That feels wrong from an aesthetical or mathematical
viewpoint. Furthermore, if you have for example a vector of vectors,
vector!int row = [1,2,3];
auto vec = Vector!(Vector!int)(5, row);
then vec should be 5 rows, and not 5 times the same row.
(1) Allow default-constructors for structs
I don't see a reason, why "this(int foo)" is allowed, but "this()" is
not. There might be some useful non-trivial init to do for complex
structs.
It has to do with the init property. It has to be known at compile-time for all
types. For classes, that's easy because it's null, but for structs, that's what
all of their member variables are directly initialized to. If you add a default
constructor, then it would have to be to whatever that constructed them to,
which would shift it from compile time to runtime. It should be possible to have
default constructors which are definitely limited in a number of ways (like
having to be nothrow and possibly pure), but that hasn't been sorted out, and
even if it is, plenty of cases where people want default constructors still
wouldn't likely work. It just doesn't work to have default constructors which
can run completely arbitrary code. You could get exceptions thrown in weird
places and a variety of other problems which we can't have in situations where
init is used. Hopefully, we'll get limited default constructors at some point,
but it hasn't happened yet (and probably won't without a good proposal that
deals with all of the potentiall issues), and regardless, it will never be as
flexible as what C++ does. It's primarily a side effect of insisting that all
variables be default initialized if they're not directly initialized.
I partially see your point, the constructor would be called in places
the programmer didnt expect, but actually, what's the problem with an
exception? They can always happen anyway (at least outOfMemory)
(2) const parameters by reference
If a parameter to a function is read-only, the right notion depends on
the type of that parameter. I.e. "in" for simple stuff like ints, and
"ref const" for big structures. Using "in" for big data implies a
whole copy, even though it's constant, and using "ref const" for
simple types is a useless indirection. This is a problem for generic
code, when the type is templated, because there is now way to switch
between "in" and "ref const" with compile-time-reflection.
Solution one: make "ref" a real type-constructor, so you could do the
following (this is possible in C++):
static if(is(T == struct))
alias ref const T const_type;
else
alias const scope T const_type;
// "const scope" is (currently) equivalent to "in"
void foo(const_type x)
Solution two: let "in" decide wheather to pass by reference or value,
depending on the type. Probably the better solution cause the
programmer dont need to care of the descision himself anymore.
I think that auto ref is supposed to deal with some of this, but it's buggy at
the moment, and I'm not sure exactly what it's supposed to do. There was some
discussion on this one in a recent thread.
letting "in" decide would be cleaner IMO, but anyway good to hear that
problem is recognized. Will look for the other thread.
(3) make foreach parameters constant
when you do "foreach(x;a)" the x value gets copied in each iteration,
once again, that matters for big types especially when you have a
copy-constructor. Current work-around is prepending "ref": nothing
gets copied, but the compiler wont know it is meant to be read-only.
Solution: either allow "ref const" or "in" in foreach. Or you could
even make x default to constant if not stated as "ref" explicitly.
Last alternative seems logical to me, but it may break existing code.
I'd hate to see foreach variables be const by default. That would be overly
limiting and would definitely break a lot of code. Making ref const work
properly
would be good (I think that it works in at least some cases) for structs that
you don't want to be copied but wouldn't be all that useful otherwise. Nothing
in D is const by default, and I think that making anything const by default
would clash with the rest of the language. Particularly since then how would you
make it mutable? No, it should be possible to have const refs to structs for
foreach variables, but it shouldn't be the default. The language as a whole just
does not support that.
You are right that default-const would be contrary to the rest of the
language, but when I think longer about this... the same default-const
should apply for all function parameter. They should be input, output
or inout. But the "mutable copy of the original" which is common in
C/C++/D/everything-alike, is actually pretty weird. (modifying
non-output parameters inside a function is considered bad style even
in C++ and Java). But well, that would be really a step too big for
D2... maybe I'll suggest it for D3 some day *g*
Krox