digitalmars.D - tail const
- Fawzi Mohamed (29/29) Nov 30 2010 Speaking about D mistakes Steve spoke about missing tail const.
- so (9/22) Dec 01 2010 Sorry if i am overlooking something but if we are going that far, why no...
- Jonathan M Davis (12/49) Dec 01 2010 Various syntaxes have been proposed in the past. Syntax isn't really the...
- so (4/18) Dec 01 2010 Oh I see, reading Steven's post cleared it.
- Michel Fortin (14/36) Dec 01 2010 Well... I just took a quick look at the problem from inside the
- Michel Fortin (7/41) Dec 01 2010 Turns out it's there's a trick that makes it much simpler than I
- Fawzi Mohamed (33/71) Dec 02 2010 great!
- Michel Fortin (18/23) Dec 02 2010 It is significantly more complex, not only for the compiler but also
- Fawzi Mohamed (5/28) Dec 02 2010 ok, eheh I just realized that also the tail shared protection has
- Steven Schveighoffer (18/37) Dec 02 2010 Where it would be beneficial is in mimicking the tail-const properties o...
- Michel Fortin (7/36) Dec 02 2010 I'm not sure I get the problem. Can you show me in code?
- Simen kjaeraas (13/14) Dec 02 2010 const a = map!"a+a"( [1,2,3] );
- Andrei Alexandrescu (9/21) Dec 02 2010 Well the code asks for a constant object, and I don't see it as
- Simen kjaeraas (51/76) Dec 03 2010 True. I believe I was thinking that T should be implicitly convertible
- Simen kjaeraas (5/11) Dec 04 2010 To expound further on this, I have created the attached module.
- Andrei Alexandrescu (11/20) Dec 04 2010 Looks promising. A few comments.
- Simen kjaeraas (23/36) Dec 05 2010 It adds some .'s:
- Simen kjaeraas (8/12) Dec 05 2010 A closer look at this reveals that it won't work that simply, because
- Simen kjaeraas (11/22) Dec 26 2010 After more problems, I have also come to the conclusion that what is
- Simen kjaeraas (7/15) Dec 26 2010 One might then think (I certainly did) that Tail(((Im|M)utable)|Const)
- Simen kjaeraas (5/42) Dec 15 2010 Nobody ever commented on this. Figured I'd poke around with a stick
- Steven Schveighoffer (6/31) Dec 03 2010 Now, change data to an SList range. This is what I'm talking about.
- Steven Schveighoffer (60/84) Dec 03 2010 Here is an example range from dcollections (well, at least the pertinant...
- Andrei Alexandrescu (10/44) Dec 03 2010 I was about to post a similar analysis, but my suggested conclusion is
- Steven Schveighoffer (21/66) Dec 03 2010 I had not thought of it this way. I assumed you would only need to defi...
- Steven Schveighoffer (9/50) Dec 03 2010 BTW, even though I conceed that my ideas are too complex to be worth
- Andrei Alexandrescu (4/10) Dec 03 2010 I believe this is the level of understanding and the kind of attitude
- Fawzi Mohamed (37/92) Dec 04 2010 I fully agree with this.
- Fawzi Mohamed (15/19) Dec 05 2010 This was thought a bit as provocation, but as nobody reacted, and with
- Michel Fortin (24/99) Dec 03 2010 A fine explanation. Thank you.
- Steven Schveighoffer (40/58) Dec 03 2010 Yes, Andrei also pointed out this problem. Part of the issue is that
- Michel Fortin (17/56) Dec 03 2010 Just like you, I don't think you should be able to implicitly cast
- Steven Schveighoffer (7/51) Dec 03 2010 Oh, I misread your original idea, sorry. I like the idea.
- Michel Fortin (8/64) Dec 03 2010 Yes, by "same memory layout" I mean the same variables, with the same
- Andrei Alexandrescu (12/16) Dec 03 2010 I'm afraid that can't work.
- Dmitry Olshansky (11/26) Dec 03 2010 Looks discouraging at first, but perfectly valid given that the example
- Michel Fortin (19/49) Dec 04 2010 Yes, indeed. I was a little over-enthusiastic when saying it'd work for
- Michel Fortin (9/57) Dec 04 2010 That wasn't very well put. Here I was looking only at the obj field. As
- Andrei Alexandrescu (16/43) Dec 04 2010 If conversion is allowed only for values (i.e. perform a memberwise copy...
- Michel Fortin (20/37) Dec 04 2010 Copying a struct always bypasses the constructor, whether it's a
- Andrei Alexandrescu (7/42) Dec 04 2010 The postblit constructor assumes the source and target types are the sam...
- Michel Fortin (19/69) Dec 04 2010 Well, sometime this assumption is also barrier that gets in the way.
- Dmitry Olshansky (9/29) Dec 04 2010 But I observe there still may be some rough edges Andrei mentioned about...
- Andrei Alexandrescu (8/75) Dec 04 2010 Nagonna work. Any method inside A assumes certain types, you can't
- Steven Schveighoffer (16/60) Dec 04 2010 These are the kinds of things I am afraid of with static if. Because we...
- spir (10/24) Dec 01 2010 issue.=20
- Michel Fortin (9/31) Dec 01 2010 Yes. "C* b" already has the meaning of a pointer to a class reference,
- vincent picaud (34/36) Dec 01 2010 Hello community, that is my first post here.
- vincent picaud (25/25) Dec 01 2010 Reading back my first post I realized that there are a lot of confusions...
- Simen kjaeraas (8/14) Dec 01 2010 The issue is not with syntax (I am of the impression that most who want
- Jonathan M Davis (7/28) Dec 01 2010 * has nothing to do with references. * is for pointers. We're dealing wi...
Speaking about D mistakes Steve spoke about missing tail const. I was thinking about this, and I fully agree that it is a hole. I don't know if it was already discussed, but I was thinking that one could introduce *const T t1; and *immutable T t2; with the following meaning: const or immutable is applied on the "dereferenced" type. For classes this would mean the object, not the pointer. Thus for example if T is a class type one would be able to reassign t1 t1=t2; but not to modify the content of t1 or t2 in any way. One can also extend it to array types: if T is U[], then it would mean const(U)[] or immutable(U)[], and to pointer types, *const int* would then mean const(int)*. For other types maybe the best solution would be to drop the const/immutable for basic types, functions and delegates apply it to all fields of a struct (not sure how much work this would be to implement) This use of * should not introduce much ambiguity (a pointer is T*, indeed also const*T would be almost as unambiguos). One can see that this tail const is really a common type, indeed string is such a type, and a function can be pure even if its arguments is *immutable, because any changes would be done to a local copy in the function. I think that these things point toward the usefulness of a *const and *immutable attributes. Fawzi
Nov 30 2010
Speaking about D mistakes Steve spoke about missing tail const. I was thinking about this, and I fully agree that it is a hole. I don't know if it was already discussed, but I was thinking that one could introduce *const T t1; and *immutable T t2;Sorry if i am overlooking something but if we are going that far, why not just : const(int)* p; // tail const pointer - already here const(int)& r; // tail const reference - will be introduced and quite straightforward.One can see that this tail const is really a common type, indeed string is such a type, and a function can be pure even if its arguments is *immutable, because any changes would be done to a local copy in the function. I think that these things point toward the usefulness of a *const and *immutable attributes.It is indeed common and IMHO it is the biggest reason why pointers are still used too much in C++ where references should be the obvious choice. -- Using Opera's revolutionary email client: http://www.opera.com/mail/
Dec 01 2010
On Wednesday 01 December 2010 02:56:58 so wrote:Various syntaxes have been proposed in the past. Syntax isn't really the issue. It's pretty easy to come up with one. I think that out of the ones I've seen, the I liked the best was the one proposed by Michel Fortin:Speaking about D mistakes Steve spoke about missing tail const. I was thinking about this, and I fully agree that it is a hole. I don't know if it was already discussed, but I was thinking that one could introduce *const T t1; and *immutable T t2;Sorry if i am overlooking something but if we are going that far, why not just : const(int)* p; // tail const pointer - already here const(int)& r; // tail const reference - will be introduced and quite straightforward.One can see that this tail const is really a common type, indeed string is such a type, and a function can be pure even if its arguments is *immutable, because any changes would be done to a local copy in the function. I think that these things point toward the usefulness of a *const and *immutable attributes.It is indeed common and IMHO it is the biggest reason why pointers are still used too much in C++ where references should be the obvious choice.I proposed the following a while ago. First allow the class reference to (optionally) be made explicit: C a; // mutable reference to mutable class C ref b; // mutable reference to mutable class And now you can apply tail-const to it: const(C)ref c; // mutable reference to const class const(C ref) d; // const reference to const class const(C) e; // const reference to const classThe real issue is not syntax but getting it into the compiler. Apparently, there are difficulties in implementing tail const in the compiler which made Walter give up on it in the past. It should be doable, but Walter is totally sick of the issue and doesn't want to put the time in to do it - he has plenty on his plate as it is. So, if it's going to be done, someone else has to step up to the plate and do it. And with the general lack of dmd developers, that hasn't happened. No one thus far has had both the inclination and the time. - Jonathan M Davis
Dec 01 2010
The real issue is not syntax but getting it into the compiler. Apparently, there are difficulties in implementing tail const in the compiler which made Walter give up on it in the past. It should be doable, but Walter is totally sick of the issue and doesn't want to put the time in to do it - he has plenty on his plate as it is. So, if it's going to be done, someone else has to step up to the plate and do it. And with the general lack of dmd developers, that hasn't happened. No one thus far has had both the inclination and the time. - Jonathan M DavisOh I see, reading Steven's post cleared it. Thanks! -- Using Opera's revolutionary email client: http://www.opera.com/mail/
Dec 01 2010
On 2010-12-01 06:17:24 -0500, Jonathan M Davis <jmdavisProg gmx.com> said:Well... I just took a quick look at the problem from inside the compiler. The issue is this: the compiler has a type hierarchy, and TypeClass is one type in it. There is no separate type for a class reference, it just uses TypeClass do designate a class reference, which means that if your TypeClass has the const or immutable modifier, so does your reference. So either we create a TypeClassRef to designate the reference, or we add additional flags to TypeClass for the reference's modifier; in either case many parts of the semantic analysis has to be revised to take this into account. -- Michel Fortin michel.fortin michelf.com http://michelf.com/I proposed the following a while ago. First allow the class reference to (optionally) be made explicit: C a; // mutable reference to mutable class C ref b; // mutable reference to mutable class And now you can apply tail-const to it: const(C)ref c; // mutable reference to const class const(C ref) d; // const reference to const class const(C) e; // const reference to const classThe real issue is not syntax but getting it into the compiler. Apparently, there are difficulties in implementing tail const in the compiler which made Walter give up on it in the past. It should be doable, but Walter is totally sick of the issue and doesn't want to put the time in to do it - he has plenty on his plate as it is. So, if it's going to be done, someone else has to step up to the plate and do it. And with the general lack of dmd developers, that hasn't happened. No one thus far has had both the inclination and the time.
Dec 01 2010
On 2010-12-01 09:37:08 -0500, Michel Fortin <michel.fortin michelf.com> said:On 2010-12-01 06:17:24 -0500, Jonathan M Davis <jmdavisProg gmx.com> said:Turns out it's there's a trick that makes it much simpler than I expected. Patch coming soon. ;-) -- Michel Fortin michel.fortin michelf.com http://michelf.com/Well... I just took a quick look at the problem from inside the compiler. The issue is this: the compiler has a type hierarchy, and TypeClass is one type in it. There is no separate type for a class reference, it just uses TypeClass do designate a class reference, which means that if your TypeClass has the const or immutable modifier, so does your reference. So either we create a TypeClassRef to designate the reference, or we add additional flags to TypeClass for the reference's modifier; in either case many parts of the semantic analysis has to be revised to take this into account.I proposed the following a while ago. First allow the class reference to (optionally) be made explicit: C a; // mutable reference to mutable class C ref b; // mutable reference to mutable class And now you can apply tail-const to it: const(C)ref c; // mutable reference to const class const(C ref) d; // const reference to const class const(C) e; // const reference to const classThe real issue is not syntax but getting it into the compiler. Apparently, there are difficulties in implementing tail const in the compiler which made Walter give up on it in the past. It should be doable, but Walter is totally sick of the issue and doesn't want to put the time in to do it - he has plenty on his plate as it is. So, if it's going to be done, someone else has to step up to the plate and do it. And with the general lack of dmd developers, that hasn't happened. No one thus far has had both the inclination and the time.
Dec 01 2010
On 1-dic-10, at 20:07, Michel Fortin wrote:On 2010-12-01 09:37:08 -0500, Michel Fortin <michel.fortin michelf.com> said:great! well as your are at it I would argue a bit more on the syntax. In my opinion it is useful more useful to have a weak_const, (or tail const , or *const, I don't care so much about the syntax, but I care about the concept), like I sketched in my post, and not just fix the class issue. Indeed as I did try to argue it is useful to have an easy way to say "all my local stack memory might be modified, but not anything that it refers to" (thus weak const). This is the maximum modifiability that one can allow to arguments to pure functions, so a very useful level of protection. weak_const can be defined recursively: weak_const T is - const(T) if T is a reference, a D has not rebinding of refs (otherwise it should protect only the object, not the rebinding of the ref). - T if T is a basic type, function or delegate - const(U)* if is(T U==U*) - const(U)[] if is(T U==U[]) // this is a special case of the next - WeakConst!(T) if (T==struct) where WeakConst!(T) is a structure like T, but where all its fields are weak_const (i.e. apply recursively weak_const to the content of the structure. Indeed the recursion on the structure is the most complex thing, and might be an implementation challenge, but if doable it would be very nice. Basically one has to set a flag for all things that are local, and would not be affected by the weak const. I suppose that will probably considered too difficult to implement, but I wanted to propose it again because I find that it is the most clean solution conceptually. FawziOn 2010-12-01 06:17:24 -0500, Jonathan M Davis <jmdavisProg gmx.com> said:Turns out it's there's a trick that makes it much simpler than I expected. Patch coming soon. ;-)Well... I just took a quick look at the problem from inside the compiler. The issue is this: the compiler has a type hierarchy, and TypeClass is one type in it. There is no separate type for a class reference, it just uses TypeClass do designate a class reference, which means that if your TypeClass has the const or immutable modifier, so does your reference. So either we create a TypeClassRef to designate the reference, or we add additional flags to TypeClass for the reference's modifier; in either case many parts of the semantic analysis has to be revised to take this into account.I proposed the following a while ago. First allow the class reference to (optionally) be made explicit: C a; // mutable reference to mutable class C ref b; // mutable reference to mutable class And now you can apply tail-const to it: const(C)ref c; // mutable reference to const class const(C ref) d; // const reference to const class const(C) e; // const reference to const classThe real issue is not syntax but getting it into the compiler. Apparently, there are difficulties in implementing tail const in the compiler which made Walter give up on it in the past. It should be doable, but Walter is totally sick of the issue and doesn't want to put the time in to do it - he has plenty on his plate as it is. So, if it's going to be done, someone else has to step up to the plate and do it. And with the general lack of dmd developers, that hasn't happened. No one thus far has had both the inclination and the time.
Dec 02 2010
On 2010-12-02 05:57:18 -0500, Fawzi Mohamed <fawzi gmx.ch> said:well as your are at it I would argue a bit more on the syntax. [...] I suppose that will probably considered too difficult to implement, but I wanted to propose it again because I find that it is the most clean solution conceptually.It is significantly more complex, not only for the compiler but also for the one reading/writing the code, as you'd have to propagate that 'weak_const' as a new, distinct modifier for it to be usable across function calls. I don't think it's worth it really. As for the syntax for classes, I feel "const(Object)ref" with the optional ref marker is easier to grasp than introducing a new concept called 'weak_const'. I welcome any suggestions, but my aim is to keep the changes as small and localized as possible in the compiler and follow as closely as possible existing language patterns. My only concern with the "const(Object)ref" syntax is that we're reusing 'ref' to denote an object reference with different properties (rebindable, nullable) than what 'ref' currently stands for. But it remains the best syntax I've seen so far. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Dec 02 2010
On 2-dic-10, at 13:09, Michel Fortin wrote:On 2010-12-02 05:57:18 -0500, Fawzi Mohamed <fawzi gmx.ch> said:ok, eheh I just realized that also the tail shared protection has exactly the same constraints as the weak const (or tail const), and also for that it seems that the more complex struct case was scrapped, restricting it to pointer array and refs.well as your are at it I would argue a bit more on the syntax. [...] I suppose that will probably considered too difficult to implement, but I wanted to propose it again because I find that it is the most clean solution conceptually.It is significantly more complex, not only for the compiler but also for the one reading/writing the code, as you'd have to propagate that 'weak_const' as a new, distinct modifier for it to be usable across function calls. I don't think it's worth it really.As for the syntax for classes, I feel "const(Object)ref" with the optional ref marker is easier to grasp than introducing a new concept called 'weak_const'. I welcome any suggestions, but my aim is to keep the changes as small and localized as possible in the compiler and follow as closely as possible existing language patterns. My only concern with the "const(Object)ref" syntax is that we're reusing 'ref' to denote an object reference with different properties (rebindable, nullable) than what 'ref' currently stands for. But it remains the best syntax I've seen so far. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Dec 02 2010
On Thu, 02 Dec 2010 07:09:27 -0500, Michel Fortin <michel.fortin michelf.com> wrote:On 2010-12-02 05:57:18 -0500, Fawzi Mohamed <fawzi gmx.ch> said:Where it would be beneficial is in mimicking the tail-const properties of arrays in generic ranges. I have a container C, which defines a range over its elements R. const(R) is not a usable range, because popFront cannot be const. So now I need to define constR, which is identical to R, except the front() function returns a const element. So now, I need the same for immutable. And now I need to triplicate all my functions which accept the ranges, or return them. And I can't use inout(R) as a return value for ranges. If you can solve the general problem, and not just the class tail-const, it would be hugely beneficial. My thought was that a modifier on const itself could be stored in the TypeInfo_Const as a boolean (tail or not), and the equivalent done in dmd source itself. -Stevewell as your are at it I would argue a bit more on the syntax. [...] I suppose that will probably considered too difficult to implement, but I wanted to propose it again because I find that it is the most clean solution conceptually.It is significantly more complex, not only for the compiler but also for the one reading/writing the code, as you'd have to propagate that 'weak_const' as a new, distinct modifier for it to be usable across function calls. I don't think it's worth it really. As for the syntax for classes, I feel "const(Object)ref" with the optional ref marker is easier to grasp than introducing a new concept called 'weak_const'. I welcome any suggestions, but my aim is to keep the changes as small and localized as possible in the compiler and follow as closely as possible existing language patterns. My only concern with the "const(Object)ref" syntax is that we're reusing 'ref' to denote an object reference with different properties (rebindable, nullable) than what 'ref' currently stands for. But it remains the best syntax I've seen so far.
Dec 02 2010
On 2010-12-02 16:14:58 -0500, "Steven Schveighoffer" <schveiguy yahoo.com> said:On Thu, 02 Dec 2010 07:09:27 -0500, Michel Fortin <michel.fortin michelf.com> wrote:I'm not sure I get the problem. Can you show me in code? -- Michel Fortin michel.fortin michelf.com http://michelf.com/My only concern with the "const(Object)ref" syntax is that we're reusing 'ref' to denote an object reference with different properties (rebindable, nullable) than what 'ref' currently stands for. But it remains the best syntax I've seen so far.Where it would be beneficial is in mimicking the tail-const properties of arrays in generic ranges. I have a container C, which defines a range over its elements R. const(R) is not a usable range, because popFront cannot be const. So now I need to define constR, which is identical to R, except the front() function returns a const element. So now, I need the same for immutable. And now I need to triplicate all my functions which accept the ranges, or return them. And I can't use inout(R) as a return value for ranges. If you can solve the general problem, and not just the class tail-const, it would be hugely beneficial. My thought was that a modifier on const itself could be stored in the TypeInfo_Const as a boolean (tail or not), and the equivalent done in dmd source itself.
Dec 02 2010
Michel Fortin <michel.fortin michelf.com> wrote:I'm not sure I get the problem. Can you show me in code?const a = map!"a+a"( [1,2,3] ); foreach ( e; a ) { } The foreach fails because popFront is not const. What is needed is for typeof(a) to be Map!("a+a", const(int)[]). IOW, is( const(Map!("a+a", int[])) == Map!("a+a", const(int)[]) ). One possible way to do this is for all types T to have defined types immutable_t and const_t, which by default alias to immutable(T) and const(T), but can be defined to alias to other types. The compiler would then automagically convert cast(const)T to cast(T.const_t)T. -- Simen
Dec 02 2010
On 12/2/10 6:54 PM, Simen kjaeraas wrote:Michel Fortin <michel.fortin michelf.com> wrote:Well the code asks for a constant object, and I don't see it as reasonable for the type system to automagically infer the intent. What should work is this: const(int)[] data = [1,2,3]; auto a = map!"a+a"(data); foreach (e;a) { } AndreiI'm not sure I get the problem. Can you show me in code?const a = map!"a+a"( [1,2,3] ); foreach ( e; a ) { } The foreach fails because popFront is not const. What is needed is for typeof(a) to be Map!("a+a", const(int)[]). IOW, is( const(Map!("a+a", int[])) == Map!("a+a", const(int)[]) ). One possible way to do this is for all types T to have defined types immutable_t and const_t, which by default alias to immutable(T) and const(T), but can be defined to alias to other types. The compiler would then automagically convert cast(const)T to cast(T.const_t)T.
Dec 02 2010
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:On 12/2/10 6:54 PM, Simen kjaeraas wrote:rMichel Fortin <michel.fortin michelf.com> wrote:I'm not sure I get the problem. Can you show me in code?const a =3D map!"a+a"( [1,2,3] ); foreach ( e; a ) { } The foreach fails because popFront is not const. What is needed is fo=typeof(a) to be Map!("a+a", const(int)[]). IOW, is( const(Map!("a+a", int[])) =3D=3D Map!("a+a", const(int)[]) ). One possible way to do this is for all types T to have defined types immutable_t and const_t, which by default alias to immutable(T) and const(T), but can be defined to alias to other types. The compiler would then automagically convert cast(const)T to cast(T.const_t)T.Well the code asks for a constant object, and I don't see it as =reasonable for the type system to automagically infer the intent.True. I believe I was thinking that T should be implicitly convertible to T.const_t (or tailconst_t, as may be more appropriate). What might be appropriate is a function tailconst( T )( T t ) that returns a tail const version of the passed type. That is, given a T[], const(T[]), const(T)[], immutable(T[]), or immutable(T)[], it returns a const(T)[]. For a MyRange!R, const(MyRange!R), or immutable(MyRange!R), it returns a MyRange!(R).tailconst_t. See bottom of post for a (na=C3=AFve) implementation.What should work is this: const(int)[] data =3D [1,2,3]; auto a =3D map!"a+a"(data); foreach (e;a) { }That does work. import std.traits; /** * Return the tail-const type for a given type **/ template TailConst( T ) { static if ( is( T U : U[] ) ) { alias const(Unqual!U)[] TailConst; } else static if ( is( T U : U* ) ) { alias const(Unqual!U)* TailConst; } else static if ( is( T.tailconst_t ) ) { alias T.tailconst_t TailConst; } else static assert( false ); } unittest { struct test { alias int tailconst_t; } assert( is( TailConst!( int[] ) =3D=3D const(int)[] ) ); assert( is( TailConst!( immutable( int[] ) ) =3D=3D const(int)[] ) = ); assert( is( TailConst!( const(int)[] ) =3D=3D const(int)[] ) ); assert( is( TailConst!test =3D=3D int ) ); } /** * Converts the given parameter to tail const **/ TailConst!T tailconst( T )( T t ) { TailConst!T tmp =3D t; return tmp; } unittest { struct test( T ) { alias test!(const T) tailconst_t; this( const test!( Unqual!T ) t ) {} } assert( __traits( compiles, { tailconst( [1,2,3] ); } ) ); assert( __traits( compiles, { test!int t; tailconst( t ); } ) ); } -- = Simen
Dec 03 2010
Simen kjaeraas <simen.kjaras gmail.com> wrote:What might be appropriate is a function tailconst( T )( T t ) that returns a tail const version of the passed type. That is, given a T[], const(T[]), const(T)[], immutable(T[]), or immutable(T)[], it returns a const(T)[]. For a MyRange!R, const(MyRange!R), or immutable(MyRange!R), it returns a MyRange!(R).tailconst_t. See bottom=of post for a (na=C3=AFve) implementation.To expound further on this, I have created the attached module. Critique wanted. -- = Simen
Dec 04 2010
On 12/4/10 12:23 PM, Simen kjaeraas wrote:Simen kjaeraas <simen.kjaras gmail.com> wrote:Looks promising. A few comments. * For TailXxx you need to handle built-in simple types (int, float...) to return themselves. Also, structs for which hasIndirections returns false also return themselves. * tailconst_t does not obey Phobos' naming convention. I think it's fine to use TailConst in spite of the apparent ambiguity. * You may want to add more stringent checks for tailconst_t (well TailConst etc) to make sure it's not bogus - has the same size, compatible members etc. AndreiWhat might be appropriate is a function tailconst( T )( T t ) that returns a tail const version of the passed type. That is, given a T[], const(T[]), const(T)[], immutable(T[]), or immutable(T)[], it returns a const(T)[]. For a MyRange!R, const(MyRange!R), or immutable(MyRange!R), it returns a MyRange!(R).tailconst_t. See bottom of post for a (naïve) implementation.To expound further on this, I have created the attached module. Critique wanted.
Dec 04 2010
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:On 12/4/10 12:23 PM, Simen kjaeraas wrote:Done.To expound further on this, I have created the attached module. Critique wanted.Looks promising. A few comments. * For TailXxx you need to handle built-in simple types (int, float...) to return themselves. Also, structs for which hasIndirections returns false also return themselves.* tailconst_t does not obey Phobos' naming convention. I think it's fine to use TailConst in spite of the apparent ambiguity.It adds some .'s: alias SimpleRange!(.TailConst!T) TailConst; static if ( !is( T == .TailMutable!T ) ) { this( SimpleRange.TailImmutable r ) { Not sure if this is a problem.* You may want to add more stringent checks for tailconst_t (well TailConst etc) to make sure it's not bogus - has the same size, compatible members etc.I've tried, and it seems adding static assert( T.sizeof == T.TailConst.sizeof ); does not work (no size yet for forward reference). Checking that all members are the same type and order is easy and works. Comparing member names bumped me into bug 5079. Likely related to the above, seeing as both have to do with unfinished types. I've also considered a template on the form mixin tailConst!( SimpleRange, SimpleRange!( Tail!T ) ); or mixin tailConst!( SimpleRange, Tail!T ); which would automagically define aliases. More work for me, perhaps less work for users. This could not define constructors, as overloads from inside template mixins don't work with overloads outside. -- Simen
Dec 05 2010
Simen kjaeraas <simen.kjaras gmail.com> wrote:I've also considered a template on the form mixin tailConst!( SimpleRange, SimpleRange!( Tail!T ) ); or mixin tailConst!( SimpleRange, Tail!T );A closer look at this reveals that it won't work that simply, because SimpleRange in this context is the struct, not the template. This, however, works: mixin tailConst!( .SimpleRange, TailT! ); Not sure how I like this. -- Simen
Dec 05 2010
Simen kjaeraas <simen.kjaras gmail.com> wrote:Simen kjaeraas <simen.kjaras gmail.com> wrote:After more problems, I have also come to the conclusion that what is most commonly needed is not really tail-const, but head-mutable. Why this took me more than a few minutes to consider, I do not know. Common use-cases would then look like this: struct MyRange( Range ) { HeadMutable!Range r; // Range primitives, TailConst support, etc. } -- SimenI've also considered a template on the form mixin tailConst!( SimpleRange, SimpleRange!( Tail!T ) ); or mixin tailConst!( SimpleRange, Tail!T );A closer look at this reveals that it won't work that simply, because SimpleRange in this context is the struct, not the template. This, however, works: mixin tailConst!( .SimpleRange, TailT! ); Not sure how I like this.
Dec 26 2010
Simen kjaeraas <simen.kjaras gmail.com> wrote:After more problems, I have also come to the conclusion that what is most commonly needed is not really tail-const, but head-mutable. Why this took me more than a few minutes to consider, I do not know. Common use-cases would then look like this: struct MyRange( Range ) { HeadMutable!Range r; // Range primitives, TailConst support, etc. }One might then think (I certainly did) that Tail(((Im|M)utable)|Const) would be superfluous, and HeadMutable is the solution to all problems. This is not true. HeadMutable is incapable of conveying that TailMutable!T is implicitly castable to TailConst!T. -- Simen
Dec 26 2010
Simen kjaeraas <simen.kjaras gmail.com> wrote:Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Nobody ever commented on this. Figured I'd poke around with a stick and see if there were any opinions. IOW: bump. -- SimenOn 12/4/10 12:23 PM, Simen kjaeraas wrote:Done.To expound further on this, I have created the attached module. Critique wanted.Looks promising. A few comments. * For TailXxx you need to handle built-in simple types (int, float...) to return themselves. Also, structs for which hasIndirections returns false also return themselves.* tailconst_t does not obey Phobos' naming convention. I think it's fine to use TailConst in spite of the apparent ambiguity.It adds some .'s: alias SimpleRange!(.TailConst!T) TailConst; static if ( !is( T == .TailMutable!T ) ) { this( SimpleRange.TailImmutable r ) { Not sure if this is a problem.* You may want to add more stringent checks for tailconst_t (well TailConst etc) to make sure it's not bogus - has the same size, compatible members etc.I've tried, and it seems adding static assert( T.sizeof == T.TailConst.sizeof ); does not work (no size yet for forward reference). Checking that all members are the same type and order is easy and works. Comparing member names bumped me into bug 5079. Likely related to the above, seeing as both have to do with unfinished types. I've also considered a template on the form mixin tailConst!( SimpleRange, SimpleRange!( Tail!T ) ); or mixin tailConst!( SimpleRange, Tail!T ); which would automagically define aliases. More work for me, perhaps less work for users. This could not define constructors, as overloads from inside template mixins don't work with overloads outside.
Dec 15 2010
On Thu, 02 Dec 2010 22:17:53 -0500, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:On 12/2/10 6:54 PM, Simen kjaeraas wrote:Now, change data to an SList range. This is what I'm talking about. Arrays already enjoy the benefits of tail-const, I want to extend that to all range types (and in fact all struct types). -SteveMichel Fortin <michel.fortin michelf.com> wrote:Well the code asks for a constant object, and I don't see it as reasonable for the type system to automagically infer the intent. What should work is this: const(int)[] data = [1,2,3]; auto a = map!"a+a"(data); foreach (e;a) { }I'm not sure I get the problem. Can you show me in code?const a = map!"a+a"( [1,2,3] ); foreach ( e; a ) { } The foreach fails because popFront is not const. What is needed is for typeof(a) to be Map!("a+a", const(int)[]). IOW, is( const(Map!("a+a", int[])) == Map!("a+a", const(int)[]) ). One possible way to do this is for all types T to have defined types immutable_t and const_t, which by default alias to immutable(T) and const(T), but can be defined to alias to other types. The compiler would then automagically convert cast(const)T to cast(T.const_t)T.
Dec 03 2010
On Thu, 02 Dec 2010 17:02:42 -0500, Michel Fortin <michel.fortin michelf.com> wrote:On 2010-12-02 16:14:58 -0500, "Steven Schveighoffer" <schveiguy yahoo.com> said:Here is an example range from dcollections (well, at least the pertinant part), for a linked list: struct Range { LinkNode *node; void popFront() { node = node.next; } } Now, let's say I have a LinkList instance (ignore the parameterized type). I want to pass this list to a function and ensure nothing changes in the list. So I create a function like this: void foo(const(LinkList) list) { auto r = list[]; // get a range from the list } Now, LinkList has a function like this: Range opSlice() { Range result; result.node = head; return result; } In order to satisfy constancy, I now have to do two things. I have to define a *different* range, because const(Range) doesn't work (popFront is not and cannot be const). Call it ConstRange. The second thing I have to do is now define a completely separate function for opSlice: ConstRange opSlice() const { ConstRange result; result.node = head; // result.node is tail-const } Now, I could possibly make Range just parameterized on the parameterized type, but I still have to create two separate types (whether generated by template or not). Finally, I have to repeat *all this* for immutable. And for all functions that take a range, or return a range. And this is the real kicker... it *still* doesn't work correctly. Observe: void foo(LinkList.ConstRange r) { } If I have a mutable LinkList called list, I can't do foo(list[]), because Range does not implicitly convert to ConstRange. I have to first convert list to a const(LinkList), and then use the slice operator. Now, if we have tail-const that I can apply to a struct, which just makes all references contained in the type tail-const, then this becomes very very easy (with proposed syntax from Tomek): tail inout(Range) opSlice() inout { ... } One function, one Range defined, very simple, very elegant. Note that I can do all this if my range is an array (as it is in ArrayList) without modification to the compiler, because tail-const arrays are possible. All I want is to duplicate the implicit casting, and implicit typing, that arrays have with tail const. If we can have a solution that fixes the tail-const class problem *and* this problem, it will be two birds, one stone. -SteveOn Thu, 02 Dec 2010 07:09:27 -0500, Michel Fortin <michel.fortin michelf.com> wrote:I'm not sure I get the problem. Can you show me in code?My only concern with the "const(Object)ref" syntax is that we're reusing 'ref' to denote an object reference with different properties (rebindable, nullable) than what 'ref' currently stands for. But it remains the best syntax I've seen so far.Where it would be beneficial is in mimicking the tail-const properties of arrays in generic ranges. I have a container C, which defines a range over its elements R. const(R) is not a usable range, because popFront cannot be const. So now I need to define constR, which is identical to R, except the front() function returns a const element. So now, I need the same for immutable. And now I need to triplicate all my functions which accept the ranges, or return them. And I can't use inout(R) as a return value for ranges. If you can solve the general problem, and not just the class tail-const, it would be hugely beneficial. My thought was that a modifier on const itself could be stored in the TypeInfo_Const as a boolean (tail or not), and the equivalent done in dmd source itself.
Dec 03 2010
On 12/3/10 5:17 PM, Steven Schveighoffer wrote:On Thu, 02 Dec 2010 17:02:42 -0500, Michel Fortin <michel.fortin michelf.com> wrote:[snip analysis]On 2010-12-02 16:14:58 -0500, "Steven Schveighoffer" <schveiguy yahoo.com> said:Here is an example range from dcollections (well, at least the pertinant part), for a linked list:On Thu, 02 Dec 2010 07:09:27 -0500, Michel Fortin <michel.fortin michelf.com> wrote:I'm not sure I get the problem. Can you show me in code?My only concern with the "const(Object)ref" syntax is that we're reusing 'ref' to denote an object reference with different properties (rebindable, nullable) than what 'ref' currently stands for. But it remains the best syntax I've seen so far.Where it would be beneficial is in mimicking the tail-const properties of arrays in generic ranges. I have a container C, which defines a range over its elements R. const(R) is not a usable range, because popFront cannot be const. So now I need to define constR, which is identical to R, except the front() function returns a const element. So now, I need the same for immutable. And now I need to triplicate all my functions which accept the ranges, or return them. And I can't use inout(R) as a return value for ranges. If you can solve the general problem, and not just the class tail-const, it would be hugely beneficial. My thought was that a modifier on const itself could be stored in the TypeInfo_Const as a boolean (tail or not), and the equivalent done in dmd source itself.tail inout(Range) opSlice() inout { ... }I was about to post a similar analysis, but my suggested conclusion is very different: in my humble opinion, we must make-do without tail const. We can't afford to inflict such complexity on our users. The burden of defining tail const/immutable/inout functions in addition to non- tail const/immutable/inout functions (sometimes the distinction would need to be made!) is just too high. I think we need to work out solutions within the existing language. Andrei
Dec 03 2010
On Fri, 03 Dec 2010 19:06:36 -0500, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:On 12/3/10 5:17 PM, Steven Schveighoffer wrote:I had not thought of it this way. I assumed you would only need to define a function as tail-const or const. Certainly if a function can be const, you don't need a tail-const version also do you? A ref to tail-const should implicitly cast to ref to const. If you need tail-const version, then a const object should not be able to call this. Immutable is different, ref to tail-immutable does not implicitly cast to ref immutable. This really is a shame, I think you are right, we cannot do tail-const generically in this way :( I overlooked this because arrays are passed to their 'member' functions generally by value and not by reference. The same is not true for structs with member functions. But we absolutely need a way to say "this is implicitly castable to it's tail-const version." This is the problem I showed at the end of my earlier post -- you cannot implicitly convert Range into ConstRange. It would be nice to have an enforceable way to allow this, so that the burden of proof that implicit casting works properly is not on the developer. In addition, the way inout works would be nice to hook into this mechanism, so you could describe how inout applies to a tail-inout version. Thanks for pointing this out. -SteveOn Thu, 02 Dec 2010 17:02:42 -0500, Michel Fortin <michel.fortin michelf.com> wrote:[snip analysis]On 2010-12-02 16:14:58 -0500, "Steven Schveighoffer" <schveiguy yahoo.com> said:Here is an example range from dcollections (well, at least the pertinant part), for a linked list:On Thu, 02 Dec 2010 07:09:27 -0500, Michel Fortin <michel.fortin michelf.com> wrote:I'm not sure I get the problem. Can you show me in code?My only concern with the "const(Object)ref" syntax is that we're reusing 'ref' to denote an object reference with different properties (rebindable, nullable) than what 'ref' currently stands for. But it remains the best syntax I've seen so far.Where it would be beneficial is in mimicking the tail-const properties of arrays in generic ranges. I have a container C, which defines a range over its elements R. const(R) is not a usable range, because popFront cannot be const. So now I need to define constR, which is identical to R, except the front() function returns a const element. So now, I need the same for immutable. And now I need to triplicate all my functions which accept the ranges, or return them. And I can't use inout(R) as a return value for ranges. If you can solve the general problem, and not just the class tail-const, it would be hugely beneficial. My thought was that a modifier on const itself could be stored in the TypeInfo_Const as a boolean (tail or not), and the equivalent done in dmd source itself.tail inout(Range) opSlice() inout { ... }I was about to post a similar analysis, but my suggested conclusion is very different: in my humble opinion, we must make-do without tail const. We can't afford to inflict such complexity on our users. The burden of defining tail const/immutable/inout functions in addition to non- tail const/immutable/inout functions (sometimes the distinction would need to be made!) is just too high. I think we need to work out solutions within the existing language.
Dec 03 2010
On Fri, 03 Dec 2010 19:06:36 -0500, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:On 12/3/10 5:17 PM, Steven Schveighoffer wrote:BTW, even though I conceed that my ideas are too complex to be worth using, I don't agree we must "make-do" without tail-const. We just need to find a different way to solve the problem. Let's talk about how we could add some sort of custom implicit casting to the type-system. And actually, we need implicit lvalue casting (because all member functions have ref this). -SteveOn Thu, 02 Dec 2010 17:02:42 -0500, Michel Fortin <michel.fortin michelf.com> wrote:[snip analysis]On 2010-12-02 16:14:58 -0500, "Steven Schveighoffer" <schveiguy yahoo.com> said:Here is an example range from dcollections (well, at least the pertinant part), for a linked list:On Thu, 02 Dec 2010 07:09:27 -0500, Michel Fortin <michel.fortin michelf.com> wrote:I'm not sure I get the problem. Can you show me in code?My only concern with the "const(Object)ref" syntax is that we're reusing 'ref' to denote an object reference with different properties (rebindable, nullable) than what 'ref' currently stands for. But it remains the best syntax I've seen so far.Where it would be beneficial is in mimicking the tail-const properties of arrays in generic ranges. I have a container C, which defines a range over its elements R. const(R) is not a usable range, because popFront cannot be const. So now I need to define constR, which is identical to R, except the front() function returns a const element. So now, I need the same for immutable. And now I need to triplicate all my functions which accept the ranges, or return them. And I can't use inout(R) as a return value for ranges. If you can solve the general problem, and not just the class tail-const, it would be hugely beneficial. My thought was that a modifier on const itself could be stored in the TypeInfo_Const as a boolean (tail or not), and the equivalent done in dmd source itself.tail inout(Range) opSlice() inout { ... }I was about to post a similar analysis, but my suggested conclusion is very different: in my humble opinion, we must make-do without tail const. We can't afford to inflict such complexity on our users.
Dec 03 2010
On 12/3/10 7:26 PM, Steven Schveighoffer wrote:BTW, even though I conceed that my ideas are too complex to be worth using, I don't agree we must "make-do" without tail-const. We just need to find a different way to solve the problem. Let's talk about how we could add some sort of custom implicit casting to the type-system. And actually, we need implicit lvalue casting (because all member functions have ref this).I believe this is the level of understanding and the kind of attitude that can push things forward. Andrei
Dec 03 2010
On 4-dic-10, at 02:26, Steven Schveighoffer wrote:On Fri, 03 Dec 2010 19:06:36 -0500, Andrei Alexandrescu <SeeWebsiteForEmail erdani.orgI fully agree with this. I will try to recap what I think is the most clean solution from the conceptual point of view, then maybe others have an idea on how to find a good solution that is not too difficult to implement, and doesn't break what was said in TDPL too much. The current const implies that the references to that type have to be constant. tail const, and has the recursive definition I had given: valueConst(T) is T for basic types functions and templates refConst(U)* if is(T U==U*) refConst(U)[] if is(T U==U[]) V if is(T == struct), where V is a structure just like T, but where each of its fields is tail const tail const marks all that is copied when one assigns T v2=v1; as mutable. Indeed to protect v1 it is not needed to protect the values that get copied in the assignment, those values can be changed without changing v1. For this reason tail const is the most weak const that one can have in pure functions, in ideally should mean tail const (thus in some way tail const comes from the protection of a starting const. any lvalue by default should be tail const, if it was const, and tail immutable if it was immutable, but is implicitly convertible to full const/immutable. in a way tail const is more fundamental as it is the least protection that one has to give to protect some data owned by others. If I have a global variable the current const/immutable can guarantee that its value will not change, while tail immutable guarantees that one can safely point to that data as it won't be changed: that data can be shared safely (not necessarily by several threads, even simply by several objects). Thus both have their function, but in general I think that tail const might be even more important (if I had to choose one const/immutable type I would choose the tail one).wrote:On 12/3/10 5:17 PM, Steven Schveighoffer wrote:BTW, even though I conceed that my ideas are too complex to be worth using, I don't agree we must "make-do" without tail-const. We just need to find a different way to solve the problem. Let's talk about how we could add some sort of custom implicit casting to the type- system. And actually, we need implicit lvalue casting (because all member functions have ref this).On Thu, 02 Dec 2010 17:02:42 -0500, Michel Fortin <michel.fortin michelf.com> wrote:[snip analysis]On 2010-12-02 16:14:58 -0500, "Steven Schveighoffer" <schveiguy yahoo.com> said:Here is an example range from dcollections (well, at least the pertinant part), for a linked list:On Thu, 02 Dec 2010 07:09:27 -0500, Michel Fortin <michel.fortin michelf.com> wrote:I'm not sure I get the problem. Can you show me in code?My only concern with the "const(Object)ref" syntax is that we're reusing 'ref' to denote an object reference with different properties (rebindable, nullable) than what 'ref' currently stands for. But it remains the best syntax I've seen so far.Where it would be beneficial is in mimicking the tail-const properties of arrays in generic ranges. I have a container C, which defines a range over its elements R. const(R) is not a usable range, because popFront cannot be const. So now I need to define constR, which is identical to R, except the front() function returns a const element. So now, I need the same for immutable. And now I need to triplicate all my functions which accept the ranges, or return them. And I can't use inout(R) as a return value for ranges. If you can solve the general problem, and not just the class tail-const, it would be hugely beneficial. My thought was that a modifier on const itself could be stored in the TypeInfo_Const as a boolean (tail or not), and the equivalent done in dmd source itself.tail inout(Range) opSlice() inout { ... }I was about to post a similar analysis, but my suggested conclusion is very different: in my humble opinion, we must make-do without tail const. We can't afford to inflict such complexity on our users.
Dec 04 2010
On 5-dic-10, at 00:39, Fawzi Mohamed wrote:[...] Thus both have their function, but in general I think that tail const might be even more important (if I had to choose one const/ immutable type I would choose the tail one).This was thought a bit as provocation, but as nobody reacted, and with trolls roving around I want to clarify. the normal const obviously allows sharing, so it isn't a bad choice, but it introduces more constraints that needed to simply share memory. These constraints make the life more difficult, so I wondered if choosing tail const as only const would work. It has issues, but not as bad as one would think, applying tail const to ref T is basically const on T. There are still issues but I thought lets throw that in and see what other think. By the way I think that one of the problems in having const and tail const is that it is expressing the constness implied by one operator using the other operator, in this sense tail const might be (slightly) better Fawzi
Dec 05 2010
On 2010-12-03 18:17:26 -0500, "Steven Schveighoffer" <schveiguy yahoo.com> said:Here is an example range from dcollections (well, at least the pertinant part), for a linked list: struct Range { LinkNode *node; void popFront() { node = node.next; } } Now, let's say I have a LinkList instance (ignore the parameterized type). I want to pass this list to a function and ensure nothing changes in the list. So I create a function like this: void foo(const(LinkList) list) { auto r = list[]; // get a range from the list } Now, LinkList has a function like this: Range opSlice() { Range result; result.node = head; return result; } In order to satisfy constancy, I now have to do two things. I have to define a *different* range, because const(Range) doesn't work (popFront is not and cannot be const). Call it ConstRange. The second thing I have to do is now define a completely separate function for opSlice: ConstRange opSlice() const { ConstRange result; result.node = head; // result.node is tail-const } Now, I could possibly make Range just parameterized on the parameterized type, but I still have to create two separate types (whether generated by template or not). Finally, I have to repeat *all this* for immutable. And for all functions that take a range, or return a range. And this is the real kicker... it *still* doesn't work correctly. Observe: void foo(LinkList.ConstRange r) { } If I have a mutable LinkList called list, I can't do foo(list[]), because Range does not implicitly convert to ConstRange. I have to first convert list to a const(LinkList), and then use the slice operator. Now, if we have tail-const that I can apply to a struct, which just makes all references contained in the type tail-const, then this becomes very very easy (with proposed syntax from Tomek): tail inout(Range) opSlice() inout { ... } One function, one Range defined, very simple, very elegant. Note that I can do all this if my range is an array (as it is in ArrayList) without modification to the compiler, because tail-const arrays are possible. All I want is to duplicate the implicit casting, and implicit typing, that arrays have with tail const. If we can have a solution that fixes the tail-const class problem *and* this problem, it will be two birds, one stone.A fine explanation. Thank you. I disagree about your proposed solution, but I recognize the problem. The basic problem with your solution is that it creates a new kind of const, a new kind of immutable and a new kind of shared. You should realize that for the compiler to know the constness of member variables inside a function, it'll have to know whether the 'this' pointer is 'const' or 'tail const'. So I think it's the wrong path. The right path would be, I think, to parametrize the constness in the type. A way to do this within the current constrains of the language would be to make Range implicitly convertible to ConstRange, something you should be able to do with "alias X this", X being a function returning a ConstRange. The disadvantages are the duplication of the opSlice function, and the inability to cast implicitly a ref Range to ref ConstRange or a Range[] to a ConstRange[]. I have an idea that would fix those: make a template struct/class instance implicitly convertible to another instance of that same template if all members share the same memory layout and each member is implicitly convertible to the same member of the other template. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Dec 03 2010
On Fri, 03 Dec 2010 20:40:23 -0500, Michel Fortin <michel.fortin michelf.com> wrote:A fine explanation. Thank you. I disagree about your proposed solution, but I recognize the problem. The basic problem with your solution is that it creates a new kind of const, a new kind of immutable and a new kind of shared. You should realize that for the compiler to know the constness of member variables inside a function, it'll have to know whether the 'this' pointer is 'const' or 'tail const'. So I think it's the wrong path.Yes, Andrei also pointed out this problem. Part of the issue is that member functions are always passed 'this' by ref. Arrays don't have this problem because you have control over passing them by ref or by value. I agree my solution does not work, or actually that it works but makes things worse :)The right path would be, I think, to parametrize the constness in the type. A way to do this within the current constrains of the language would be to make Range implicitly convertible to ConstRange, something you should be able to do with "alias X this", X being a function returning a ConstRange. The disadvantages are the duplication of the opSlice function, and the inability to cast implicitly a ref Range to ref ConstRange or a Range[] to a ConstRange[].In fact, I'm not sure you could do Range[] to ConstRange[], I think that's two levels of indirection. I can sort of live with defining multiple functions, at least it would be possible to make something work. If we could have some way to hook inout, like designate Range for mutable, ConstRange for const, and ImmutableRange for immutable, and let InoutRange represent the inout type (which gets implicitly converted to one of those ) But maybe I'm dreaming :)I have an idea that would fix those: make a template struct/class instance implicitly convertible to another instance of that same template if all members share the same memory layout and each member is implicitly convertible to the same member of the other template.I had thought of that too, a long time ago, but I wasn't sure if it could work. I'd go two steps further: 1. all the member variable names must be identical. 2. you need to identify that implicit conversion is allowed. 1 is for sanity ;) struct Pair(T) { T x; T y; } struct ConstPair(T) { const(T) y; const(T) x; } 2 is to ensure you are not able to incorrectly morph data into things it should not be. i.e.: struct Point { int x; int y; } I don't think you should be able to implicitly cast Pair!int to Point, sometimes you want to define different APIs for the same data. -Steve
Dec 03 2010
On 2010-12-03 21:02:10 -0500, "Steven Schveighoffer" <schveiguy yahoo.com> said:On Fri, 03 Dec 2010 20:40:23 -0500, Michel Fortin <michel.fortin michelf.com> wrote:Just like you, I don't think you should be able to implicitly cast Pair!int to Point. What I was suggesting is that the implicit cast would work only as long as the struct/class instance comes from the same template definition. That'd actually make Pair!int and Pair!uint convertible between each other (because int and uint are implicitly converted from one another) but ConstPair, which originate from a different template definition, wouldn't be convertible from Pair. Instead of defining ConstPair, you'd use Pair!(const(T)) to denote a pair of const elements, and because T is convertible to const(T), Pair!T is also convertible to Pair!(const(T))... as long as the memory layout is preserved, of course. -- Michel Fortin michel.fortin michelf.com http://michelf.com/I have an idea that would fix those: make a template struct/class instance implicitly convertible to another instance of that same template if all members share the same memory layout and each member is implicitly convertible to the same member of the other template.I had thought of that too, a long time ago, but I wasn't sure if it could work. I'd go two steps further: 1. all the member variable names must be identical. 2. you need to identify that implicit conversion is allowed. 1 is for sanity ;) struct Pair(T) { T x; T y; } struct ConstPair(T) { const(T) y; const(T) x; } 2 is to ensure you are not able to incorrectly morph data into things it should not be. i.e.: struct Point { int x; int y; } I don't think you should be able to implicitly cast Pair!int to Point, sometimes you want to define different APIs for the same data.
Dec 03 2010
On Fri, 03 Dec 2010 21:19:14 -0500, Michel Fortin <michel.fortin michelf.com> wrote:On 2010-12-03 21:02:10 -0500, "Steven Schveighoffer" <schveiguy yahoo.com> said:Oh, I misread your original idea, sorry. I like the idea. I just think it should be explicit that the 'same memory layout' means the names of the variables are the same. I just think of bizarre cases where static ifs are used to confuse things. -SteveOn Fri, 03 Dec 2010 20:40:23 -0500, Michel Fortin <michel.fortin michelf.com> wrote:Just like you, I don't think you should be able to implicitly cast Pair!int to Point. What I was suggesting is that the implicit cast would work only as long as the struct/class instance comes from the same template definition. That'd actually make Pair!int and Pair!uint convertible between each other (because int and uint are implicitly converted from one another) but ConstPair, which originate from a different template definition, wouldn't be convertible from Pair. Instead of defining ConstPair, you'd use Pair!(const(T)) to denote a pair of const elements, and because T is convertible to const(T), Pair!T is also convertible to Pair!(const(T))... as long as the memory layout is preserved, of course.I have an idea that would fix those: make a template struct/class instance implicitly convertible to another instance of that same template if all members share the same memory layout and each member is implicitly convertible to the same member of the other template.I had thought of that too, a long time ago, but I wasn't sure if it could work. I'd go two steps further: 1. all the member variable names must be identical. 2. you need to identify that implicit conversion is allowed. 1 is for sanity ;) struct Pair(T) { T x; T y; } struct ConstPair(T) { const(T) y; const(T) x; } 2 is to ensure you are not able to incorrectly morph data into things it should not be. i.e.: struct Point { int x; int y; } I don't think you should be able to implicitly cast Pair!int to Point, sometimes you want to define different APIs for the same data.
Dec 03 2010
On 2010-12-03 21:25:01 -0500, "Steven Schveighoffer" <schveiguy yahoo.com> said:On Fri, 03 Dec 2010 21:19:14 -0500, Michel Fortin <michel.fortin michelf.com> wrote:Yes, by "same memory layout" I mean the same variables, with the same names, occupying the same bytes. -- Michel Fortin michel.fortin michelf.com http://michelf.com/On 2010-12-03 21:02:10 -0500, "Steven Schveighoffer" <schveiguy yahoo.com> said:Oh, I misread your original idea, sorry. I like the idea. I just think it should be explicit that the 'same memory layout' means the names of the variables are the same. I just think of bizarre cases where static ifs are used to confuse things.On Fri, 03 Dec 2010 20:40:23 -0500, Michel Fortin <michel.fortin michelf.com> wrote:Just like you, I don't think you should be able to implicitly cast Pair!int to Point. What I was suggesting is that the implicit cast would work only as long as the struct/class instance comes from the same template definition. That'd actually make Pair!int and Pair!uint convertible between each other (because int and uint are implicitly converted from one another) but ConstPair, which originate from a different template definition, wouldn't be convertible from Pair. Instead of defining ConstPair, you'd use Pair!(const(T)) to denote a pair of const elements, and because T is convertible to const(T), Pair!T is also convertible to Pair!(const(T))... as long as the memory layout is preserved, of course.I have an idea that would fix those: make a template struct/class instance implicitly convertible to another instance of that same template if all members share the same memory layout and each member is implicitly convertible to the same member of the other template.I had thought of that too, a long time ago, but I wasn't sure if it could work. I'd go two steps further: 1. all the member variable names must be identical. 2. you need to identify that implicit conversion is allowed. 1 is for sanity ;) struct Pair(T) { T x; T y; } struct ConstPair(T) { const(T) y; const(T) x; } 2 is to ensure you are not able to incorrectly morph data into things it should not be. i.e.: struct Point { int x; int y; } I don't think you should be able to implicitly cast Pair!int to Point, sometimes you want to define different APIs for the same data.
Dec 03 2010
On 12/3/10 7:40 PM, Michel Fortin wrote:I have an idea that would fix those: make a template struct/class instance implicitly convertible to another instance of that same template if all members share the same memory layout and each member is implicitly convertible to the same member of the other template.I'm afraid that can't work. struct A(T) { T obj; void fun() { obj->method(); } } auto a = new A!Widget; a.obj = new Widget; A!Object b = a; // works because Widget converts to Object b.obj = new Object; // ummmm... b.fun(); // ummmm... Andrei
Dec 03 2010
On 04.12.2010 6:23, Andrei Alexandrescu wrote:On 12/3/10 7:40 PM, Michel Fortin wrote:Looks discouraging at first, but perfectly valid given that the example works only when A!Object compiles, i.e. Object have method 'method', and then:I have an idea that would fix those: make a template struct/class instance implicitly convertible to another instance of that same template if all members share the same memory layout and each member is implicitly convertible to the same member of the other template.I'm afraid that can't work. struct A(T) { T obj; void fun() { obj->method(); } }auto a = new A!Widget; a.obj = new Widget; A!Object b = *a; // works because Widget converts to Object// A!Object* b = a; // should not compile, and would be a problem if A is a class //now we have another struct b with reference to a's widgetb.obj = new Object; //no problem, a stays intact b.fun(); // since A!Object already compiles, it's perfectly validIn fact, it looks like Michel's rule is very promising, just replace "struct/class" part with "struct" in definition. -- Dmitry Olshansky
Dec 03 2010
On 2010-12-04 02:48:26 -0500, Dmitry Olshansky <dmitry.olsh gmail.com> said:On 04.12.2010 6:23, Andrei Alexandrescu wrote:Yes, indeed. I was a little over-enthusiastic when saying it'd work for classes too; the vtable pointer would be a problem for that. But it seems it can work well for structs. You're right, "A!Object b = a" should compile fine while "A!Object* b = &a;" should not, because it'd allow you to assign any Object to the Widget field. That said, you can still allow this convertion: "A!(const(Object))* b = &a;". That's because the obj member becomes const and you can no longer assign anything to it. So, to refine the rules I'd say a templated struct can be converted to a lvalue of another instance of the same templated struct with the same memory layout if all members can be converted to a lvalue of their type in the second struct. If one field can only be converted as a rvalue, the result is a rvalue struct. We should also make it so Widget can convert to const(Object) as an lvalue; that doesn't work currently. -- Michel Fortin michel.fortin michelf.com http://michelf.com/On 12/3/10 7:40 PM, Michel Fortin wrote:Looks discouraging at first, but perfectly valid given that the example works only when A!Object compiles, i.e. Object have method 'method', and then:I have an idea that would fix those: make a template struct/class instance implicitly convertible to another instance of that same template if all members share the same memory layout and each member is implicitly convertible to the same member of the other template.I'm afraid that can't work. struct A(T) { T obj; void fun() { obj->method(); } }auto a = new A!Widget; a.obj = new Widget; A!Object b = *a; // works because Widget converts to Object// A!Object* b = a; // should not compile, and would be a problem if A is a class //now we have another struct b with reference to a's widgetb.obj = new Object; //no problem, a stays intact b.fun(); // since A!Object already compiles, it's perfectly validIn fact, it looks like Michel's rule is very promising, just replace "struct/class" part with "struct" in definition.
Dec 04 2010
On 2010-12-04 07:13:30 -0500, Michel Fortin <michel.fortin michelf.com> said:On 2010-12-04 02:48:26 -0500, Dmitry Olshansky <dmitry.olsh gmail.com> said:That wasn't very well put. Here I was looking only at the obj field. As defined above it wouldn't compile because "fun() { obj.method() }" won't compile for Object and the template won't instanciate. Please ignore "fun()" when reading all this.On 04.12.2010 6:23, Andrei Alexandrescu wrote:Yes, indeed. I was a little over-enthusiastic when saying it'd work for classes too; the vtable pointer would be a problem for that. But it seems it can work well for structs. You're right, "A!Object b = a" should compile fine while "A!Object* b = &a;" should not, because it'd allow you to assign any Object to the Widget field. That said, you can still allow this convertion: "A!(const(Object))* b = &a;". That's because the obj member becomes const and you can no longer assign anything to it.On 12/3/10 7:40 PM, Michel Fortin wrote:Looks discouraging at first, but perfectly valid given that the example works only when A!Object compiles, i.e. Object have method 'method', and then:I have an idea that would fix those: make a template struct/class instance implicitly convertible to another instance of that same template if all members share the same memory layout and each member is implicitly convertible to the same member of the other template.I'm afraid that can't work. struct A(T) { T obj; void fun() { obj->method(); } }auto a = new A!Widget; a.obj = new Widget; A!Object b = *a; // works because Widget converts to Object// A!Object* b = a; // should not compile, and would be a problem if A is a class //now we have another struct b with reference to a's widgetb.obj = new Object; //no problem, a stays intact b.fun(); // since A!Object already compiles, it's perfectly validIn fact, it looks like Michel's rule is very promising, just replace "struct/class" part with "struct" in definition.So, to refine the rules I'd say a templated struct can be converted to a lvalue of another instance of the same templated struct with the same memory layout if all members can be converted to a lvalue of their type in the second struct. If one field can only be converted as a rvalue, the result is a rvalue struct. We should also make it so Widget can convert to const(Object) as an lvalue; that doesn't work currently.-- Michel Fortin michel.fortin michelf.com http://michelf.com/
Dec 04 2010
On 12/4/10 1:48 AM, Dmitry Olshansky wrote:On 04.12.2010 6:23, Andrei Alexandrescu wrote:If conversion is allowed only for values (i.e. perform a memberwise copy of one struct to another), it looks like things could work. Almost. The problem is that that surreptitious copy completely bypasses the constructor: struct A(T) { private T obj; private bool isObject; this(T obj_) { obj = obj_; static if (is(T == Object)) isObject = true; } } auto a = A!Widget(new Widget); A!Object b = a; // works, new automatic conversion rule assert(!b.isObject); // passes, invariant is messed up AndreiOn 12/3/10 7:40 PM, Michel Fortin wrote:Looks discouraging at first, but perfectly valid given that the example works only when A!Object compiles, i.e. Object have method 'method', and then:I have an idea that would fix those: make a template struct/class instance implicitly convertible to another instance of that same template if all members share the same memory layout and each member is implicitly convertible to the same member of the other template.I'm afraid that can't work. struct A(T) { T obj; void fun() { obj->method(); } }auto a = new A!Widget; a.obj = new Widget; A!Object b = *a; // works because Widget converts to Object// A!Object* b = a; // should not compile, and would be a problem if A is a class //now we have another struct b with reference to a's widgetb.obj = new Object; //no problem, a stays intact b.fun(); // since A!Object already compiles, it's perfectly validIn fact, it looks like Michel's rule is very promising, just replace "struct/class" part with "struct" in definition.
Dec 04 2010
On 2010-12-04 08:55:19 -0500, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> said:If conversion is allowed only for values (i.e. perform a memberwise copy of one struct to another), it looks like things could work. Almost. The problem is that that surreptitious copy completely bypasses the constructor: struct A(T) { private T obj; private bool isObject; this(T obj_) { obj = obj_; static if (is(T == Object)) isObject = true; } } auto a = A!Widget(new Widget); A!Object b = a; // works, new automatic conversion rule assert(!b.isObject); // passes, invariant is messed upCopying a struct always bypasses the constructor, whether it's a template or not. If you want to maintain invariants, add the "this(this)" postblit constructor. Your invariant in the case above is a little silly since it stores a value deduced from the template parameter as a member variable. But if it's that important, fixing it is trivial: add a postblit constructor. this(this) { static if (is(T == Object)) isObject = true; else isObject = false; } I'm not sure why you imply it won't work for references. That's the whole point of the proposal. It can work for references as long as the memory layout is the same and each member can also be converted as a reference. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Dec 04 2010
On 12/4/10 9:58 AM, Michel Fortin wrote:On 2010-12-04 08:55:19 -0500, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> said:That's not copying, it's moving, and it always preserves type.If conversion is allowed only for values (i.e. perform a memberwise copy of one struct to another), it looks like things could work. Almost. The problem is that that surreptitious copy completely bypasses the constructor: struct A(T) { private T obj; private bool isObject; this(T obj_) { obj = obj_; static if (is(T == Object)) isObject = true; } } auto a = A!Widget(new Widget); A!Object b = a; // works, new automatic conversion rule assert(!b.isObject); // passes, invariant is messed upCopying a struct always bypasses the constructor, whether it's a template or not.If you want to maintain invariants, add the "this(this)" postblit constructor.The postblit constructor assumes the source and target types are the same.Your invariant in the case above is a little silly since it stores a value deduced from the template parameter as a member variable. But if it's that important, fixing it is trivial: add a postblit constructor. this(this) { static if (is(T == Object)) isObject = true; else isObject = false; }The problem is the default and implicit behavior is broken.I'm not sure why you imply it won't work for references. That's the whole point of the proposal. It can work for references as long as the memory layout is the same and each member can also be converted as a reference.No. For references to work with mutable objects, you need equivariance, not contravariance. It's a classic. Andrei
Dec 04 2010
On 2010-12-04 11:06:14 -0500, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> said:On 12/4/10 9:58 AM, Michel Fortin wrote:Well, sometime this assumption is also barrier that gets in the way. I'll concede to you that it might not be desirable to allow this in all cases and we might need a way to opt-in or opt-out of this.On 2010-12-04 08:55:19 -0500, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> said:That's not copying, it's moving, and it always preserves type.If conversion is allowed only for values (i.e. perform a memberwise copy of one struct to another), it looks like things could work. Almost. The problem is that that surreptitious copy completely bypasses the constructor: struct A(T) { private T obj; private bool isObject; this(T obj_) { obj = obj_; static if (is(T == Object)) isObject = true; } } auto a = A!Widget(new Widget); A!Object b = a; // works, new automatic conversion rule assert(!b.isObject); // passes, invariant is messed upCopying a struct always bypasses the constructor, whether it's a template or not.If you want to maintain invariants, add the "this(this)" postblit constructor.The postblit constructor assumes the source and target types are the same.Your invariant in the case above is a little silly since it stores a value deduced from the template parameter as a member variable. But if it's that important, fixing it is trivial: add a postblit constructor. this(this) { static if (is(T == Object)) isObject = true; else isObject = false; }The problem is the default and implicit behavior is broken.Perhaps you should stop misinterpreting. Where did I say that references would have to work with *mutable* objects? struct A(T) { T obj; } Now, if you have a reference to "A!Widget", it's true that you can't convert it to a reference to "A!Object". What you could do however is convert it to a reference to "A!(const(Object))". The compiler would have to transitively check whether each member of the original can be converted by reference to their new type before allowing the conversion. -- Michel Fortin michel.fortin michelf.com http://michelf.com/I'm not sure why you imply it won't work for references. That's the whole point of the proposal. It can work for references as long as the memory layout is the same and each member can also be converted as a reference.No. For references to work with mutable objects, you need equivariance, not contravariance. It's a classic.
Dec 04 2010
On 05.12.2010 0:19, Michel Fortin wrote:On 2010-12-04 11:06:14 -0500, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> said:My thoughts exactly!Perhaps you should stop misinterpreting. Where did I say that references would have to work with *mutable* objects? struct A(T) { T obj; } Now, if you have a reference to "A!Widget", it's true that you can't convert it to a reference to "A!Object". What you could do however is convert it to a reference to "A!(const(Object))".I'm not sure why you imply it won't work for references. That's the whole point of the proposal. It can work for references as long as the memory layout is the same and each member can also be converted as a reference.No. For references to work with mutable objects, you need equivariance, not contravariance. It's a classic.The compiler would have to transitively check whether each member of the original can be converted by reference to their new type before allowing the conversion.But I observe there still may be some rough edges Andrei mentioned about this rule for a value types, so I wonder, can we make it an unsafe library facility? Since by the end of day, all we need (better let the compiler do it, but..) is to transitively check fields and then just cast the damn thing. -- Dmitry Olshansky
Dec 04 2010
On 12/4/10 3:19 PM, Michel Fortin wrote:On 2010-12-04 11:06:14 -0500, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> said:In fairness you changed the approach.On 12/4/10 9:58 AM, Michel Fortin wrote:Well, sometime this assumption is also barrier that gets in the way. I'll concede to you that it might not be desirable to allow this in all cases and we might need a way to opt-in or opt-out of this.On 2010-12-04 08:55:19 -0500, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> said:That's not copying, it's moving, and it always preserves type.If conversion is allowed only for values (i.e. perform a memberwise copy of one struct to another), it looks like things could work. Almost. The problem is that that surreptitious copy completely bypasses the constructor: struct A(T) { private T obj; private bool isObject; this(T obj_) { obj = obj_; static if (is(T == Object)) isObject = true; } } auto a = A!Widget(new Widget); A!Object b = a; // works, new automatic conversion rule assert(!b.isObject); // passes, invariant is messed upCopying a struct always bypasses the constructor, whether it's a template or not.If you want to maintain invariants, add the "this(this)" postblit constructor.The postblit constructor assumes the source and target types are the same.Your invariant in the case above is a little silly since it stores a value deduced from the template parameter as a member variable. But if it's that important, fixing it is trivial: add a postblit constructor. this(this) { static if (is(T == Object)) isObject = true; else isObject = false; }The problem is the default and implicit behavior is broken.Perhaps you should stop misinterpreting.I'm not sure why you imply it won't work for references. That's the whole point of the proposal. It can work for references as long as the memory layout is the same and each member can also be converted as a reference.No. For references to work with mutable objects, you need equivariance, not contravariance. It's a classic.Where did I say that references would have to work with *mutable* objects? struct A(T) { T obj; } Now, if you have a reference to "A!Widget", it's true that you can't convert it to a reference to "A!Object". What you could do however is convert it to a reference to "A!(const(Object))". The compiler would have to transitively check whether each member of the original can be converted by reference to their new type before allowing the conversion.Nagonna work. Any method inside A assumes certain types, you can't simply change all field types and just assume that methods will continue to work as intended, even if they still compile. I would agree such a rule works with a simple, controlled record type such as Tuple. Andrei
Dec 04 2010
On Sat, 04 Dec 2010 08:55:19 -0500, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:On 12/4/10 1:48 AM, Dmitry Olshansky wrote:These are the kinds of things I am afraid of with static if. Because we can change the behavior it is not safe to assume that we can simply copy or do a reinterpret-cast. I like the idea, but I think we need some sort of way to limit the scope of this feature. Can we define a new way to just template constancy? The compiler and programmer can easily reason about that. Now, you can just replace isObject with isConst, and we have the same issue if constancy is allowed to be templated. This is kind of why I didn't want to rely on templates to do tail-const -- there is just too much power there. Perhaps we can limit compile-time checking of this new const template parameter. If the compiler detects any static check on the const parameter, it doesn't allow implicit casting. Is that feasible? -SteveOn 04.12.2010 6:23, Andrei Alexandrescu wrote:If conversion is allowed only for values (i.e. perform a memberwise copy of one struct to another), it looks like things could work. Almost. The problem is that that surreptitious copy completely bypasses the constructor: struct A(T) { private T obj; private bool isObject; this(T obj_) { obj = obj_; static if (is(T == Object)) isObject = true; } }On 12/3/10 7:40 PM, Michel Fortin wrote:Looks discouraging at first, but perfectly valid given that the example works only when A!Object compiles, i.e. Object have method 'method', and then:I have an idea that would fix those: make a template struct/class instance implicitly convertible to another instance of that same template if all members share the same memory layout and each member is implicitly convertible to the same member of the other template.I'm afraid that can't work. struct A(T) { T obj; void fun() { obj->method(); } }auto a = new A!Widget; a.obj = new Widget; A!Object b = *a; // works because Widget converts to Object// A!Object* b = a; // should not compile, and would be a problem if A is a class //now we have another struct b with reference to a's widgetb.obj = new Object; //no problem, a stays intact b.fun(); // since A!Object already compiles, it's perfectly validIn fact, it looks like Michel's rule is very promising, just replace "struct/class" part with "struct" in definition.
Dec 04 2010
On Wed, 1 Dec 2010 03:17:24 -0800 Jonathan M Davis <jmdavisProg gmx.com> wrote:Various syntaxes have been proposed in the past. Syntax isn't really the =issue.=20It's pretty easy to come up with one. I think that out of the ones I've s=een,=20the I liked the best was the one proposed by Michel Fortin: =20This is the nicest proposal, imo as well. Is "ref" used here only because "C * b" would mean double indirection? Denis -- -- -- -- -- -- -- vit esse estrany =E2=98=A3 spir.wikidot.comI proposed the following a while ago. First allow the class reference =20 to (optionally) be made explicit: C a; // mutable reference to mutable class C ref b; // mutable reference to mutable class =20 And now you can apply tail-const to it: const(C)ref c; // mutable reference to const class const(C ref) d; // const reference to const class const(C) e; // const reference to const class =20
Dec 01 2010
On 2010-12-01 09:12:00 -0500, spir <denis.spir gmail.com> said:On Wed, 1 Dec 2010 03:17:24 -0800 Jonathan M Davis <jmdavisProg gmx.com> wrote:Yes. "C* b" already has the meaning of a pointer to a class reference, so using '*' would be a breaking change. Beside that, if you use the pointer syntax you'd expect to be able to use the "*b" syntax to dereference the variable... -- Michel Fortin michel.fortin michelf.com http://michelf.com/Various syntaxes have been proposed in the past. Syntax isn't really theissue.It's pretty easy to come up with one. I think that out of the ones I've seen,the I liked the best was the one proposed by Michel Fortin:This is the nicest proposal, imo as well. Is "ref" used here only because "C * b" would mean double indirection?I proposed the following a while ago. First allow the class reference to (optionally) be made explicit: C a; // mutable reference to mutable class C ref b; // mutable reference to mutable class And now you can apply tail-const to it: const(C)ref c; // mutable reference to const class const(C ref) d; // const reference to const class const(C) e; // const reference to const class
Dec 01 2010
Hello community, that is my first post here. My background is more than 10 years of C++ and to be positive I would like to say that there are a lot of things I love in D ( perhaps the object of a new thread :O) ).Speaking about D mistakes Steve spoke about missing tail const. I was thinking about this, and I fully agree that it is a hole.I fully agree with that and IMHO I think D must supports this natively. At least one facet of the problem is a "syntax" problem. Doing a parallel with C++ and concerning pointers there are 4 possible variants: // C++ 1/ int *p; 2/ int *const p; 3/ const int * p; 4/ const int *const p; In D, if I try the summarize the situation, we have : 1/ int *p; 2/ const(int)* p; 3/ forbiden due to the "const transitivity" philosophy 4/ const(int*) p; Now concerning D objects, there are some kind of "implicit pointers" (with reference counting) with no direct equivalent in C++ (and hence this problem does not occur in C++). IMHO the syntaxic problem is the consequence of a missing "place holder" for the two "const" attributes (because there is no more "*" to play with in the declaration). Perhaps one idea is to make "_" plays the role of this missing place holder. To make things clear, for a class A the D syntax would be: 1/ __ = nothing: A p ( no change ) 2/ _const(A) p; (mimic "int *const p;") 3/ const_(A) p; (mimic "const int *p;" but anyway forbiden in D due to const transitivity) 4/ const(A) p; (no change) To summarize there would be just one keyword to add in D : "_const". This attribute would have sense only for Objects (the same logic would also hold for "_immutable"). To my IMHO the syntax _const is easy to unerstand, because "_" clearly shows the place holder position and its missing "const" I hope this suggestion is not too naive and can help the debate...
Dec 01 2010
Reading back my first post I realized that there are a lot of confusions... please ignore it and consider the corrected version here: At least one facet of the problem is a "syntax" problem. Doing a parallel with C++ and concerning pointers there are 4 possible variants: // C++ 1/ int *p; 2/ const int *p; 3/ int *const p; 4/ const int *const p; In D, if I try the summarize the situation, we have (right?) : 1/ int *p; 2/ const(int)* p; 3/ ? <- not allowed because of const transitivity property 4/ const(int*) p; Now concerning D objects, there are some kind of "implicit pointers" (with reference counting) with no direct equivalent in C++ (and hence this problem does not occur in C++). IMHO the syntaxic problem is the consequence of a missing "place holder" for the two "const" attributes (because there is no more "*" to play with in the declaration). Perhaps one idea is to make "_" plays the role of this missing place holder. To make things clear, for a class A the D syntax would be: 1/ __ = nothing: A p ( no change ) 2/ const_(A) p; (mimic "const int *p;") 3/ _const(A) p; (would mimic "int *const p;" but anyway _forbiden_ in D due to const transitivity) 4/ const(A) p; (no change) To summarize there would be just one keyword to add in D : "const_". This attribute would have sense only for Objects (the same logic would also hold for "immutable_"). To my IMHO the syntax const_ is easy to unerstand, because "_" clearly shows the place holder position and its missing "const" (remembering C++) ... hope there is not more error in this post...sic
Dec 01 2010
vincent picaud <vincent.picaud laposte.net> wrote:To summarize there would be just one keyword to add in D : "const_". This attribute would have sense only for Objects (the same logic would also hold for "immutable_"). To my IMHO the syntax const_ is easy to unerstand, because "_" clearly shows the place holder position and its missing "const" (remembering C++)The issue is not with syntax (I am of the impression that most who want this, like Michel Fortin's const(A) ref a). The problem is Walter does not want to do it, as he considers it impossible to get right. Of that I'm not sure, but he's proven me wrong in the past, so it could happen again. -- Simen
Dec 01 2010
On Wednesday, December 01, 2010 06:12:00 spir wrote:On Wed, 1 Dec 2010 03:17:24 -0800 Jonathan M Davis <jmdavisProg gmx.com> wrote:* has nothing to do with references. * is for pointers. We're dealing with references here. C* would either be a pointer to a reference or a pointer to an object (I'm not sure which, technically-speaking, since it's a bit of a pain to deal with pointers and classes). Regardless, C* b already means something totally different. - Jonathan M DavisVarious syntaxes have been proposed in the past. Syntax isn't really the issue. It's pretty easy to come up with one. I think that out of the ones I've seen, the I liked the best was the one proposed by Michel Fortin:This is the nicest proposal, imo as well. Is "ref" used here only because "C * b" would mean double indirection?I proposed the following a while ago. First allow the class reference to (optionally) be made explicit: C a; // mutable reference to mutable class C ref b; // mutable reference to mutable class And now you can apply tail-const to it: const(C)ref c; // mutable reference to const class const(C ref) d; // const reference to const class const(C) e; // const reference to const class
Dec 01 2010