On Monday, 27 August 2012 at 20:22:47 UTC, Era Scarecrow wrote:
On Monday, 27 August 2012 at 14:53:57 UTC, F i L wrote:
in C#, you use 'new Type()' for both classes and structs, and
it works fine. In fact, it has some benefit with generic
programming. Plus, it's impossible to completely get away from
having to understand the type, even in C++/D today, because we
can always make factory functions:
I'm sure in C# that all structs and classes are heap allocated
(It takes after C++ very likely) that's the simplest way to do
it. You can do that in C++ as well, but other than having to
declare it a pointer first. In C++ they made structs 'classes
that are public by default' by it's definition I believe.
Considering how C++ is set up that makes perfect sense.
FooType newFoo() {
return new FooType( ... );
}
void main() {
auto f = newFoo(); // struct or class?
}
By looking at newFoo I'd say a class; But if like in C# I'm
sure you can't tell the difference (But C++ with the pointer
you can). And for factory functions I'd put them inside the
struct/class, unless I had a compelling reason not to.
class Record {
static Record newRecord(string options) {
Record rec = new Record();
// stuff building the complex record
return rec;
}
}
However, I do agree it would be nice having some kind of
distinction between stack/heap allocation and copy/ref
semantics. Because constructor names are completely arbitrary
in my proposal, I think you could easily just choose a
different name (say, 'new' vs 'set').
struct Foo {
this set() { ... }
}
class Bar {
this new() { ... }
}
void main() {
auto f = Foo.set( ... ); // stack/copy
auto b = Bar.new( ... ); // heap/ref
}
Again, this is just an arbitrary distinction and wouldn't be
enforced, so third-party libs could choose something
completely different... but then, they always could (like
above) so it's benefit is debatable.
I've had ideas before about having two different '=' operators
for assignment and copy, but honestly, I think just looking up
and understanding the types you're working with might be the
best solution. A task much easier with proper IDE tooltips and
the like.
Would new still be a key word?
No. You could just as easily name your constructor
'freshCopyYo()'. In fact, you often want multiple constructor
names for different constructing procedures, like I explain in
my original post. For instance if you're loading from a file,
having a 'load()' constructor makes more sense than 'new()'.
When converting from some other type, have a 'from()'
constructor. The name implies the action, but they all
"construct" the type:
class Text
{
this new() // blank
this new(string s) // basic constructor
this from(int i) // convert from int
this from(float f) // convert from float
this load(string filename) // load from file
}
All of these are constructors, because they're return type is
'this'. They all implicitly allocate an object and have a
'this' reference. However, their names and implementations are
completely arbitrary, which is a good thing because we need
and use these arbitrary constructors all the time today, we
just have to do it in a completely inconsistent way (static
factory functions).
And a postblits would end up being...? The extra 'this' makes
it look like an obvious typo or a minor headache.
this this(this){} //postblitz?
Honestly I kinda like it how it is now. It's fairly clear and
concise. Only if you start getting creative can it begin to get
confusing; Then again in any language someone who decided to
make it confusing would make it confusing regardless.
--
enum JOJOJO = 100;
class Jo {
int jojo1;
Jo jo(Jo JOO) {
int jojo = 10;
int joJo = JOJOJO;
Jo JO = new Jo();
// and so on and so forth.
}
}
// Make a new Jo!
Jo newJo(){
return Jo.jo(null);
}
Jo something = newJo(); //now is this a class or a struct :P
Right back at you.
--
Seriously... Actually if you get it confusing enough you could
submit it to The International Obfuscated C Code Contest.
http://www.ioccc.org/
I would say if 'new' is part of the function name, it's
returns a class. If it's referenced (or a pointer), it could be
either. But not having a universal constructor would make
creating an array with defaults impossible without
declaring/hinting which one to use. It's one reason structs
have to have good contents for all it's data members at
compile-time, so you could create an array with defaulted
information that works.
If we take your approach and suggestion, which one should the
compile assume?
Something globalSomething;
class Something {
this defaultConstructor();
this duplicate(); //or clone
this copyGlobalSomething();
this constructorWithDefault(int x = 100);
}
By signature alone... Which one? They are all legal, they are
uniquely named, and they are all equal candidates. Order of
functions are irrelevant.
FiL's scheme looks backwards to me. One of the main drawbacks of
factories is the fact that they have non-standard names.
Given a class Foo, How would I know to call a factory newFoo?
More generally, given template:
T create(T)(...) {
// .. how do I create an instance of T?
}
The correct scheme would be (as implemented in a few languages):
1. The constructor needs to be split into two separate stages -
creation/allocation and initialization.
2. Creation is done via a regular (virtual) method with a
standardized name ("new").
3. Initialization is done via a non-virtual function, e.g what we
call a c-tor.
The compiler/programmer know that creation is done via "new" and
that new must call a c-tor. If there is no c-tor defined one will
be generated for you by the compiler (same as it is now). In
addition, a default creation method will also be generated by the
compiler if it is not defined by the programmer. All it will do
is just forward to the init function (aka c-tor).
The main difference is that now the programmer has control over
the first stage (creation) and it is no longer implicitly
hardwired.
Example use case:
class LimitedAccount : Account {
// "regular allocation" - on GC heap
private Account new(Person p) {
return GC.allocate!LimitedAccount(P);
}
// init
this(Person p) {...}
...more code...
}
class Bank {
Account new(Person p, AccountType t) {
switch(t) {
case AccountType.LIMITED: return LimitedAccount.new(p);
... more cases...
}
}
}
// usage:
Account acc = Bank.new(PoorShmoe, AccountType.LIMITED);