www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - auto ref

reply Walter Bright <newshound1 digitalmars.com> writes:
There's a need in generic code to have a function take a parameter by 
ref if it is an lvalue, and by value if it is an rvalue. This can be 
addressed by making it a template using auto ref:

   T foo(T)(auto ref T x) { ... }

   foo(3)    // call by value
   int y;
   foo(y)    // call by reference

There is also a need to 'transmit' the ref'ness to the return value. 
This can be done with auto ref:

   auto ref foo(T)(auto ref T x) { return x; }

   foo(3)   =>  int foo(int x)
   foo(y)   =>  ref int foo(ref int x)

This means that the generic forwarding function would look like:

    auto ref foo(alias F, T...)(auto ref T args) { return F(args); }
Dec 15 2009
next sibling parent reply Mikhail Dahl <mikhail nevertobeknown.com> writes:
On 16/12/2009 07:18, Walter Bright wrote:
 There's a need in generic code to have a function take a parameter by
 ref if it is an lvalue, and by value if it is an rvalue. This can be
 addressed by making it a template using auto ref:

 T foo(T)(auto ref T x) { ... }

 foo(3) // call by value
 int y;
 foo(y) // call by reference

 There is also a need to 'transmit' the ref'ness to the return value.
 This can be done with auto ref:

 auto ref foo(T)(auto ref T x) { return x; }

 foo(3) => int foo(int x)
 foo(y) => ref int foo(ref int x)

 This means that the generic forwarding function would look like:

 auto ref foo(alias F, T...)(auto ref T args) { return F(args); }
Just to confirm (I'm just getting into D, so I don't know much about the language, still learning), but the idea is to apply 'auto' not only to types but also to storage classes? As in 'automatically apply ref in the case of lvalue'? If so I'm a little confused by 'auto ref foo(...' - if auto ref here is also simply an 'apply ref in the case of lvalue', then where is the return type being inferred from? Dahl
Dec 15 2009
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Mikhail Dahl wrote:
 Just to confirm (I'm just getting into D, so I don't know much about the 
 language, still learning), but the idea is to apply 'auto' not only to 
 types but also to storage classes? As in 'automatically apply ref in the 
 case of lvalue'?
Yes, exactly.
 If so I'm a little confused by 'auto ref foo(...' - if auto ref here is 
 also simply an 'apply ref in the case of lvalue', then where is the 
 return type being inferred from?
From the return statement in the body of the function.
Dec 15 2009
parent reply Mikhail Dahl <mikhail nevertobeknown.com> writes:
On 16/12/2009 07:41, Walter Bright wrote:
 Mikhail Dahl wrote:
 Just to confirm (I'm just getting into D, so I don't know much about
 the language, still learning), but the idea is to apply 'auto' not
 only to types but also to storage classes? As in 'automatically apply
 ref in the case of lvalue'?
Yes, exactly.
 If so I'm a little confused by 'auto ref foo(...' - if auto ref here
 is also simply an 'apply ref in the case of lvalue', then where is the
 return type being inferred from?
From the return statement in the body of the function.
Ah yeah sorry, it's too obvious. That's what happens at 8am when you've had no sleep.
Dec 16 2009
parent Walter Bright <newshound1 digitalmars.com> writes:
Mikhail Dahl wrote:
 That's what happens at 8am when you've 
 had no sleep.
I know the feeling well. Well enough that I don't even bother trying to code when I feel that way, as after I get some sleep I have to unwind all those "great ideas" I had when overtired.
Dec 16 2009
prev sibling parent reply KennyTM~ <kennytm gmail.com> writes:
On Dec 16, 09 15:18, Walter Bright wrote:
 There's a need in generic code to have a function take a parameter by
 ref if it is an lvalue, and by value if it is an rvalue. This can be
 addressed by making it a template using auto ref:

 T foo(T)(auto ref T x) { ... }

 foo(3) // call by value
 int y;
 foo(y) // call by reference

 There is also a need to 'transmit' the ref'ness to the return value.
 This can be done with auto ref:

 auto ref foo(T)(auto ref T x) { return x; }

 foo(3) => int foo(int x)
 foo(y) => ref int foo(ref int x)

 This means that the generic forwarding function would look like:

 auto ref foo(alias F, T...)(auto ref T args) { return F(args); }
auto const?
Dec 16 2009
next sibling parent reply Jason House <jason.james.house gmail.com> writes:
KennyTM~ Wrote:

 auto const?
I was wondering the same thing.
Dec 16 2009
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Jason House wrote:
 KennyTM~ Wrote:
 
 auto const?
I was wondering the same thing.
The const transport thing is, unfortunately, a very different problem.
Dec 16 2009
parent reply Jason House <jason.james.house gmail.com> writes:
Walter Bright Wrote:

 Jason House wrote:
 KennyTM~ Wrote:
 
 auto const?
I was wondering the same thing.
The const transport thing is, unfortunately, a very different problem.
Of course, but it may still go through bikeshed issues. This morning I read about inout, return, vconst, aconst, sameconst, autoconst, auto const, and bikeshed. At least one of those was in jest :) auto const isn't that bad, and you obviously liked auto ref...
Dec 16 2009
next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 16 Dec 2009 16:46:14 -0500, Jason House  
<jason.james.house gmail.com> wrote:

 Walter Bright Wrote:

 Jason House wrote:
 KennyTM~ Wrote:

 auto const?
I was wondering the same thing.
The const transport thing is, unfortunately, a very different problem.
Of course, but it may still go through bikeshed issues. This morning I read about inout, return, vconst, aconst, sameconst, autoconst, auto const, and bikeshed. At least one of those was in jest :) auto const isn't that bad, and you obviously liked auto ref...
I think one of the problems is that ref is a storage class, so it's easy to insert another storage class on top of it. With the const transport issue, the identifier has to be a type constructor, and needs to decorate types in the same way const can. i.e.: identifier(int)[] but ref isn't like this, you don't see: ref(int)[] So if you used auto const, what does this mean: auto const(int)[] It looks strange to me. Remember that you will see this not only in parameter types but in stack variable declarations. This also makes auto a type constructor, which it is not currently. I think we have enough multi-meaning keywords. I don't really care what the keyword turns out to be, but I think it needs to be a single keyword. -Steve
Dec 16 2009
prev sibling parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2009-12-16 16:46:14 -0500, Jason House <jason.james.house gmail.com> said:

 Walter Bright Wrote:
 
 Jason House wrote:
 KennyTM~ Wrote:
 
 auto const?
I was wondering the same thing.
The const transport thing is, unfortunately, a very different problem.
Of course, but it may still go through bikeshed issues. This morning I read about inout, return, vconst, aconst, sameconst, autoconst, auto const, and bikeshed. At least one of those was in jest :) auto const isn't that bad, and you obviously liked auto ref...
Since this is just a special kind of const, it could be called "const^" (const or a derived constness): const?(Object) func(const?(Object) o) { return o; } The interesting thing about it, beside not taking a keyword, is that it can scale in the future if we need to add many distinct constness to the same function signature: const?(Object) func(const?(Object) o, const?2(Object) o2, out const?2(Object) o3) { o3 = o2; return o; } Not that you'd need that often, but if it does becomes necessary in the future we'll still have some options. Furthermore, the concept could be extended to any type. This could be useful with class hierarchies: Object? func(Object? o) { writeln(o.toString()); return o; } MyObject o = func(new MyObject); Here, "Object?" means Object or a derived type. Yeah, that breaks the proposed syntax for nullable types... just too bad. If that's really a problem we could use ^ instead. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Dec 16 2009
next sibling parent Michel Fortin <michel.fortin michelf.com> writes:
On 2009-12-16 19:05:09 -0500, Michel Fortin <michel.fortin michelf.com> said:

 Since this is just a special kind of const, it could be called "const^" 
 (const or a derived constness):
Bad editing. That should be "const?". Sorry if it confuses anyone. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Dec 16 2009
prev sibling next sibling parent reply =?UTF-8?B?UGVsbGUgTcOlbnNzb24=?= <pelle.mansson gmail.com> writes:
On 12/17/2009 01:05 AM, Michel Fortin wrote:
 Object? func(Object? o) {
 writeln(o.toString());
 return o;
 }

 MyObject o = func(new MyObject);

 Here, "Object?" means Object or a derived type.
You know, just Object means Object or a derived type. That's what inheritance is.
Dec 16 2009
parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2009-12-17 01:57:50 -0500, Pelle Månsson <pelle.mansson gmail.com> said:

 On 12/17/2009 01:05 AM, Michel Fortin wrote:
 Object? func(Object? o) {
 writeln(o.toString());
 return o;
 }
 
 MyObject o = func(new MyObject);
 
 Here, "Object?" means Object or a derived type.
 
You know, just Object means Object or a derived type. That's what inheritance is.
The idea is to be able to say in the function signature that the same type is returned, avoiding a cast that would be unnecessary otherwise. It's the same principle as for "const?", or "inout". But you're right that my definition isn't very good. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Dec 17 2009
parent reply KennyTM~ <kennytm gmail.com> writes:
On Dec 17, 09 19:44, Michel Fortin wrote:
 On 2009-12-17 01:57:50 -0500, Pelle MÃ¥nsson <pelle.mansson gmail.com> said:

 On 12/17/2009 01:05 AM, Michel Fortin wrote:
 Object? func(Object? o) {
 writeln(o.toString());
 return o;
 }

 MyObject o = func(new MyObject);

 Here, "Object?" means Object or a derived type.
You know, just Object means Object or a derived type. That's what inheritance is.
The idea is to be able to say in the function signature that the same type is returned, avoiding a cast that would be unnecessary otherwise. It's the same principle as for "const?", or "inout". But you're right that my definition isn't very good.
T func(T)(T o);
Dec 17 2009
parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2009-12-17 07:09:57 -0500, KennyTM~ <kennytm gmail.com> said:

 On Dec 17, 09 19:44, Michel Fortin wrote:
 On 2009-12-17 01:57:50 -0500, Pelle Månsson <pelle.mansson gmail.com> said:
 
 On 12/17/2009 01:05 AM, Michel Fortin wrote:
 Object? func(Object? o) {
 writeln(o.toString());
 return o;
 }
 
 MyObject o = func(new MyObject);
 
 Here, "Object?" means Object or a derived type.
 
You know, just Object means Object or a derived type. That's what inheritance is.
The idea is to be able to say in the function signature that the same type is returned, avoiding a cast that would be unnecessary otherwise. It's the same principle as for "const?", or "inout". But you're right that my definition isn't very good.
T func(T)(T o);
That would work, unless you want a virtual function. If templates were always an acceptable solution, the whole discussion about passing const qualifiers from the argument to the return value wouldn't be of any use either. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Dec 17 2009
parent reply "Nick Sabalausky" <a a.a> writes:
"Michel Fortin" <michel.fortin michelf.com> wrote in message 
news:hgd9jb$26v9$1 digitalmars.com...
 On 2009-12-17 07:09:57 -0500, KennyTM~ <kennytm gmail.com> said:

 On Dec 17, 09 19:44, Michel Fortin wrote:
 On 2009-12-17 01:57:50 -0500, Pelle Månsson <pelle.mansson gmail.com> 
 said:

 On 12/17/2009 01:05 AM, Michel Fortin wrote:
 Object? func(Object? o) {
 writeln(o.toString());
 return o;
 }

 MyObject o = func(new MyObject);

 Here, "Object?" means Object or a derived type.
You know, just Object means Object or a derived type. That's what inheritance is.
The idea is to be able to say in the function signature that the same type is returned, avoiding a cast that would be unnecessary otherwise. It's the same principle as for "const?", or "inout". But you're right that my definition isn't very good.
T func(T)(T o);
That would work, unless you want a virtual function. If templates were always an acceptable solution, the whole discussion about passing const qualifiers from the argument to the return value wouldn't be of any use either.
Pardon my ignorance, but why is it that templated functions can't be virtual?
Dec 17 2009
parent reply dsimcha <dsimcha yahoo.com> writes:
== Quote from Nick Sabalausky (a a.a)'s article
 Pardon my ignorance, but why is it that templated functions can't be
 virtual?
This is an unfortunate consequence of compilation model leaking out into language design. You're supposed to be able to subclass a base class even if you don't have the full source code to it. Let's say we have: class A { void doStuff(T)(T arg) {} } class B : A { void doStuff(T)(T arg) { writeln("In B."); } } And then we do: B b = new B; b.doStuff(1); The instantiation of B.doStuff() with an int would require that the vtable for A be updated to include doStuff!(int). This is not possible if we also allow base classes to be compiled separately from derived classes and the code that uses the derived class. The only solution I see would be to completely get rid of separate compilation. For my personal use, since most of my projects are under 10k lines and take a negligible amount of time to compile anyhow, I'd be in favor of this. However, for larger projects or projects that require things to work based only on interface, without access to the full source code, this is probably impractical.
Dec 17 2009
next sibling parent reply Bill Baxter <wbaxter gmail.com> writes:
On Thu, Dec 17, 2009 at 9:28 AM, dsimcha <dsimcha yahoo.com> wrote:
 =3D=3D Quote from Nick Sabalausky (a a.a)'s article
 Pardon my ignorance, but why is it that templated functions can't be
 virtual?
This is an unfortunate consequence of compilation model leaking out into =
language
 design. =A0You're supposed to be able to subclass a base class even if yo=
u don't
 have the full source code to it. =A0Let's say we have:

 class A {
 =A0 =A0void doStuff(T)(T arg) {}
 }

 class B : A {
 =A0 =A0void doStuff(T)(T arg) {
 =A0 =A0 =A0 =A0writeln("In B.");
 =A0 =A0}
 }

 And then we do:

 B b =3D new B;
 b.doStuff(1);

 The instantiation of B.doStuff() with an int would require that the vtabl=
e for A
 be updated to include doStuff!(int). =A0This is not possible if we also a=
llow base
 classes to be compiled separately from derived classes and the code that =
uses the
 derived class.

 The only solution I see would be to completely get rid of separate compil=
ation.
 For my personal use, since most of my projects are under 10k lines and ta=
ke a
 negligible amount of time to compile anyhow, I'd be in favor of this. =A0=
However,
 for larger projects or projects that require things to work based only on
 interface, without access to the full source code, this is probably impra=
ctical. This 'auto ref' stuff and the multi-flavor 'vconst' functions are basically templates with a known list of instantiations (ref /no-ref, and const/immutable/plain) In theory there's no reason you couldn't allow templates to also create virtual functions, if you limit yourself to listing the possible instantiations up front. It feels like we may be missing out on a more general construct here that could embrace all these cases via some extension to template syntax. I'm reminded of how Go interfaces do the job of both specifying that a function is a template and checking constraints on use. In D-ish syntax, Go uses something like this: interface Foo { ... } void doSomething(Foo f, int x) { ... } To mean something like template CheckIfIsFoo!(T) { ... } void doSomething(T)(T f, int x) if (CheckIfIsFoo!(T)) { ... } The advantage is that the compiler can offer better error messages about what part of the check fails, and the syntax is cleaned up significantly (less repetition, less proliferation of parentheses). Maybe a similar approach could be used to separate the specification of these alternatives into pseudo-types that can then be used as parameters. Like static interface VConst(T) { constnessOf(T) is in [const, immutable, !const]; } VConst!(Bar) doSomething(VConst!(Bar) b, int y) {...} static interface MaybeRef(T) { refnessOf(T) is in [ref, !ref]; } MaybeRef!(Bar) doSomethingElse(MaybeRef!(Bar) b, int y) { ... } Anyway, the more I look at standard C++ and D template syntax the more I think that Go is onto something. Yeh, there are some generic container-like templated types where T really can be any type (which Go inexcusably ignores), but very often there are some restrictions on T that are important enough and common enough that they deserve some support with a terse syntax. --bb
Dec 17 2009
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Bill Baxter wrote:
 On Thu, Dec 17, 2009 at 9:28 AM, dsimcha <dsimcha yahoo.com> wrote:
 == Quote from Nick Sabalausky (a a.a)'s article
 Pardon my ignorance, but why is it that templated functions can't be
 virtual?
This is an unfortunate consequence of compilation model leaking out into language design. You're supposed to be able to subclass a base class even if you don't have the full source code to it. Let's say we have: class A { void doStuff(T)(T arg) {} } class B : A { void doStuff(T)(T arg) { writeln("In B."); } } And then we do: B b = new B; b.doStuff(1); The instantiation of B.doStuff() with an int would require that the vtable for A be updated to include doStuff!(int). This is not possible if we also allow base classes to be compiled separately from derived classes and the code that uses the derived class. The only solution I see would be to completely get rid of separate compilation. For my personal use, since most of my projects are under 10k lines and take a negligible amount of time to compile anyhow, I'd be in favor of this. However, for larger projects or projects that require things to work based only on interface, without access to the full source code, this is probably impractical.
This 'auto ref' stuff and the multi-flavor 'vconst' functions are basically templates with a known list of instantiations (ref /no-ref, and const/immutable/plain) In theory there's no reason you couldn't allow templates to also create virtual functions, if you limit yourself to listing the possible instantiations up front.
This notion of templates "restricted enough" to be virtual has been intensively discussed a couple of years ago between Walter, Bartosz and myself. Back then it was much less clear where to draw the line, but right now the idea is well worth revisiting. Andrei
Dec 17 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 17 Dec 2009 13:03:51 -0500, Bill Baxter <wbaxter gmail.com> wrote:

 This 'auto ref' stuff and the multi-flavor 'vconst' functions are
 basically templates with a known list of instantiations (ref /no-ref,
 and const/immutable/plain)
 In theory there's no reason you couldn't allow templates to also
 create virtual functions, if you limit yourself to listing the
 possible instantiations up front.
Technically, the vconst functions provide one other feature -- implicit casting back to the correct type. You can't support that with templates, and still have the compiler ensure const is obeyed during the function all with a function signature. Also, there is only ever a single vconst function, not 3^n overloaded ones. But I agree with allowing a restricted template to be able to be virtual. One recent discussion that applies here is templatized operator overloading -- wouldn't it be nice if: opBinary(op : "+")(T rhs) {...} could be a virtual function? That gives us immediate support for virtual functions without the hoaky: opBinary(op : "+")(T rhs) {return opAdd(rhs);} mixin boilerplate. (BTW, this was Denis Koroshin's idea, not mine) -Steve
Dec 17 2009
prev sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
dsimcha:

The only solution I see would be to completely get rid of separate compilation.<
"All problems in computer science can be solved by another level of indirection;" -- David Wheeler But that also slows code down a little :-) -------------------- Bill Baxter: Static interfaces are an easy idea, it was discussed some weeks ago, and probably it's not too much hard to implement in the language. I like them enough, but they don't add that much to the template constraints already present, so it's mostly a duplication of syntax and semantics. So I am not sure they are a good idea.
static interface VConst(T) {
constnessOf(T) is in [const, immutable, !const]; } VConst!(Bar) doSomething(VConst!(Bar) b, int y) {...} static interface MaybeRef(T) { refnessOf(T) is in [ref, !ref]; } MaybeRef!(Bar) doSomethingElse(MaybeRef!(Bar) b, int y) { ... }< Walter has just added traits to perform this, so I think this is already doable, with __trait/meta. and template constraints. The opIn_r defined for arrays is something that D2 must eventually have, there's no doubt about this. But "is in", followed by an array of those modifiers is currently impossible (you may create a tuple of templates, where each template tests for constness, etc). Maybe in future you can create an array of annotations: [ const, immutable, notConst] Bye, bearophile
Dec 17 2009
parent reply Bill Baxter <wbaxter gmail.com> writes:
On Thu, Dec 17, 2009 at 11:41 AM, bearophile <bearophileHUGS lycos.com> wro=
te:
 dsimcha:

The only solution I see would be to completely get rid of separate compil=
ation.<
 "All problems in computer science can be solved by another level of indir=
ection;"
 -- David Wheeler
 But that also slows code down a little :-)

 --------------------

 Bill Baxter:

 Static interfaces are an easy idea, it was discussed some weeks ago, and =
probably it's not too much hard to implement in the language. I like them e= nough, but they don't add that much to the template constraints already pre= sent, so it's mostly a duplication of syntax and semantics. So I am not sur= e they are a good idea. My reason for bringing them up was to say that perhaps they could be carried beyond that simple idea.
static interface VConst(T) {
=A0 constnessOf(T) is in [const, immutable, !const]; } VConst!(Bar) doSomething(VConst!(Bar) b, int y) {...} static interface MaybeRef(T) { =A0 refnessOf(T) is in [ref, !ref]; } MaybeRef!(Bar) doSomethingElse(MaybeRef!(Bar) b, int y) { ... }< Walter has just added traits to perform this, so I think this is already =
doable, with __trait/meta. and template constraints.
 The opIn_r defined for arrays is something that D2 must eventually have, =
there's no doubt about this.
 But "is in", followed by an array of those modifiers is currently impossi=
ble (you may create a tuple of templates, where each template tests for con= stness, etc). Maybe in future you can create an array of annotations:
 [ const,  immutable,  notConst]
Yeh, I know what I wrote is impossible now. Just putting the idea out there because it seems like we're heading for a place where we solve the problem of generating multiple versions of a function in three completely different ways (auto ref, vconst and of course templates) [four if you count generating them with string mixins]. I don't know what the proper generalization is, but I think if you try to keep templates the way they are while generalizing them to handle these cases you will end up with an unworkable notation nightmare. I like the way Go static interfaces refactor template specifications to simplify the overall syntax. In WalterAndrei.pdf from the D conference there was talk of static parameters and of unifying regular functions and templates. The Go approach basically accomplishes that by associating a symbol with all the template specification baggage. Then just by using that symbol in the parameter list of an ordinary-looking function, the compiler can tell not only 'hey we've got a template' but also what types of constraints apply. In a sense, current templates are analogous to structures that require you to spell out the members of the struct every time you use one. For structs we can do: struct Complex { float re; float im; }; Complex addComplex(Complex a, Complex b); But imagine if you had to write that always as template isComplex(T) { enum isComplex =3D __traits(hasMember,T,"re") && __traits(hasMember,T,"im") && typeof(T.init.im =3D=3D float) && typeof(T.init.re =3D= =3D float); } T addComplex(T)(T a, T b) if (IsComplex!(T)) { ... } The current system forces you to do that the instant your constraint becomes more complex than "a struct that has these members of these types in this particular order". Instead, Go encodes the complexity in a reusable symbol that acts like a type name, but it contains richer, more generic information than a regular type. A big thing lacking is that Go doesn't give you a way to express constraints between types. Like with: T index(T,S)(T[S] x, S y) { ... } But I think you could devise a way to encompass such cases. Maybe something like: static interface Indexable { Indexed :=3D Indexable[Index]; } Indexable.Indexed index(Indexable x, Indexable.Index y) { ... } (Note this is more general than the above T[S] template which only matches built-in AA's) This is clearly too much for D2 to swallow at this point. But I think it's worth thinking about whether there may be a significantly better way to specify parametrized types and constraints on them than what C++ and D use. --bb
Dec 18 2009
parent bearophile <bearophileHUGS lycos.com> writes:
Bill Baxter:
 I don't know
 what the proper generalization is, but I think if you try to keep
 templates the way they are while generalizing them to handle these
 cases you will end up with an unworkable notation nightmare.
It's not easy to design such large changes up-front (Andrei has for example done it with Ranges, but only few people have enough brain to do that and produce a final result that's useful in practice), so I think the design strategy used here is: try to generalize templates to address those cases, produce a too much high castle of cards that you can see is unstable, then try to fix the mess redesigning using a more clean design. The key here is to keep the D3 design process flexible enough, so you can fix design mistakes along the way ;-) It's an iterative process of generalization & abstraction.
 In WalterAndrei.pdf from the D conference there was talk of static
 parameters and of unifying regular functions and templates.
I think Walter has abandoned that idea, for never specified implementation difficulties. I'd like to know more about those difficulties (but I agree that if an implementation is too much hard to do compared to the gains it gives, then it's better to abandon it).
 But I think
 it's worth thinking about whether there may be a significantly better
 way to specify parametrized types and constraints on them than what
 C++ and D use.
Of course. But you may need to implement a more powerful type system to do that. Bye, bearophile
Dec 18 2009
prev sibling parent reply "Nick Sabalausky" <a a.a> writes:
"dsimcha" <dsimcha yahoo.com> wrote in message 
news:hgdpn2$t1c$1 digitalmars.com...
 == Quote from Nick Sabalausky (a a.a)'s article
 Pardon my ignorance, but why is it that templated functions can't be
 virtual?
This is an unfortunate consequence of compilation model leaking out into language design. You're supposed to be able to subclass a base class even if you don't have the full source code to it. Let's say we have: class A { void doStuff(T)(T arg) {} } class B : A { void doStuff(T)(T arg) { writeln("In B."); } } And then we do: B b = new B; b.doStuff(1); The instantiation of B.doStuff() with an int would require that the vtable for A be updated to include doStuff!(int). This is not possible if we also allow base classes to be compiled separately from derived classes and the code that uses the derived class. The only solution I see would be to completely get rid of separate compilation. For my personal use, since most of my projects are under 10k lines and take a negligible amount of time to compile anyhow, I'd be in favor of this. However, for larger projects or projects that require things to work based only on interface, without access to the full source code, this is probably impractical.
But we already can't instantiate templates without the source anyway, so I don't see how doing that vtable update for A would create any additional requirements on source availability that we don't already have, regardless of whether or not the function is virtual. Either way, the issue of "Do I need to provide the source?" still boils down to just "Am I defining a public template?" and is not at all dependent on "Is this going to be externally subclassed?".
Dec 18 2009
parent dsimcha <dsimcha yahoo.com> writes:
== Quote from Nick Sabalausky (a a.a)'s article
 "dsimcha" <dsimcha yahoo.com> wrote in message
 news:hgdpn2$t1c$1 digitalmars.com...
 == Quote from Nick Sabalausky (a a.a)'s article
 Pardon my ignorance, but why is it that templated functions can't be
 virtual?
This is an unfortunate consequence of compilation model leaking out into language design. You're supposed to be able to subclass a base class even if you don't have the full source code to it. Let's say we have: class A { void doStuff(T)(T arg) {} } class B : A { void doStuff(T)(T arg) { writeln("In B."); } } And then we do: B b = new B; b.doStuff(1); The instantiation of B.doStuff() with an int would require that the vtable for A be updated to include doStuff!(int). This is not possible if we also allow base classes to be compiled separately from derived classes and the code that uses the derived class. The only solution I see would be to completely get rid of separate compilation. For my personal use, since most of my projects are under 10k lines and take a negligible amount of time to compile anyhow, I'd be in favor of this. However, for larger projects or projects that require things to work based only on interface, without access to the full source code, this is probably impractical.
But we already can't instantiate templates without the source anyway, so I don't see how doing that vtable update for A would create any additional requirements on source availability that we don't already have, regardless of whether or not the function is virtual. Either way, the issue of "Do I need to provide the source?" still boils down to just "Am I defining a public template?" and is not at all dependent on "Is this going to be externally subclassed?".
To clarify, the requirement is somewhat stronger than having the source. A, B, and all code that instantiates template methods of A or B would probably all have to be compiled as one compilation unit to get the vtable layout right.
Dec 18 2009
prev sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 16 Dec 2009 19:05:09 -0500, Michel Fortin  
<michel.fortin michelf.com> wrote:

 On 2009-12-16 16:46:14 -0500, Jason House <jason.james.house gmail.com>  
 said:

 Walter Bright Wrote:

 Jason House wrote:
 KennyTM~ Wrote:

 auto const?
I was wondering the same thing.
The const transport thing is, unfortunately, a very different problem.
Of course, but it may still go through bikeshed issues. This morning I read about inout, return, vconst, aconst, sameconst, autoconst, auto const, and bikeshed. At least one of those was in jest :) auto const isn't that bad, and you obviously liked auto ref...
Since this is just a special kind of const, it could be called "const^" (const or a derived constness): const?(Object) func(const?(Object) o) { return o; } The interesting thing about it, beside not taking a keyword, is that it can scale in the future if we need to add many distinct constness to the same function signature: const?(Object) func(const?(Object) o, const?2(Object) o2, out const?2(Object) o3) { o3 = o2; return o; }
This can never work. a const?(Object) is const during the function execution, and cannot be assigned to.
 Not that you'd need that often, but if it does becomes necessary in the  
 future we'll still have some options.

 Furthermore, the concept could be extended to any type. This could be  
 useful with class hierarchies:

 	Object? func(Object? o) {
 		writeln(o.toString());
 		return o;
 	}

 	MyObject o = func(new MyObject);

 Here, "Object?" means Object or a derived type.
This doesn't have the same utility as vconst, since you can't apply Object to other types like you can constancy. Plus you can already do this with a template and have a virtual version of the func: T func(T)(T o) if(T : Object) {func_virt(o); return o; } protected void func_virt(Object o) {writeln(o.toString());} -Steve
Dec 17 2009
parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2009-12-17 14:52:40 -0500, "Steven Schveighoffer" 
<schveiguy yahoo.com> said:

 The interesting thing about it, beside not taking a keyword, is that it 
  can scale in the future if we need to add many distinct constness to 
 the  same function signature:
 
 	const?(Object) func(const?(Object) o, const?2(Object) o2, out  
 const?2(Object) o3) {
 		o3 = o2;
 		return o;
 	}
This can never work. a const?(Object) is const during the function execution, and cannot be assigned to.
I'm not sure why, but I always forget that const(Object) is not rebindable. My mistake, here is the corrected example: const?(MyStruct)* func(const?(MyStruct)* s, const?2(MyStruct)* s2, const?2(MyStruct)* s3) { o2 = o3; return s; }
 Furthermore, the concept could be extended to any type. This could be  
 useful with class hierarchies:
 
 	Object? func(Object? o) {
 		writeln(o.toString());
 		return o;
 	}
 
 	MyObject o = func(new MyObject);
 
 Here, "Object?" means Object or a derived type.
This doesn't have the same utility as vconst, since you can't apply Object to other types like you can constancy. Plus you can already do this with a template and have a virtual version of the func: T func(T)(T o) if(T : Object) {func_virt(o); return o; } protected void func_virt(Object o) {writeln(o.toString());}
Indeed. I was mostly trying to show that the "const?" notation can easily be extended to all sort of things, which makes it a better choice than other notations. We could do the same with an old idea that didn't get in the language: scope arguments. // *a and *b are in the same scope, so you can swap a and b void swap(scope?(int)* a, scope?(int)* b) { int tmp = a; a = b; b = tmp; } But the scope problem would require more thought. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Dec 17 2009
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 17 Dec 2009 15:17:22 -0500, Michel Fortin  
<michel.fortin michelf.com> wrote:

 On 2009-12-17 14:52:40 -0500, "Steven Schveighoffer"  
 <schveiguy yahoo.com> said:

 The interesting thing about it, beside not taking a keyword, is that  
 it  can scale in the future if we need to add many distinct constness  
 to the  same function signature:
  	const?(Object) func(const?(Object) o, const?2(Object) o2, out   
 const?2(Object) o3) {
 		o3 = o2;
 		return o;
 	}
This can never work. a const?(Object) is const during the function execution, and cannot be assigned to.
I'm not sure why, but I always forget that const(Object) is not rebindable. My mistake, here is the corrected example: const?(MyStruct)* func(const?(MyStruct)* s, const?2(MyStruct)* s2, const?2(MyStruct)* s3) { o2 = o3; return s; }
That doesn't do anything :) It may as well be written: const?(MyStruct)* func(const?(MyStruct)* s) { return s; } If you want something like this: const?(MyStruct)* func(const?(MyStruct)* s, const?2(MyStruct)** s2, const?2(MyStruct)** s3) { *s2 = *s3; return s; } This will not accept any args except for const?(MyStruct)** for s2 and s3. That is, you cannot pass a MyStruct** or a const(MyStruct)** or an immutable(MyStruct)** because it is 2 levels of indirection (this is the test we ran earlier).
 Furthermore, the concept could be extended to any type. This could be   
 useful with class hierarchies:
  	Object? func(Object? o) {
 		writeln(o.toString());
 		return o;
 	}
  	MyObject o = func(new MyObject);
  Here, "Object?" means Object or a derived type.
This doesn't have the same utility as vconst, since you can't apply Object to other types like you can constancy. Plus you can already do this with a template and have a virtual version of the func: T func(T)(T o) if(T : Object) {func_virt(o); return o; } protected void func_virt(Object o) {writeln(o.toString());}
Indeed. I was mostly trying to show that the "const?" notation can easily be extended to all sort of things, which makes it a better choice than other notations. We could do the same with an old idea that didn't get in the language: scope arguments. // *a and *b are in the same scope, so you can swap a and b void swap(scope?(int)* a, scope?(int)* b) { int tmp = a; a = b; b = tmp; } But the scope problem would require more thought.
OK, that makes more sense. Except scope is not a type constructor (yet) :) -Steve
Dec 17 2009
prev sibling parent reply "Simen kjaeraas" <simen.kjaras gmail.com> writes:
On Wed, 16 Dec 2009 12:00:46 +0100, KennyTM~ <kennytm gmail.com> wrote:

 On Dec 16, 09 15:18, Walter Bright wrote:
 There's a need in generic code to have a function take a parameter by
 ref if it is an lvalue, and by value if it is an rvalue. This can be
 addressed by making it a template using auto ref:

 T foo(T)(auto ref T x) { ... }

 foo(3) // call by value
 int y;
 foo(y) // call by reference

 There is also a need to 'transmit' the ref'ness to the return value.
 This can be done with auto ref:

 auto ref foo(T)(auto ref T x) { return x; }

 foo(3) => int foo(int x)
 foo(y) => ref int foo(ref int x)

 This means that the generic forwarding function would look like:

 auto ref foo(alias F, T...)(auto ref T args) { return F(args); }
auto const?
auto const auto ref Foo bar( auto const auto ref Foo arg ) { return arg; } Am I the only one who finds this confusing? -- Simen
Dec 16 2009
parent reply Leandro Lucarella <llucax gmail.com> writes:
Simen kjaeraas, el 17 de diciembre a las 02:16 me escribiste:
 On Wed, 16 Dec 2009 12:00:46 +0100, KennyTM~ <kennytm gmail.com> wrote:
 
On Dec 16, 09 15:18, Walter Bright wrote:
There's a need in generic code to have a function take a parameter by
ref if it is an lvalue, and by value if it is an rvalue. This can be
addressed by making it a template using auto ref:

T foo(T)(auto ref T x) { ... }

foo(3) // call by value
int y;
foo(y) // call by reference

There is also a need to 'transmit' the ref'ness to the return value.
This can be done with auto ref:

auto ref foo(T)(auto ref T x) { return x; }

foo(3) => int foo(int x)
foo(y) => ref int foo(ref int x)

This means that the generic forwarding function would look like:

auto ref foo(alias F, T...)(auto ref T args) { return F(args); }
auto const?
auto const auto ref Foo bar( auto const auto ref Foo arg ) { return arg; } Am I the only one who finds this confusing?
Just call "auto const auto ref" "auto auto": auto auto Foo bar( auto auto Foo arg ) { return arg; } =P -- Leandro Lucarella (AKA luca) http://llucax.com.ar/ ---------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------- We are born naked, wet and hungry Then things get worse
Dec 17 2009
parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
On Thu, 17 Dec 2009 17:26:52 +0100, Leandro Lucarella <llucax gmail.com>  
wrote:

 Simen kjaeraas, el 17 de diciembre a las 02:16 me escribiste:
 On Wed, 16 Dec 2009 12:00:46 +0100, KennyTM~ <kennytm gmail.com> wrote:

On Dec 16, 09 15:18, Walter Bright wrote:
There's a need in generic code to have a function take a parameter by
ref if it is an lvalue, and by value if it is an rvalue. This can be
addressed by making it a template using auto ref:

T foo(T)(auto ref T x) { ... }

foo(3) // call by value
int y;
foo(y) // call by reference

There is also a need to 'transmit' the ref'ness to the return value.
This can be done with auto ref:

auto ref foo(T)(auto ref T x) { return x; }

foo(3) => int foo(int x)
foo(y) => ref int foo(ref int x)

This means that the generic forwarding function would look like:

auto ref foo(alias F, T...)(auto ref T args) { return F(args); }
auto const?
auto const auto ref Foo bar( auto const auto ref Foo arg ) { return arg; } Am I the only one who finds this confusing?
Just call "auto const auto ref" "auto auto": auto auto Foo bar( auto auto Foo arg ) { return arg; } =P
Even better, with return type inference: auto auto auto bar( T )( auto auto T arg ) { return arg; } :p -- Simen
Dec 17 2009