www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - 'new' class method

reply bearophile <bearophileHUGS lycos.com> writes:
In Python to create new instances of a class you use the normal function call
syntax, this allows you to use them as factory functions:

class C:
    def __init__(self, id):
        self.id = id
    def __repr__(self):
        return "<%s>" % self.id
seq = map(C, [1, -5, 10, 3])

That creates an array (list) of four objects.

In D with a map() you can do something similar:

import d.all;
import std.string: format;

class C {
    int id;
    this(int id) {
        this.id = id;
    }
    string toString() {
        return format("<%s>", this.id);
    }
    static C opCall(int id) {
        return new C(id);
    }
}
void main() {
    auto seq = map((int id){return new C(id);}, [1, -5, 10, 3]);
    putr(seq);
}

You can use the opCall method in a more direct way:

    auto seq2 = map(&(C.opCall), [1, -5, 10, 3]);
    putr(seq2);

But probably even better is to replace the current new syntax with a class
method that creates the instances (I think the Ruby language has such syntax):

    auto seq3 = map(C.new, [1, -5, 10, 3]);
    putr(seq3);

With that normal code like:

new Foo(10, 20)

becomes:

Foo.new(10, 20)

Not a big change but allows a more functional style of coding.

Bye,
bearophile
Oct 23 2008
next sibling parent reply "Bill Baxter" <wbaxter gmail.com> writes:
On Thu, Oct 23, 2008 at 6:32 PM, bearophile <bearophileHUGS lycos.com> wrote:
 In Python to create new instances of a class you use the normal function call
syntax, this allows you to use them as factory functions:

 class C:
    def __init__(self, id):
        self.id = id
    def __repr__(self):
        return "<%s>" % self.id
 seq = map(C, [1, -5, 10, 3])

 That creates an array (list) of four objects.

 In D with a map() you can do something similar:

 import d.all;
 import std.string: format;

 class C {
    int id;
    this(int id) {
        this.id = id;
    }
    string toString() {
        return format("<%s>", this.id);
    }
    static C opCall(int id) {
        return new C(id);
    }
 }
 void main() {
    auto seq = map((int id){return new C(id);}, [1, -5, 10, 3]);
    putr(seq);
 }

 You can use the opCall method in a more direct way:

    auto seq2 = map(&(C.opCall), [1, -5, 10, 3]);
    putr(seq2);

 But probably even better is to replace the current new syntax with a class
method that creates the instances (I think the Ruby language has such syntax):

    auto seq3 = map(C.new, [1, -5, 10, 3]);
    putr(seq3);

That would be map(&(C.new), [1,-5,10,3]); wouldn't it?
 With that normal code like:

 new Foo(10, 20)

 becomes:

 Foo.new(10, 20)

 Not a big change but allows a more functional style of coding.

I like it. Can we get rid of delete's specialness too? Maybe just delete(anInstance); Make it like a normal function too. Enabling things like map(&delete, [obj1, obj2, obj3]) --bb
Oct 23 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Bill Baxter wrote:
 On Thu, Oct 23, 2008 at 6:32 PM, bearophile
 <bearophileHUGS lycos.com> wrote:
 In Python to create new instances of a class you use the normal
 function call syntax, this allows you to use them as factory
 functions:
 
 class C: def __init__(self, id): self.id = id def __repr__(self): 
 return "<%s>" % self.id seq = map(C, [1, -5, 10, 3])
 
 That creates an array (list) of four objects.
 
 In D with a map() you can do something similar:
 
 import d.all; import std.string: format;
 
 class C { int id; this(int id) { this.id = id; } string toString()
 { return format("<%s>", this.id); } static C opCall(int id) { 
 return new C(id); } } void main() { auto seq = map((int id){return
 new C(id);}, [1, -5, 10, 3]); putr(seq); }
 
 You can use the opCall method in a more direct way:
 
 auto seq2 = map(&(C.opCall), [1, -5, 10, 3]); putr(seq2);
 
 But probably even better is to replace the current new syntax with
 a class method that creates the instances (I think the Ruby
 language has such syntax):
 
 auto seq3 = map(C.new, [1, -5, 10, 3]); putr(seq3);

That would be map(&(C.new), [1,-5,10,3]); wouldn't it?
 With that normal code like:
 
 new Foo(10, 20)
 
 becomes:
 
 Foo.new(10, 20)
 
 Not a big change but allows a more functional style of coding.

I like it. Can we get rid of delete's specialness too? Maybe just delete(anInstance); Make it like a normal function too. Enabling things like map(&delete, [obj1, obj2, obj3]) --bb

Andrei already mentioned that he wants to get read of new/delete IIRC. I like Ruby's way of making it regular methods. something like: obj.new(..) and obj.delete() but, how do you specify stack vs heap allocation? something like: auto a = new S(); //on heap auto b = S(); //on stack also, what about placement new?
Oct 23 2008
next sibling parent reply dsimcha <dsimcha yahoo.com> writes:
== Quote from Yigal Chripun (yigal100 gmail.com)'s article
 Bill Baxter wrote:
 On Thu, Oct 23, 2008 at 6:32 PM, bearophile
 <bearophileHUGS lycos.com> wrote:
 In Python to create new instances of a class you use the normal
 function call syntax, this allows you to use them as factory
 functions:

 class C: def __init__(self, id): self.id = id def __repr__(self):
 return "<%s>" % self.id seq = map(C, [1, -5, 10, 3])

 That creates an array (list) of four objects.

 In D with a map() you can do something similar:

 import d.all; import std.string: format;

 class C { int id; this(int id) { this.id = id; } string toString()
 { return format("<%s>", this.id); } static C opCall(int id) {
 return new C(id); } } void main() { auto seq = map((int id){return
 new C(id);}, [1, -5, 10, 3]); putr(seq); }

 You can use the opCall method in a more direct way:

 auto seq2 = map(&(C.opCall), [1, -5, 10, 3]); putr(seq2);

 But probably even better is to replace the current new syntax with
 a class method that creates the instances (I think the Ruby
 language has such syntax):

 auto seq3 = map(C.new, [1, -5, 10, 3]); putr(seq3);

That would be map(&(C.new), [1,-5,10,3]); wouldn't it?
 With that normal code like:

 new Foo(10, 20)

 becomes:

 Foo.new(10, 20)

 Not a big change but allows a more functional style of coding.

I like it. Can we get rid of delete's specialness too? Maybe just delete(anInstance); Make it like a normal function too. Enabling things like map(&delete, [obj1, obj2, obj3]) --bb

I like Ruby's way of making it regular methods. something like: obj.new(..) and obj.delete() but, how do you specify stack vs heap allocation? something like: auto a = new S(); //on heap auto b = S(); //on stack also, what about placement new?

One thing I would definitely like to see is for struct and class syntax to be unified so that if you change a struct to a class or vice versa, this doesn't require changes all over your code. Then again, the reference vs. value semantic issue may bite you anyhow.
Oct 23 2008
next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Bill Baxter" wrote
 To me this makes a lot of sense:

 auto a = SomeStruct();  // a is a struct on the stack
 auto b = SomeClass();  // b is a class on the heap

 It kills static opCall for classes, but who cares.  I'm often tempted
 to write static opCalls for classes like this anyway:
    static SomeClass opCall() {  return new SomeClass(); }

 Just to get the uniformity of construction syntax.

It also cleans up this ugliness: auto data = (new FileInput(filename)).readAll(); Which then becomes: auto data = FileInput(filename).readAll(); -Steve
Oct 23 2008
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
Bill Baxter:
 How about this as a start:
 
 // Default cases
 auto a = SomeStruct(arg);  // struct on the stack
 auto a = SomeClass(arg);  // class on the heap
 
 // Anti-default cases
 auto a = SomeStruct.new(arg);  // struct on the heap
 scope a = SomeClass(arg);  // class on the stack
 
 // Placement cases
 ...

I agree that the placement new syntax can be less nice. But for the struct/class cases I'd like something more symmetric :-) A first silly possibility: auto a = SomeStruct(arg); // stack auto a = SomeStruct.new(arg); // heap auto a = SomeStruct.pnew(placement_arg)(arg); // placement A possible alternative: auto a = SomeStruct.pnew(placement_arg).new(arg); // placement auto a = SomeClass(arg); // stack auto a = SomeClass.new(arg); // heap auto a = SomeClass.pnew(placement_arg)(arg); // placement A possible alternative: auto a = SomeClass.pnew(placement_arg).new(arg); // placement Bye, bearophile
Oct 23 2008
prev sibling next sibling parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Fri, 24 Oct 2008 05:31:44 +0900,
Bill Baxter wrote:
 But, ok, this isn't Python so we have to have a story for placement new.
 I believe use of placement new is pretty rare.  (I use it rarely,
 anyway. :-) )   If so it should be ok if the syntax is a little
 clunky.

Maybe do it with a library function? auto c = core.intrinsic.construct!(C)(addr, ctor_arg0, ctor_arg1);
Oct 23 2008
parent reply KennyTM~ <kennytm gmail.com> writes:
Bill Baxter wrote:
 On Fri, Oct 24, 2008 at 6:28 AM, Sergey Gromov <snake.scaly gmail.com> wrote:
 Fri, 24 Oct 2008 05:31:44 +0900,
 Bill Baxter wrote:
 But, ok, this isn't Python so we have to have a story for placement new.
 I believe use of placement new is pretty rare.  (I use it rarely,
 anyway. :-) )   If so it should be ok if the syntax is a little
 clunky.

auto c = core.intrinsic.construct!(C)(addr, ctor_arg0, ctor_arg1);

The current placement new in D can take an arbitrary list of args. So you have two lists -- new_args and ctor_args. --bb

Can a library function return a constructor? Like this: auto c = core.intrinsic.construct!(C)(addr, new_args...)(ctor_args ...); But that's similar to C.new(addr, new_args...)(ctor_args...);
Oct 23 2008
parent Sergey Gromov <snake.scaly gmail.com> writes:
Fri, 24 Oct 2008 14:32:23 +0800,
KennyTM~ wrote:
 Bill Baxter wrote:
 On Fri, Oct 24, 2008 at 6:28 AM, Sergey Gromov <snake.scaly gmail.com> wrote:
 Fri, 24 Oct 2008 05:31:44 +0900,
 Bill Baxter wrote:
 But, ok, this isn't Python so we have to have a story for placement new.
 I believe use of placement new is pretty rare.  (I use it rarely,
 anyway. :-) )   If so it should be ok if the syntax is a little
 clunky.

auto c = core.intrinsic.construct!(C)(addr, ctor_arg0, ctor_arg1);

The current placement new in D can take an arbitrary list of args. So you have two lists -- new_args and ctor_args. --bb

Can a library function return a constructor? Like this: auto c = core.intrinsic.construct!(C)(addr, new_args...)(ctor_args ...); But that's similar to C.new(addr, new_args...)(ctor_args...);

It shouldn't be easy to call a constructor directly--you don't want to do it by accident. Current new semantics automates and hides a lot of stuff under the hood: * calculates the required size * calls an allocator of the class to be constructed, choosing the allocator according to overloading rules * calls a constructor for the same class, choosing the constructor according to overloading rules * converts the results into a class reference In theory, if you drop part of this automation, it's sufficient to have a built-in function that calls a constructor on an arbitrary memory and returns a reference to the constructed object.
Oct 24 2008
prev sibling parent reply Benji Smith <dlanguage benjismith.net> writes:
Bill Baxter wrote:
 How about this as a start:
 
 // Default cases
 auto a = SomeStruct(arg);  // struct on the stack
 auto a = SomeClass(arg);  // class on the heap
 
 // Anti-default cases
 auto a = SomeStruct.new(arg);  // struct on the heap
 scope a = SomeClass(arg);  // class on the stack
 
 // Placement cases
 auto a = SomeStruct.new[placement_arg](arg); // placement struct
 auto a = SomeClass.new[placement_arg](arg); // placement class

I don't particularly care about getting rid of the "new" keyword. In fact, I especially like the part about getting rid of this little ugly: // uuuuuugly auto x = (new MyClass()).whatever(); // holy crap. so much better. auto x = MyClass().whatever(); But I think it's weird that people want to retain "new" to indicate heap construction and use the word "scope" to indicate stack construction. Except for structs, which would have different rules. Ugh. Why not just introduce "heap" and "stack" keywords, and say it directly? stack auto x = MyClass(); heap auto y = MyStruct(); ...or, since "heap" and "stack" are good user keywords, maybe introduce an "on" keyword. Maybe something like this: auto x = on(stack) MyClass(); auto y = on(heap) MyStruct(); To me, there's nothing about the "new" keyword that implies heap construction. "Scope" is a little bit better, because I know the memory will be cleaned up at the end of the scope, but that doesn't *necessarily* mean stack allocation, per se. --benji
Oct 23 2008
next sibling parent reply Benji Smith <dlanguage benjismith.net> writes:
Bill Baxter wrote:
 On Fri, Oct 24, 2008 at 3:01 PM, Benji Smith <dlanguage benjismith.net> wrote:
 Why not just introduce "heap" and "stack" keywords, and say it directly?

Did you see my subsequent proposal? I'm actually quite fond of it. But no one has responded to it. :-( [Bill cry's himself a silent tear.]

You mean the one where you suggested this? auto a = Class(); // heap (default) auto a = Struct(); // stack (default) auto a = Class.new(stack)(); // stack auto a = Struct.new(stack)(); // stack auto a = Class.new(heap)(); // heap auto a = Struct.new(heap)(); // heap void *addr; auto a = Class.new(addr)(); // placement auto a = Struct.new(addr)(); // placement Yeah! I noticed it. And I like it. In fact, I thought to myself "that's almost the same as my idea". Generally, I'm not a fan of the consecutive sequence of parentheses (especially that templates already make for double parens; I can't imagine I'd want triple parens for a templatized consttructor call). For non-template chained calls, I'm not quite sure which I prefer: auto a = Foo.new(heap)(Bar.new(stack)()); auto a = on(heap) Foo(on(stack) Bar());
 New is already a keyword meaning allocation/construction in D, Java,
 C++ and maybe C#?  So anyone familiar with those will know right away
 that "new" probably has something to do with construction.  So the
 question is why wouldn't you use "new"?

Well, in Java and C#, objects can *only* be allocated on the heap, and structs can *only* be allocated on the stack (unless they're members of an object, or if they're in an array). To me, "new" is a great word for "allocate, and then invoke the constructor", but it doesn't have anything to do with *HEAP* allocation, per se. And then I thought to myself "for chrissakes, if you want the heap or the stack, why not ask for it by name?" --benji
Oct 24 2008
parent reply KennyTM~ <kennytm gmail.com> writes:
Benji Smith wrote:
 Bill Baxter wrote:
 On Fri, Oct 24, 2008 at 3:01 PM, Benji Smith 
 <dlanguage benjismith.net> wrote:
 Why not just introduce "heap" and "stack" keywords, and say it directly?

Did you see my subsequent proposal? I'm actually quite fond of it. But no one has responded to it. :-( [Bill cry's himself a silent tear.]

You mean the one where you suggested this? auto a = Class(); // heap (default) auto a = Struct(); // stack (default) auto a = Class.new(stack)(); // stack auto a = Struct.new(stack)(); // stack auto a = Class.new(heap)(); // heap auto a = Struct.new(heap)(); // heap void *addr; auto a = Class.new(addr)(); // placement auto a = Struct.new(addr)(); // placement

I strongly *oppose* using stack and heap as keywords because there are also (partly irrelevant) data structures called stack and heap, even though according to the guideline these class/structs should be named Stack and Heap. And I think we can include the meaning that “scope” attribute always allocate on stack (Currently 4 conditions need to be met for so), but I'm afraid this may break something (otherwise the other 3 conditions won't be there?). Maybe some experts can comment on this? (Ref: http://www.digitalmars.com/d/2.0/memory.html#stackclass) (Oh, BTW, please fix bug #1521 for clarification.) And I suggest keeping the “new” syntax for heap ("return a pointer") allocation. So, auto c = Class(...); // default on heap auto s = Struct(...); // default on stack (compatible with current syntax) scope c = Class(...); // on stack scope s = Struct(...); // on stack (compatible with current syntax) auto c = new Class(...); // on heap (compatible with current syntax) auto s = new Struct(...); // on heap (compatible with current syntax) auto c = new(...) Class(...); // placement (compatible with current syntax) auto s = new(...) Struct(...); // placement (compatible with current syntax?) /* if you want symmetry, do it like this: new c = Class(...); new(...) c = Class(...); kinda strange for me. */ These will cause minimum change while eliminating the “new” for most cases. This won't free the “new” keyword though.
 Yeah! I noticed it. And I like it. In fact, I thought to myself "that's 
 almost the same as my idea". Generally, I'm not a fan of the consecutive 
 sequence of parentheses (especially that templates already make for 
 double parens; I can't imagine I'd want triple parens for a templatized 
 consttructor call).
 
 For non-template chained calls, I'm not quite sure which I prefer:
 
   auto a = Foo.new(heap)(Bar.new(stack)());
 
   auto a = on(heap) Foo(on(stack) Bar());
 
 New is already a keyword meaning allocation/construction in D, Java,
 C++ and maybe C#?  So anyone familiar with those will know right away
 that "new" probably has something to do with construction.  So the
 question is why wouldn't you use "new"?

Well, in Java and C#, objects can *only* be allocated on the heap, and structs can *only* be allocated on the stack (unless they're members of an object, or if they're in an array). To me, "new" is a great word for "allocate, and then invoke the constructor", but it doesn't have anything to do with *HEAP* allocation, per se. And then I thought to myself "for chrissakes, if you want the heap or the stack, why not ask for it by name?" --benji

Oct 24 2008
next sibling parent Benji Smith <dlanguage benjismith.net> writes:
Bill Baxter wrote:
 auto c = Class(...);   // default on heap
 auto s = Struct(...);  // default on stack (compatible with current syntax)

 scope c = Class(...);  // on stack
 scope s = Struct(...); // on stack (compatible with current syntax)

The main thing I don't like about this syntax is that it makes it impossible to create a class temporary on the stack in an expressoin. It means you always have to declare it and give it a name if you want to avoid the heap. It's just not orthogonal in that way. As in it entangles construction with declaration, when those two are usually separate concerns.

Agree with you 1000%. This is one of my minor annoyances with D. Static arrays and structs must be constructed in a statement, rather than in an expression like everything else. MyStruct s; // why can't a MyStruct be created in an expression? float[3] f; // same deal here! what gives? I'd very much like to see a little more unification in construction semantics, among classes, structs, and all the different types of arrays. For one thing, if construction semantics was more unified, it'd be much more straightforward to "upgrade" certain bits of code if/when you decide to switch from a struct implementation to a class (or vice versa). It'd probably also be simpler to write certain kinds of templates (though no examples spring to mind right now). Anyhow, If this business of getting rid of "new" is a step in that direction, I'm all for it.
 /* if you want symmetry, do it like this:

 new c = Class(...);
 new(...) c = Class(...);
 kinda strange for me. */

Yeh, me too. I definitely don't want any more of this sillyness of mixing up construction with declaration.

Ditto.
 Another possibility might be to introduce some syntax for heap vs
 stack and only use () for placement new -- like this:
 
      Class.*new(construct_args);   // stack (like *foo is dereferenced
 foo pointer)
      Struct.*new(construct_args);  // stack
 
      Class.new(construct_args); // heap
      Struct.new(construct_args); // heap
 
      Class.new (new_args)(construct_args);  // placement
      Struct.new (new_args)(construct_args); // placement

Hmmmmmm. Nope. I'll know it when I see it, but this is definitely not it :)
 For non-template chained calls, I'm not quite sure which I prefer:

  auto a = Foo.new(heap)(Bar.new(stack)());

  auto a = on(heap) Foo(on(stack) Bar());


I think Andrei is gunning to get rid of the dangling prefix words in the grammar. That's why I put .new at the end. I think he would also be happy with a regular function-like syntax, too, but what I've seen of that looks ugly. When you do 'new' there always a class or struct involved, so why not make it a pseudo property of every class and struct, just like .init or .tupleof?

Maybe... It still doesn't seem quite right, though. Really, it's the GC that's doing the allocating, so the "new" function ought to belong to the GC object, and the constructor could be a delegate passed to the GC... auto x = GC.new(&Constructor); I admit, I don't really like that either :o( But at least it makes it a little clearer what's going on than the Foo.new()() syntax. Back to the drawing board!!! --benji
Oct 24 2008
prev sibling parent reply KennyTM~ <kennytm gmail.com> writes:
Bill Baxter wrote:
 On Sat, Oct 25, 2008 at 2:04 AM, KennyTM~ <kennytm gmail.com> wrote:
 
  auto a = Class();  // heap   (default)
  auto a = Struct(); // stack   (default)

  auto a = Class.new(stack)(); // stack
  auto a = Struct.new(stack)(); // stack

  auto a = Class.new(heap)(); // heap
  auto a = Struct.new(heap)(); // heap

   void *addr;
   auto a = Class.new(addr)(); // placement
   auto a = Struct.new(addr)(); // placement

(partly irrelevant) data structures called stack and heap, even though according to the guideline these class/structs should be named Stack and Heap.

Right, that's why both Benji and I proposed making them *context* keywords. Just like "Windows" is a keyword only in the context of extern( ). This would make heap and stack keywords only in the context of new( ).

I see. But with the your new(...) syntax this can't be done because it is ambiguous with void* stack = allocate(); auto C = Class.new(stack)(); The placement new syntax should use some other syntax.
 And I think we can include the meaning that "scope" attribute always
 allocate on stack (Currently 4 conditions need to be met for so), but I'm
 afraid this may break something (otherwise the other 3 conditions won't be
 there?). Maybe some experts can comment on this?

 And I suggest keeping the "new" syntax for heap ("return a pointer")
 allocation. So,

Well, Andrei was looking at getting rid of this "new blah" syntax for other reasons already, so that's why I tacked in on as a pseudomethod of classes and structs.

I know. My suggestion is not to get rid of all the new syntax but make it appear less frequently.
 auto c = Class(...);   // default on heap
 auto s = Struct(...);  // default on stack (compatible with current syntax)

 scope c = Class(...);  // on stack
 scope s = Struct(...); // on stack (compatible with current syntax)

The main thing I don't like about this syntax is that it makes it impossible to create a class temporary on the stack in an expressoin. It means you always have to declare it and give it a name if you want to avoid the heap. It's just not orthogonal in that way. As in it entangles construction with declaration, when those two are usually separate concerns. Specifically as an example, say you have some object that does comparisions and you want to pass it to a sort function then uniq it . First you wrote this: T[] array; ... T[] filtered = uniq(sorted(MyComparator.new, array))); Then you decided the heap alloc was unnecessary there, so now you have to change it all around T[] array; ... { // made a block here so that theComparator goes away after sorting scope theComparator = MyComparator.new; T[] filtered = uniq(sorted(theComparator, array))); } Instead of just sticking "(stack)" in there and being done with it: T[] array; ... T[] filtered = uniq(sorted(MyComparator.new(stack), array)));

I see. So seems we must stick with method calls.
 auto c = new Class(...);  // on heap (compatible with current syntax)
 auto s = new Struct(...); // on heap (compatible with current syntax)

 auto c = new(...) Class(...);  // placement (compatible with current syntax)
 auto s = new(...) Struct(...); // placement (compatible with current
 syntax?)


 /* if you want symmetry, do it like this:

 new c = Class(...);
 new(...) c = Class(...);
 kinda strange for me. */

Yeh, me too. I definitely don't want any more of this sillyness of mixing up construction with declaration.
 These will cause minimum change while eliminating the "new" for most cases.
 This won't free the "new" keyword though.

I don't particularly think it needs to be freed up. A better case may be made for, delete, though because there's less that's special about how it works.
 Yeah! I noticed it. And I like it. In fact, I thought to myself "that's
 almost the same as my idea".


(To Benji now) Yep, it was!
 Generally, I'm not a fan of the consecutive
 sequence of parentheses (especially that templates already make for double
 parens; I can't imagine I'd want triple parens for a templatized
 consttructor call).


Well, the idea is that the versions that use "new" will be fairly rare. I'm certainly open to other ideas. I suggested using [] just for new args in another post. Like MyClass.new[scope](foo,bar) It just seems odd to me to use that in this one place and no other. I think the parens would work, which is why I went back to parens. Another possibility might be to introduce some syntax for heap vs stack and only use () for placement new -- like this: Class.*new(construct_args); // stack (like *foo is dereferenced foo pointer) Struct.*new(construct_args); // stack Class.new(construct_args); // heap Struct.new(construct_args); // heap Class.new (new_args)(construct_args); // placement Struct.new (new_args)(construct_args); // placement
 For non-template chained calls, I'm not quite sure which I prefer:

  auto a = Foo.new(heap)(Bar.new(stack)());

  auto a = on(heap) Foo(on(stack) Bar());


I think Andrei is gunning to get rid of the dangling prefix words in the grammar. That's why I put .new at the end. I think he would also be happy with a regular function-like syntax, too, but what I've seen of that looks ugly. When you do 'new' there always a class or struct involved, so why not make it a pseudo property of every class and struct, just like .init or .tupleof? --bb

Oct 25 2008
parent reply KennyTM~ <kennytm gmail.com> writes:
KennyTM~ wrote:
 Bill Baxter wrote:
 On Sat, Oct 25, 2008 at 2:04 AM, KennyTM~ <kennytm gmail.com> wrote:

  auto a = Class();  // heap   (default)
  auto a = Struct(); // stack   (default)

  auto a = Class.new(stack)(); // stack
  auto a = Struct.new(stack)(); // stack

  auto a = Class.new(heap)(); // heap
  auto a = Struct.new(heap)(); // heap

   void *addr;
   auto a = Class.new(addr)(); // placement
   auto a = Struct.new(addr)(); // placement

are also (partly irrelevant) data structures called stack and heap, even though according to the guideline these class/structs should be named Stack and Heap.

Right, that's why both Benji and I proposed making them *context* keywords. Just like "Windows" is a keyword only in the context of extern( ). This would make heap and stack keywords only in the context of new( ).

I see. But with the your new(...) syntax this can't be done because it is ambiguous with void* stack = allocate(); auto C = Class.new(stack)(); The placement new syntax should use some other syntax.

or do it like this: auto C = Class.new(placement, addr, etc)(); //--------------------^ use this to specify it is a placement new.
Oct 25 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
KennyTM~ wrote:
 KennyTM~ wrote:
 Bill Baxter wrote:
 On Sat, Oct 25, 2008 at 2:04 AM, KennyTM~ <kennytm gmail.com> wrote:

  auto a = Class();  // heap   (default)
  auto a = Struct(); // stack   (default)

  auto a = Class.new(stack)(); // stack
  auto a = Struct.new(stack)(); // stack

  auto a = Class.new(heap)(); // heap
  auto a = Struct.new(heap)(); // heap

   void *addr;
   auto a = Class.new(addr)(); // placement
   auto a = Struct.new(addr)(); // placement

are also (partly irrelevant) data structures called stack and heap, even though according to the guideline these class/structs should be named Stack and Heap.

Right, that's why both Benji and I proposed making them *context* keywords. Just like "Windows" is a keyword only in the context of extern( ). This would make heap and stack keywords only in the context of new( ).

I see. But with the your new(...) syntax this can't be done because it is ambiguous with void* stack = allocate(); auto C = Class.new(stack)(); The placement new syntax should use some other syntax.

or do it like this: auto C = Class.new(placement, addr, etc)(); //--------------------^ use this to specify it is a placement new.

maybe just add the concept of allocators. auto C1 = Class.new(parameters/*, Allocator = GC.DefaultAllocator */); auto C2 = Class.new(parameters, HeapAllocator); // on heap auto C3 = Class.new(parameters, StackAllocator); // on stack auto C4 = Class.new(parameters, MyPlacementNewAllocator); etc.. initial Allocator hierarchy could be part of the run-time, and could be user extended to allow for placement new.
Oct 25 2008
parent reply KennyTM~ <kennytm gmail.com> writes:
Yigal Chripun wrote:
 KennyTM~ wrote:
 KennyTM~ wrote:
 Bill Baxter wrote:
 On Sat, Oct 25, 2008 at 2:04 AM, KennyTM~ <kennytm gmail.com> wrote:

  auto a = Class();  // heap   (default)
  auto a = Struct(); // stack   (default)

  auto a = Class.new(stack)(); // stack
  auto a = Struct.new(stack)(); // stack

  auto a = Class.new(heap)(); // heap
  auto a = Struct.new(heap)(); // heap

   void *addr;
   auto a = Class.new(addr)(); // placement
   auto a = Struct.new(addr)(); // placement

are also (partly irrelevant) data structures called stack and heap, even though according to the guideline these class/structs should be named Stack and Heap.

keywords. Just like "Windows" is a keyword only in the context of extern( ). This would make heap and stack keywords only in the context of new( ).

is ambiguous with void* stack = allocate(); auto C = Class.new(stack)(); The placement new syntax should use some other syntax.

auto C = Class.new(placement, addr, etc)(); //--------------------^ use this to specify it is a placement new.

maybe just add the concept of allocators. auto C1 = Class.new(parameters/*, Allocator = GC.DefaultAllocator */); auto C2 = Class.new(parameters, HeapAllocator); // on heap auto C3 = Class.new(parameters, StackAllocator); // on stack auto C4 = Class.new(parameters, MyPlacementNewAllocator); etc.. initial Allocator hierarchy could be part of the run-time, and could be user extended to allow for placement new.

This can't work because parameters (I bet you mean constructor arguments here) can be variadic.
Oct 25 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
KennyTM~ wrote:
 Yigal Chripun wrote:
 KennyTM~ wrote:
 KennyTM~ wrote:
 Bill Baxter wrote:
 On Sat, Oct 25, 2008 at 2:04 AM, KennyTM~ <kennytm gmail.com> wrote:

  auto a = Class();  // heap   (default)
  auto a = Struct(); // stack   (default)

  auto a = Class.new(stack)(); // stack
  auto a = Struct.new(stack)(); // stack

  auto a = Class.new(heap)(); // heap
  auto a = Struct.new(heap)(); // heap

   void *addr;
   auto a = Class.new(addr)(); // placement
   auto a = Struct.new(addr)(); // placement

are also (partly irrelevant) data structures called stack and heap, even though according to the guideline these class/structs should be named Stack and Heap.

keywords. Just like "Windows" is a keyword only in the context of extern( ). This would make heap and stack keywords only in the context of new( ).

is ambiguous with void* stack = allocate(); auto C = Class.new(stack)(); The placement new syntax should use some other syntax.

auto C = Class.new(placement, addr, etc)(); //--------------------^ use this to specify it is a placement new.

maybe just add the concept of allocators. auto C1 = Class.new(parameters/*, Allocator = GC.DefaultAllocator */); auto C2 = Class.new(parameters, HeapAllocator); // on heap auto C3 = Class.new(parameters, StackAllocator); // on stack auto C4 = Class.new(parameters, MyPlacementNewAllocator); etc.. initial Allocator hierarchy could be part of the run-time, and could be user extended to allow for placement new.

This can't work because parameters (I bet you mean constructor arguments here) can be variadic.

I did mean the constructor arguments. The syntax itself is less important here, I was more talking about the concept of Allocators. like the ones in STL, for example. regarding the syntax: a few possibilities that come to mind: a) new!(Allocator)(args).. b) Allocator is just a regular parameter which you can add to your args list. now the c-tor need some machinery to handle the allocator. I'd say something like: a c-tor does two things, gets memory and puts stuff into that memory. so the first thing a c-tor does is automatically calls a default Allocator for the type to get memory, *unless* you call one explicitly yourself. it's similar to the way a c-tor has an implicit super() call if you don't call that yourself only this probably will need additional machinery to work. other ideas how to approach this?
Oct 25 2008
parent reply KennyTM~ <kennytm gmail.com> writes:
Yigal Chripun wrote:
 KennyTM~ wrote:
 Yigal Chripun wrote:
 KennyTM~ wrote:
 KennyTM~ wrote:
 Bill Baxter wrote:
 On Sat, Oct 25, 2008 at 2:04 AM, KennyTM~ <kennytm gmail.com> wrote:

  auto a = Class();  // heap   (default)
  auto a = Struct(); // stack   (default)

  auto a = Class.new(stack)(); // stack
  auto a = Struct.new(stack)(); // stack

  auto a = Class.new(heap)(); // heap
  auto a = Struct.new(heap)(); // heap

   void *addr;
   auto a = Class.new(addr)(); // placement
   auto a = Struct.new(addr)(); // placement

are also (partly irrelevant) data structures called stack and heap, even though according to the guideline these class/structs should be named Stack and Heap.

keywords. Just like "Windows" is a keyword only in the context of extern( ). This would make heap and stack keywords only in the context of new( ).

is ambiguous with void* stack = allocate(); auto C = Class.new(stack)(); The placement new syntax should use some other syntax.

auto C = Class.new(placement, addr, etc)(); //--------------------^ use this to specify it is a placement new.

auto C1 = Class.new(parameters/*, Allocator = GC.DefaultAllocator */); auto C2 = Class.new(parameters, HeapAllocator); // on heap auto C3 = Class.new(parameters, StackAllocator); // on stack auto C4 = Class.new(parameters, MyPlacementNewAllocator); etc.. initial Allocator hierarchy could be part of the run-time, and could be user extended to allow for placement new.

here) can be variadic.

I did mean the constructor arguments. The syntax itself is less important here, I was more talking about the concept of Allocators. like the ones in STL, for example. regarding the syntax: a few possibilities that come to mind: a) new!(Allocator)(args)..

Then Allocator must be known in compile time? Though also in STL.
 
 b) Allocator is just a regular parameter which you can add to your args
 list. now the c-tor need some machinery to handle the allocator.
 I'd say something like:
 a c-tor does two things, gets memory and puts stuff into that memory.
 so the first thing a c-tor does is automatically calls a default
 Allocator for the type to get memory, *unless* you call one explicitly
 yourself. it's similar to the way a c-tor has an implicit super() call
 if you don't call that yourself only this probably will need additional
 machinery to work.

Yes. Although the problem right now is syntactical. :(
 
 other ideas how to approach this?
 

Use the semicolon: Class.new(parameters; allocator params);
Oct 25 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
KennyTM~ wrote:
 Yigal Chripun wrote:
 KennyTM~ wrote:
 Yigal Chripun wrote:
 KennyTM~ wrote:
 KennyTM~ wrote:
 Bill Baxter wrote:
 On Sat, Oct 25, 2008 at 2:04 AM, KennyTM~ <kennytm gmail.com> wrote:

  auto a = Class();  // heap   (default)
  auto a = Struct(); // stack   (default)

  auto a = Class.new(stack)(); // stack
  auto a = Struct.new(stack)(); // stack

  auto a = Class.new(heap)(); // heap
  auto a = Struct.new(heap)(); // heap

   void *addr;
   auto a = Class.new(addr)(); // placement
   auto a = Struct.new(addr)(); // placement

are also (partly irrelevant) data structures called stack and heap, even though according to the guideline these class/structs should be named Stack and Heap.

keywords. Just like "Windows" is a keyword only in the context of extern( ). This would make heap and stack keywords only in the context of new( ).

because it is ambiguous with void* stack = allocate(); auto C = Class.new(stack)(); The placement new syntax should use some other syntax.

auto C = Class.new(placement, addr, etc)(); //--------------------^ use this to specify it is a placement new.

auto C1 = Class.new(parameters/*, Allocator = GC.DefaultAllocator */); auto C2 = Class.new(parameters, HeapAllocator); // on heap auto C3 = Class.new(parameters, StackAllocator); // on stack auto C4 = Class.new(parameters, MyPlacementNewAllocator); etc.. initial Allocator hierarchy could be part of the run-time, and could be user extended to allow for placement new.

here) can be variadic.

I did mean the constructor arguments. The syntax itself is less important here, I was more talking about the concept of Allocators. like the ones in STL, for example. regarding the syntax: a few possibilities that come to mind: a) new!(Allocator)(args)..

Then Allocator must be known in compile time? Though also in STL.
 b) Allocator is just a regular parameter which you can add to your args
 list. now the c-tor need some machinery to handle the allocator.
 I'd say something like:
 a c-tor does two things, gets memory and puts stuff into that memory.
 so the first thing a c-tor does is automatically calls a default
 Allocator for the type to get memory, *unless* you call one explicitly
 yourself. it's similar to the way a c-tor has an implicit super() call
 if you don't call that yourself only this probably will need additional
 machinery to work.

Yes. Although the problem right now is syntactical. :(
 other ideas how to approach this?

Use the semicolon: Class.new(parameters; allocator params);

if there's an allocator object, you don't need the above. just pass the extra params to the alloc object itself and not the C-tor. i.e: Class.new(params, Allocator(params)); extended example: SharedMemoryAllocator shmAlloc(params); auto a = ClassA.new(params, shmAlloc); auto b = ClassB.new(params, shmAlloc); auto c = StructA.new(params, shmAlloc); auto d = ClassC.new(params); // default Class Allocator (on heap) auto e = Struct.new(params); // default Struct Allocator (on stack) auto d = ClassC.new(params, StackAlloc); // on stack auto e = Struct.new(params, GCAlloc); // on heap something like that will require the compiler/runtime to recognize the allocator interface as special I think...
Oct 25 2008
parent KennyTM~ <kennytm gmail.com> writes:
Yigal Chripun wrote:
 KennyTM~ wrote:
 Yigal Chripun wrote:
 KennyTM~ wrote:
 Yigal Chripun wrote:
 KennyTM~ wrote:
 KennyTM~ wrote:
 Bill Baxter wrote:
 On Sat, Oct 25, 2008 at 2:04 AM, KennyTM~ <kennytm gmail.com> wrote:

  auto a = Class();  // heap   (default)
  auto a = Struct(); // stack   (default)

  auto a = Class.new(stack)(); // stack
  auto a = Struct.new(stack)(); // stack

  auto a = Class.new(heap)(); // heap
  auto a = Struct.new(heap)(); // heap

   void *addr;
   auto a = Class.new(addr)(); // placement
   auto a = Struct.new(addr)(); // placement

are also (partly irrelevant) data structures called stack and heap, even though according to the guideline these class/structs should be named Stack and Heap.

keywords. Just like "Windows" is a keyword only in the context of extern( ). This would make heap and stack keywords only in the context of new( ).

because it is ambiguous with void* stack = allocate(); auto C = Class.new(stack)(); The placement new syntax should use some other syntax.

auto C = Class.new(placement, addr, etc)(); //--------------------^ use this to specify it is a placement new.

auto C1 = Class.new(parameters/*, Allocator = GC.DefaultAllocator */); auto C2 = Class.new(parameters, HeapAllocator); // on heap auto C3 = Class.new(parameters, StackAllocator); // on stack auto C4 = Class.new(parameters, MyPlacementNewAllocator); etc.. initial Allocator hierarchy could be part of the run-time, and could be user extended to allow for placement new.

here) can be variadic.

important here, I was more talking about the concept of Allocators. like the ones in STL, for example. regarding the syntax: a few possibilities that come to mind: a) new!(Allocator)(args)..

 b) Allocator is just a regular parameter which you can add to your args
 list. now the c-tor need some machinery to handle the allocator.
 I'd say something like:
 a c-tor does two things, gets memory and puts stuff into that memory.
 so the first thing a c-tor does is automatically calls a default
 Allocator for the type to get memory, *unless* you call one explicitly
 yourself. it's similar to the way a c-tor has an implicit super() call
 if you don't call that yourself only this probably will need additional
 machinery to work.

 other ideas how to approach this?

Class.new(parameters; allocator params);

if there's an allocator object, you don't need the above. just pass the extra params to the alloc object itself and not the C-tor. i.e: Class.new(params, Allocator(params)); extended example: SharedMemoryAllocator shmAlloc(params); auto a = ClassA.new(params, shmAlloc); auto b = ClassB.new(params, shmAlloc); auto c = StructA.new(params, shmAlloc); auto d = ClassC.new(params); // default Class Allocator (on heap) auto e = Struct.new(params); // default Struct Allocator (on stack) auto d = ClassC.new(params, StackAlloc); // on stack auto e = Struct.new(params, GCAlloc); // on heap something like that will require the compiler/runtime to recognize the allocator interface as special I think...

What if the constructor may take the allocator object as its argument? class X { string t; this(string x, Allocator alc = Allocator.init) { t = x ~ alc.toString(); // why not? } } SharedMemoryAllocator shmAlloc(alloc_params); auto x = X.new("abc", shmAlloc); // what I'm calling? The semi-colon version disambiguates this situation.
Oct 25 2008
prev sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Sat, Oct 25, 2008 at 11:03 PM, KennyTM~ <kennytm gmail.com> wrote:

   auto C = Class.new(placement, addr, etc)();
 //--------------------^ use this to specify it is a placement new.



Yeh, I was thinking that was a possibility too, if .new(heap,a) .new(stack,a) and .new(a) weren't sufficiently distinct. I was thinking to call it .new(place,a), though. But same idea.
 maybe just add the concept of allocators.
 auto C1 = Class.new(parameters/*, Allocator = GC.DefaultAllocator */);
 auto C2 = Class.new(parameters, HeapAllocator); // on heap
 auto C3 = Class.new(parameters, StackAllocator); // on stack
 auto C4 = Class.new(parameters, MyPlacementNewAllocator);
 etc..
 initial Allocator hierarchy could be part of the run-time, and could be
 user extended to allow for placement new.

This can't work because parameters (I bet you mean constructor arguments here) can be variadic.

You could fix that by putting the allocator first, or attach params to the allocator auto C4 = Class.new(MyPlacementNewAllocator(parameters)) The bigger concern to me is that StackAllocator can't be just a regular allocator. The compiler would have to recognize that StackAllocator was being used and reserve the appropriate space on the stack, and then pass that stack address to the StackAllocator somehow. It's not impossible but it sounds messy from the implementation perspective. And I find it kind of jarring that the three modes look similar, but one is actually invoking deep compiler magic while the other two are just regular Joes. --bb
Oct 25 2008
prev sibling parent dsimcha <dsimcha yahoo.com> writes:
== Quote from Bill Baxter (wbaxter gmail.com)'s article
 It kills static opCall for classes, but who cares.

Actually, I sometimes use static opCall to encapsulate the creation of temp variables that I might want to recycle for performance reasons in some cases, though more likely with a struct than with a class. Example: SomeType foo() { auto temp = new SomeType[someNumber]; doStuff; return someStuff; } would become: struct foo { SomeType[] temp; static opCall() { //Creates a foo, simulates a regular function call. //Good for when I don't care about recycling temp storage. foo f; return f.doStuff; } SomeType doStuff() { temp.length = someNumber; //Only allocates first time. doStuff; return temp; } } This way, I can either use it the clean, easy way via the static opCall simulating a regular function call, or the efficient way by making an explicit instance of the struct, and doStuff() in a loop.
Oct 23 2008
prev sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Sat, Oct 25, 2008 at 2:04 AM, KennyTM~ <kennytm gmail.com> wrote:

  auto a = Class();  // heap   (default)
  auto a = Struct(); // stack   (default)

  auto a = Class.new(stack)(); // stack
  auto a = Struct.new(stack)(); // stack

  auto a = Class.new(heap)(); // heap
  auto a = Struct.new(heap)(); // heap

   void *addr;
   auto a = Class.new(addr)(); // placement
   auto a = Struct.new(addr)(); // placement

I strongly *oppose* using stack and heap as keywords because there are also (partly irrelevant) data structures called stack and heap, even though according to the guideline these class/structs should be named Stack and Heap.

Right, that's why both Benji and I proposed making them *context* keywords. Just like "Windows" is a keyword only in the context of extern( ). This would make heap and stack keywords only in the context of new( ).
 And I think we can include the meaning that "scope" attribute always
 allocate on stack (Currently 4 conditions need to be met for so), but I'm
 afraid this may break something (otherwise the other 3 conditions won't be
 there?). Maybe some experts can comment on this?

 And I suggest keeping the "new" syntax for heap ("return a pointer")
 allocation. So,

Well, Andrei was looking at getting rid of this "new blah" syntax for other reasons already, so that's why I tacked in on as a pseudomethod of classes and structs.
 auto c = Class(...);   // default on heap
 auto s = Struct(...);  // default on stack (compatible with current syntax)

 scope c = Class(...);  // on stack
 scope s = Struct(...); // on stack (compatible with current syntax)

The main thing I don't like about this syntax is that it makes it impossible to create a class temporary on the stack in an expressoin. It means you always have to declare it and give it a name if you want to avoid the heap. It's just not orthogonal in that way. As in it entangles construction with declaration, when those two are usually separate concerns. Specifically as an example, say you have some object that does comparisions and you want to pass it to a sort function then uniq it . First you wrote this: T[] array; ... T[] filtered = uniq(sorted(MyComparator.new, array))); Then you decided the heap alloc was unnecessary there, so now you have to change it all around T[] array; ... { // made a block here so that theComparator goes away after sorting scope theComparator = MyComparator.new; T[] filtered = uniq(sorted(theComparator, array))); } Instead of just sticking "(stack)" in there and being done with it: T[] array; ... T[] filtered = uniq(sorted(MyComparator.new(stack), array)));
 auto c = new Class(...);  // on heap (compatible with current syntax)
 auto s = new Struct(...); // on heap (compatible with current syntax)

 auto c = new(...) Class(...);  // placement (compatible with current syntax)
 auto s = new(...) Struct(...); // placement (compatible with current
 syntax?)


 /* if you want symmetry, do it like this:

 new c = Class(...);
 new(...) c = Class(...);
 kinda strange for me. */

Yeh, me too. I definitely don't want any more of this sillyness of mixing up construction with declaration.
 These will cause minimum change while eliminating the "new" for most cases.
 This won't free the "new" keyword though.

I don't particularly think it needs to be freed up. A better case may be made for, delete, though because there's less that's special about how it works.
 Yeah! I noticed it. And I like it. In fact, I thought to myself "that's
 almost the same as my idea".


(To Benji now) Yep, it was!
 Generally, I'm not a fan of the consecutive
 sequence of parentheses (especially that templates already make for double
 parens; I can't imagine I'd want triple parens for a templatized
 consttructor call).


Well, the idea is that the versions that use "new" will be fairly rare. I'm certainly open to other ideas. I suggested using [] just for new args in another post. Like MyClass.new[scope](foo,bar) It just seems odd to me to use that in this one place and no other. I think the parens would work, which is why I went back to parens. Another possibility might be to introduce some syntax for heap vs stack and only use () for placement new -- like this: Class.*new(construct_args); // stack (like *foo is dereferenced foo pointer) Struct.*new(construct_args); // stack Class.new(construct_args); // heap Struct.new(construct_args); // heap Class.new (new_args)(construct_args); // placement Struct.new (new_args)(construct_args); // placement
 For non-template chained calls, I'm not quite sure which I prefer:

  auto a = Foo.new(heap)(Bar.new(stack)());

  auto a = on(heap) Foo(on(stack) Bar());


I think Andrei is gunning to get rid of the dangling prefix words in the grammar. That's why I put .new at the end. I think he would also be happy with a regular function-like syntax, too, but what I've seen of that looks ugly. When you do 'new' there always a class or struct involved, so why not make it a pseudo property of every class and struct, just like .init or .tupleof? --bb
Oct 24 2008
prev sibling next sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Thu, Oct 23, 2008 at 8:47 PM, Yigal Chripun <yigal100 gmail.com> wrote:
 Bill Baxter wrote:
 On Thu, Oct 23, 2008 at 6:32 PM, bearophile
 <bearophileHUGS lycos.com> wrote:
 In Python to create new instances of a class you use the normal
 function call syntax, this allows you to use them as factory
 functions:

 class C: def __init__(self, id): self.id = id def __repr__(self):
 return "<%s>" % self.id seq = map(C, [1, -5, 10, 3])

 That creates an array (list) of four objects.

 In D with a map() you can do something similar:

 import d.all; import std.string: format;

 class C { int id; this(int id) { this.id = id; } string toString()
 { return format("<%s>", this.id); } static C opCall(int id) {
 return new C(id); } } void main() { auto seq = map((int id){return
 new C(id);}, [1, -5, 10, 3]); putr(seq); }

 You can use the opCall method in a more direct way:

 auto seq2 = map(&(C.opCall), [1, -5, 10, 3]); putr(seq2);

 But probably even better is to replace the current new syntax with
 a class method that creates the instances (I think the Ruby
 language has such syntax):

 auto seq3 = map(C.new, [1, -5, 10, 3]); putr(seq3);

That would be map(&(C.new), [1,-5,10,3]); wouldn't it?
 With that normal code like:

 new Foo(10, 20)

 becomes:

 Foo.new(10, 20)

 Not a big change but allows a more functional style of coding.

I like it. Can we get rid of delete's specialness too? Maybe just delete(anInstance); Make it like a normal function too. Enabling things like map(&delete, [obj1, obj2, obj3]) --bb

Andrei already mentioned that he wants to get read of new/delete IIRC. I like Ruby's way of making it regular methods. something like: obj.new(..) and obj.delete() but, how do you specify stack vs heap allocation? something like: auto a = new S(); //on heap auto b = S(); //on stack also, what about placement new?

Hmm, I didn't realize D had a placement new. But clearly it does and more -- you can pass args to new to control the allocator. http://www.digitalmars.com/d/2.0/class.html look for "Class Allocators". also http://www.digitalmars.com/d/2.0/memory.html#newdelete So simply saying make it Obj.new(args) won't cut it. You need to be able to do something like Obj.new(newargs)(constructorargs) also. --bb
Oct 23 2008
prev sibling next sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Thu, Oct 23, 2008 at 8:47 PM, Yigal Chripun <yigal100 gmail.com> wrote:
 Bill Baxter wrote:
 On Thu, Oct 23, 2008 at 6:32 PM, bearophile
 <bearophileHUGS lycos.com> wrote:
 In Python to create new instances of a class you use the normal
 function call syntax, this allows you to use them as factory
 functions:

 class C: def __init__(self, id): self.id = id def __repr__(self):
 return "<%s>" % self.id seq = map(C, [1, -5, 10, 3])

 That creates an array (list) of four objects.

 In D with a map() you can do something similar:

 import d.all; import std.string: format;

 class C { int id; this(int id) { this.id = id; } string toString()
 { return format("<%s>", this.id); } static C opCall(int id) {
 return new C(id); } } void main() { auto seq = map((int id){return
 new C(id);}, [1, -5, 10, 3]); putr(seq); }

 You can use the opCall method in a more direct way:

 auto seq2 = map(&(C.opCall), [1, -5, 10, 3]); putr(seq2);

 But probably even better is to replace the current new syntax with
 a class method that creates the instances (I think the Ruby
 language has such syntax):

 auto seq3 = map(C.new, [1, -5, 10, 3]); putr(seq3);

That would be map(&(C.new), [1,-5,10,3]); wouldn't it?
 With that normal code like:

 new Foo(10, 20)

 becomes:

 Foo.new(10, 20)

 Not a big change but allows a more functional style of coding.

I like it. Can we get rid of delete's specialness too? Maybe just delete(anInstance); Make it like a normal function too. Enabling things like map(&delete, [obj1, obj2, obj3]) --bb

Andrei already mentioned that he wants to get read of new/delete IIRC. I like Ruby's way of making it regular methods. something like: obj.new(..) and obj.delete() but, how do you specify stack vs heap allocation? something like: auto a = new S(); //on heap auto b = S(); //on stack also, what about placement new?

Hmm, I didn't realize D had a placement new. But clearly it does and more -- you can pass args to new to control the allocator. http://www.digitalmars.com/d/2.0/class.html look for "Class Allocators". also http://www.digitalmars.com/d/2.0/memory.html#newdelete So simply saying make it Obj.new(args) won't cut it. You need to be able to do something like Obj.new(newargs)(constructorargs) also. --bb
Oct 23 2008
prev sibling next sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Thu, Oct 23, 2008 at 9:26 PM, dsimcha <dsimcha yahoo.com> wrote:
 == Quote from Yigal Chripun (yigal100 gmail.com)'s article
 Bill Baxter wrote:
 On Thu, Oct 23, 2008 at 6:32 PM, bearophile
 <bearophileHUGS lycos.com> wrote:
 In Python to create new instances of a class you use the normal
 function call syntax, this allows you to use them as factory
 functions:

 class C: def __init__(self, id): self.id = id def __repr__(self):
 return "<%s>" % self.id seq = map(C, [1, -5, 10, 3])

 That creates an array (list) of four objects.

 In D with a map() you can do something similar:

 import d.all; import std.string: format;

 class C { int id; this(int id) { this.id = id; } string toString()
 { return format("<%s>", this.id); } static C opCall(int id) {
 return new C(id); } } void main() { auto seq = map((int id){return
 new C(id);}, [1, -5, 10, 3]); putr(seq); }

 You can use the opCall method in a more direct way:

 auto seq2 = map(&(C.opCall), [1, -5, 10, 3]); putr(seq2);

 But probably even better is to replace the current new syntax with
 a class method that creates the instances (I think the Ruby
 language has such syntax):

 auto seq3 = map(C.new, [1, -5, 10, 3]); putr(seq3);

That would be map(&(C.new), [1,-5,10,3]); wouldn't it?
 With that normal code like:

 new Foo(10, 20)

 becomes:

 Foo.new(10, 20)

 Not a big change but allows a more functional style of coding.

I like it. Can we get rid of delete's specialness too? Maybe just delete(anInstance); Make it like a normal function too. Enabling things like map(&delete, [obj1, obj2, obj3]) --bb

I like Ruby's way of making it regular methods. something like: obj.new(..) and obj.delete() but, how do you specify stack vs heap allocation? something like: auto a = new S(); //on heap auto b = S(); //on stack also, what about placement new?

One thing I would definitely like to see is for struct and class syntax to be unified so that if you change a struct to a class or vice versa, this doesn't require changes all over your code. Then again, the reference vs. value semantic issue may bite you anyhow.

To me this makes a lot of sense: auto a = SomeStruct(); // a is a struct on the stack auto b = SomeClass(); // b is a class on the heap It kills static opCall for classes, but who cares. I'm often tempted to write static opCalls for classes like this anyway: static SomeClass opCall() { return new SomeClass(); } Just to get the uniformity of construction syntax. --bb
Oct 23 2008
prev sibling next sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Fri, Oct 24, 2008 at 5:07 AM, Steven Schveighoffer
<schveiguy yahoo.com> wrote:
 "Bill Baxter" wrote
 To me this makes a lot of sense:

 auto a = SomeStruct();  // a is a struct on the stack
 auto b = SomeClass();  // b is a class on the heap

 It kills static opCall for classes, but who cares.  I'm often tempted
 to write static opCalls for classes like this anyway:
    static SomeClass opCall() {  return new SomeClass(); }

 Just to get the uniformity of construction syntax.

It also cleans up this ugliness: auto data = (new FileInput(filename)).readAll(); Which then becomes: auto data = FileInput(filename).readAll();

But, ok, this isn't Python so we have to have a story for placement new. I believe use of placement new is pretty rare. (I use it rarely, anyway. :-) ) If so it should be ok if the syntax is a little clunky. Also heap construction of structs. How about this as a start: // Default cases auto a = SomeStruct(arg); // struct on the stack auto a = SomeClass(arg); // class on the heap // Anti-default cases auto a = SomeStruct.new(arg); // struct on the heap scope a = SomeClass(arg); // class on the stack // Placement cases auto a = SomeStruct.new[placement_arg](arg); // placement struct auto a = SomeClass.new[placement_arg](arg); // placement class Not sure about those brackets. Kind of odd-ball, but placement new is kind of odd-ball anyway. And I think we agreed that such syntax was workable for templates, if not pleasant for the programmer. But new expressions are rarely part of mondo compound expressions, and there's the keyword "new" to clue you in, so I think it should be acceptable in this case. --bb
Oct 23 2008
prev sibling next sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Fri, Oct 24, 2008 at 5:44 AM, bearophile <bearophileHUGS lycos.com> wrote:
 Bill Baxter:
 How about this as a start:

 // Default cases
 auto a = SomeStruct(arg);  // struct on the stack
 auto a = SomeClass(arg);  // class on the heap

 // Anti-default cases
 auto a = SomeStruct.new(arg);  // struct on the heap
 scope a = SomeClass(arg);  // class on the stack

 // Placement cases
 ...

I agree that the placement new syntax can be less nice. But for the struct/class cases I'd like something more symmetric :-) A first silly possibility: auto a = SomeStruct(arg); // stack auto a = SomeStruct.new(arg); // heap auto a = SomeStruct.pnew(placement_arg)(arg); // placement A possible alternative: auto a = SomeStruct.pnew(placement_arg).new(arg); // placement auto a = SomeClass(arg); // stack auto a = SomeClass.new(arg); // heap auto a = SomeClass.pnew(placement_arg)(arg); // placement A possible alternative: auto a = SomeClass.pnew(placement_arg).new(arg); // placement

Well, D tries to make heap classes look syntactically the same stack structs in most cases despite the semantic differences. "Foo x" is either a stack struct or a heap class. The 'x' does not look like a pointer if it's a class, but in fact it is a pointer. That was D's big change over C++ -- make the *default* case for those two use the same syntax. So I don't see why construction should be the only place to put that distinction in your face. The default syntaxes look the same elsewhere, so the default scheme for construction should look the same for both. Such uniformity is also going to be better for writing templates that construct things. Structs are more likely to be pure value types than classes. So if I want to write a template that does non-default construction of an array of Things I'd rather be able to write this: Thing[] make_thing_array(Thing, ConstructArgs...)(size_t len, ConstructArgs A) { Thing[] ret; ret.length = len; foreach(ref o; ret) { o = Thing(A); } } Than have to resort to static ifs all the time just to get the default behavior: Thing[] make_thing_array(Thing, ConstructArgs...)(size_t len, ConstructArgs A) { Thing[] ret; ret.length = len; static if(is(Thing : class)) { foreach(ref o; ret) { o = new Thing(A); } } else { foreach(ref o; ret) { o = Thing(A); } } } Put another way, I think the case of "give me default allocation for the type" is more common in generic code than "give me heap allocation no matter what". So for me, the main thing I care about is making the *default* allocation strategy use the same syntax. Beyond that I don't really care much what syntax is chosen. ... But here's another thought. It doesn't really have to be me vs you here. Both cases do have some use. Maybe the best thing here is a syntax that can *either* emphasize sameness of allocation *or* emphasize default behavior. So then: auto a = Class(); // heap (default) auto a = Struct(); // stack (default) auto a = Class.new(stack)(); // stack auto a = Struct.new(stack)(); // stack auto a = Class.new(heap)(); // heap auto a = Struct.new(heap)(); // heap void *addr; auto a = Class.new(addr)(); // placement auto a = Struct.new(addr)(); // placement One thing to consider is that anything other than the default strategy is really a kind of a vague placement new. You're telling the compiler to construct the object in some non-default memory location, you're just not telling it exactly what the address is, just whether the address should be in heap or stack. In the above "heap" and "stack" are context keywords of new. --bb
Oct 23 2008
prev sibling next sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Fri, Oct 24, 2008 at 6:28 AM, Sergey Gromov <snake.scaly gmail.com> wrote:
 Fri, 24 Oct 2008 05:31:44 +0900,
 Bill Baxter wrote:
 But, ok, this isn't Python so we have to have a story for placement new.
 I believe use of placement new is pretty rare.  (I use it rarely,
 anyway. :-) )   If so it should be ok if the syntax is a little
 clunky.

Maybe do it with a library function? auto c = core.intrinsic.construct!(C)(addr, ctor_arg0, ctor_arg1);

The current placement new in D can take an arbitrary list of args. So you have two lists -- new_args and ctor_args. --bb
Oct 23 2008
prev sibling next sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Fri, Oct 24, 2008 at 5:55 AM, dsimcha <dsimcha yahoo.com> wrote:
 == Quote from Bill Baxter (wbaxter gmail.com)'s article
 It kills static opCall for classes, but who cares.

Actually, I sometimes use static opCall to encapsulate the creation of temp variables that I might want to recycle for performance reasons in some cases, though more likely with a struct than with a class. Example: SomeType foo() { auto temp = new SomeType[someNumber]; doStuff; return someStuff; } would become: struct foo { SomeType[] temp; static opCall() { //Creates a foo, simulates a regular function call. //Good for when I don't care about recycling temp storage. foo f; return f.doStuff; } SomeType doStuff() { temp.length = someNumber; //Only allocates first time. doStuff; return temp; } } This way, I can either use it the clean, easy way via the static opCall simulating a regular function call, or the efficient way by making an explicit instance of the struct, and doStuff() in a loop.

There are definitely uses for static opCall, but it's all just for cuteness' sake. There's no reason you can't use a regular static method instead. Like temp.length = someNumber.tmp; // static method call, only allocates first time --bb
Oct 23 2008
prev sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Fri, Oct 24, 2008 at 3:01 PM, Benji Smith <dlanguage benjismith.net> wrote:
 Bill Baxter wrote:
 How about this as a start:

 // Default cases
 auto a = SomeStruct(arg);  // struct on the stack
 auto a = SomeClass(arg);  // class on the heap

 // Anti-default cases
 auto a = SomeStruct.new(arg);  // struct on the heap
 scope a = SomeClass(arg);  // class on the stack

 // Placement cases
 auto a = SomeStruct.new[placement_arg](arg); // placement struct
 auto a = SomeClass.new[placement_arg](arg); // placement class

I don't particularly care about getting rid of the "new" keyword. In fact, I especially like the part about getting rid of this little ugly: // uuuuuugly auto x = (new MyClass()).whatever(); // holy crap. so much better. auto x = MyClass().whatever(); But I think it's weird that people want to retain "new" to indicate heap construction and use the word "scope" to indicate stack construction. Except for structs, which would have different rules. Ugh. Why not just introduce "heap" and "stack" keywords, and say it directly?

Did you see my subsequent proposal? I'm actually quite fond of it. But no one has responded to it. :-( [Bill cry's himself a silent tear.]
   stack auto x = MyClass();
   heap auto y = MyStruct();

 ...or, since "heap" and "stack" are good user keywords, maybe introduce an
 "on" keyword. Maybe something like this:

   auto x = on(stack) MyClass();
   auto y = on(heap) MyStruct();

That's kinda cute. "in" would almost work there. Too bad we don't usually say "put it in the stack" in CS. :-)
 To me, there's nothing about the "new" keyword that implies heap
 construction. "Scope" is a little bit better, because I know the memory will
 be cleaned up at the end of the scope, but that doesn't *necessarily* mean
 stack allocation, per se.

New is already a keyword meaning allocation/construction in D, Java, C++ and maybe C#? So anyone familiar with those will know right away that "new" probably has something to do with construction. So the question is why wouldn't you use "new"? --bb
Oct 24 2008