digitalmars.D.learn - const confusion
- Witold Baryluk (86/86) May 31 2009 Content-Disposition: inline
- Jarrett Billingsley (11/87) May 31 2009 able object,
- Witold Baryluk (29/39) Jun 01 2009 Thanks. I was already thinking about implementing something like this.
- Jarrett Billingsley (8/47) Jun 01 2009 g references of local variables?
- Steven Schveighoffer (24/64) Jun 01 2009 You are running into limitations that are planned to be fixed.
- Witold Baryluk (51/145) Jun 23 2009 I have curently something like this in my implementation of rebindable.
Content-Disposition: inline Content-Transfer-Encoding: quoted-printable Hi, i haven't been here for so long time. So hi all. I'm back again. I was considering myself as hardcore D hacker, but in one point I fail completly. Constness. Consider this simple code: module ct; class C { const int a; const C b; this(int a_) { a =3D a_; b =3D null; } this(int a_, in cord b_) { a =3D a_; b =3D b_; } C m() const { // line 16 return b; } } void main() { C c1 =3D new C(1); C c2 =3D new C(2, c1); c2 =3D c2.m(); // 23 } So I have class C which all methods are "const". So it is completly immutab= le object, one can say. The problem is with line 16, I define function m which is const (because it doesn't change anything). But i'm returing some part of it, so actually som= one else can change it hypotetically Actually it is impossible for two reasons: * all fields are const, * there is no non-consts methods beside constructors. Of course compiler says here: (dmd 2.028) ct.d(16): Error: cannot implicitly convert expression (this.b) of type cons= t(C) to ct.C =20 so we change line 16 to: const(C) m() const { // line 16 And I can agree that this is correct solution. but then there is another problem, now in main() function: ct.d(23): Error: cannot implicitly convert expression (c2.m()) of type cons= t(C) to ct.C So now, i need to change all occurence of C to const(C), ok no problem: void main() { const(C) c1 =3D new C(1); const(C) c2 =3D new C(2, c1); c2 =3D c2.m(); // line 23 } Ok, nice. I can create some alias so it will be easier to use, also. But now is another problem: ct.d(23): Error: variable ct.main.c2 cannot modify const But I wan't to have possibility to change local reference to this class! Shouldn't const be about content of object? Ok, go and just create new variable: void main() { const(C) c1 =3D new C(1); const(C) c2 =3D new C(2, c1); auto c3 =3D c2.m(); // line 23 } compiler now will be happy. Ok, it is good for functional style of programing, but consider this: void main() { const(C) c1 =3D new C(1); const(C) c2 =3D new C(2, c1); while (c2 !is null) { c2 =3D c2.m(); // line 23 } writefln(c2.a); } So now i need to write tail recursive version of this loop, because compiler doen't allow me to reuse variable. I can write tail recursive loop, but it's: * not so efficient * users of my library are not familiary with functional programing * it's unnatural. Horiblle. How to ensure constness of data, and still have possibility of changing ref= erences of local variables? --=20 Witold Baryluk <baryluk smp.if.uj.edu.pl>
May 31 2009
On Sun, May 31, 2009 at 3:26 PM, Witold Baryluk <baryluk smp.if.uj.edu.pl> wrote:Hi, i haven't been here for so long time. So hi all. I'm back again. I was considering myself as hardcore D hacker, but in one point I fail completly. Constness. Consider this simple code: module ct; class C { =A0 =A0 =A0 =A0const int a; =A0 =A0 =A0 =A0const C b; =A0 =A0 =A0 =A0this(int a_) { =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0a =3D a_; =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0b =3D null; =A0 =A0 =A0 =A0} =A0 =A0 =A0 =A0this(int a_, in cord b_) { =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0a =3D a_; =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0b =3D b_; =A0 =A0 =A0 =A0} =A0 =A0 =A0 =A0C m() const { // line 16 =A0 =A0 =A0 =A0 =A0 =A0return b; =A0 =A0 =A0 =A0} } void main() { =A0 =A0 =A0 =A0C c1 =3D new C(1); =A0 =A0 =A0 =A0C c2 =3D new C(2, c1); =A0 =A0 =A0 =A0c2 =3D c2.m(); // 23 } So I have class C which all methods are "const". So it is completly immut=able object,one can say. The problem is with line 16, I define function m which is const (because =itdoesn't change anything). But i'm returing some part of it, so actually s=omoneelse can change it hypotetically Actually it is impossible for two reasons: =A0* all fields are const, =A0* there is no non-consts methods beside constructors. Of course compiler says here: (dmd 2.028) ct.d(16): Error: cannot implicitly convert expression (this.b) of type co=nst(C) to ct.Cso we change line 16 to: const(C) m() const { // line 16 And I can agree that this is correct solution. but then there is another problem, now in main() function: ct.d(23): Error: cannot implicitly convert expression (c2.m()) of type co=nst(C) to ct.CSo now, i need to change all occurence of C to const(C), ok no problem: void main() { =A0 =A0 =A0 =A0const(C) c1 =3D new C(1); =A0 =A0 =A0 =A0const(C) c2 =3D new C(2, c1); =A0 =A0 =A0 =A0c2 =3D c2.m(); =A0// line 23 } Ok, nice. I can create some alias so it will be easier to use, also. But now is another problem: ct.d(23): Error: variable ct.main.c2 cannot modify const But I wan't to have possibility to change local reference to this class! Shouldn't const be about content of object? Ok, go and just create new variable: void main() { =A0 =A0 =A0 =A0const(C) c1 =3D new C(1); =A0 =A0 =A0 =A0const(C) c2 =3D new C(2, c1); =A0 =A0 =A0 =A0auto c3 =3D c2.m(); =A0// line 23 } compiler now will be happy. Ok, it is good for functional style of programing, but consider this: void main() { =A0 =A0 =A0 =A0const(C) c1 =3D new C(1); =A0 =A0 =A0 =A0const(C) c2 =3D new C(2, c1); =A0 =A0 =A0 =A0while (c2 !is null) { =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0c2 =3D c2.m(); =A0// line 23 =A0 =A0 =A0 =A0} =A0 =A0 =A0 =A0writefln(c2.a); } So now i need to write tail recursive version of this loop, because compi=lerdoen't allow me to reuse variable. I can write tail recursive loop, but it's: =A0* not so efficient =A0* users of my library are not familiary with functional programing =A0* it's unnatural. Horiblle. How to ensure constness of data, and still have possibility of changing r=eferences of local variables? Rebindable. http://www.digitalmars.com/d/2.0/phobos/std_typecons.html#Rebindable
May 31 2009
Dnia 2009-05-31, nie o godzinie 15:36 -0400, Jarrett Billingsley pisze:On Sun, May 31, 2009 at 3:26 PM, Witold Baryluk <baryluk smp.if.uj.edu.pl> wrote:Thanks. I was already thinking about implementing something like this. It is very thin, and probably doesn't eat even single byte more than original reference. So generally we need to cheat: union with const and non-const version + opAssign/opDot, and some hidden casts. If everybody is doing this, why not. Only one problem is that i need to make some wrappers for it: alias Rebindable!(C) CC; first try: auto c1 = CC(new C(1)); auto c2 = CC(new C(2, c1)); // oops doesn't work c2 = c2.b(); second try: auto c1 = CC(new C(1)); auto c2 = CC(new C(2, c1.opDot())); // ok, works c2 = c2.b(); define some function on original data: int something(in C c) { return c.a; } something(c2); // oops, doesn't work something(c2.opDot()); // ok, works So generally now i need to overload all my functions to support also Rebindable!(C), where I will unwrap object and call original function? The same with constructors. Can't be this done more simpler? As I remember there was something like opCast (for explicit casts)? Maybe Rebindable should have it casting to original type (with const)?Horrible. How to ensure constness of data, and still have possibility of changing references of local variables?Rebindable. http://www.digitalmars.com/d/2.0/phobos/std_typecons.html#Rebindable
Jun 01 2009
On Mon, Jun 1, 2009 at 4:01 PM, Witold Baryluk <baryluk smp.if.uj.edu.pl> w= rote:Dnia 2009-05-31, nie o godzinie 15:36 -0400, Jarrett Billingsley pisze:g references of local variables?On Sun, May 31, 2009 at 3:26 PM, Witold Baryluk <baryluk smp.if.uj.edu.pl> wrote:Horrible. How to ensure constness of data, and still have possibility of changin=This seems like a perfect application for opImplicitCast, a feature that has been bandied about for years and which Andrei seems to have hinted at coming soon. Using implicit casts, you would be able to make a perfectly transparent wrapper type such as Rebindable. For now, you're stuck overloading on Rebindable!(T) :\Rebindable. http://www.digitalmars.com/d/2.0/phobos/std_typecons.html#RebindableThanks. I was already thinking about implementing something like this. It is very thin, and probably doesn't eat even single byte more than original reference. So generally we need to cheat: union with const and non-const version + opAssign/opDot, and some hidden casts. If everybody is doing this, why not. Only one problem is that i need to make some wrappers for it: alias Rebindable!(C) CC; first try: auto c1 =3D CC(new C(1)); auto c2 =3D CC(new C(2, c1)); // oops doesn't work c2 =3D c2.b(); second try: auto c1 =3D CC(new C(1)); auto c2 =3D CC(new C(2, c1.opDot())); // ok, works c2 =3D c2.b(); define some function on original data: int something(in C c) { =A0 =A0 =A0 =A0return c.a; } something(c2); // oops, doesn't work something(c2.opDot()); // ok, works So generally now i need to overload all my functions to support also Rebindable!(C), where I will unwrap object and call original function? The same with constructors. Can't be this done more simpler? As I remember there was something like opCast (for explicit casts)? Maybe Rebindable should have it casting to original type (with const)?
Jun 01 2009
On Mon, 01 Jun 2009 16:01:04 -0400, Witold Baryluk <baryluk smp.if.uj.edu.pl> wrote:Dnia 2009-05-31, nie o godzinie 15:36 -0400, Jarrett Billingsley pisze:You are running into limitations that are planned to be fixed. For example, rebindable probably shouldn't use opDot anymore... it should use alias this. With opDot, you don't have implicit casting back to the original type. But alias this provides that, not sure if aliasing a union member has been tested... Also, I believe Rebindable!(const C) is what you really want (I've argued in the past that Rebindable should just assume that it's type should be const). Rebindable!(T) is just an alias to T if T is not const, which is IMO absolutely useless. Another thing I just noticed, which probably should be fixed, there is an alias for get which gets the original item. get's a pretty common member name, I don't think it should be overridden by Rebindable. In fact, I think rebindable needs almost a rewrite with the recent developments of D2. The goal of Rebindable is to transparently implement the sort of "tail-const" behavior you want without any of the pain you are currently experiencing. If it doesn't work seamlessly (except for where you wish to explicitly define "this is a rebindable reference"), then it's not finished. Andrei? -SteveOn Sun, May 31, 2009 at 3:26 PM, Witold Baryluk <baryluk smp.if.uj.edu.pl> wrote:Thanks. I was already thinking about implementing something like this. It is very thin, and probably doesn't eat even single byte more than original reference. So generally we need to cheat: union with const and non-const version + opAssign/opDot, and some hidden casts. If everybody is doing this, why not. Only one problem is that i need to make some wrappers for it: alias Rebindable!(C) CC; first try: auto c1 = CC(new C(1)); auto c2 = CC(new C(2, c1)); // oops doesn't work c2 = c2.b(); second try: auto c1 = CC(new C(1)); auto c2 = CC(new C(2, c1.opDot())); // ok, works c2 = c2.b(); define some function on original data: int something(in C c) { return c.a; } something(c2); // oops, doesn't work something(c2.opDot()); // ok, works So generally now i need to overload all my functions to support also Rebindable!(C), where I will unwrap object and call original function? The same with constructors. Can't be this done more simpler? As I remember there was something like opCast (for explicit casts)? Maybe Rebindable should have it casting to original type (with const)?Horrible. How to ensure constness of data, and still have possibility ofchanging references of local variables? Rebindable. http://www.digitalmars.com/d/2.0/phobos/std_typecons.html#Rebindable
Jun 01 2009
Dnia 2009-06-01, pon o godzinie 16:44 -0400, Steven Schveighoffer pisze:On Mon, 01 Jun 2009 16:01:04 -0400, Witold Baryluk <baryluk smp.if.uj.edu.pl> wrote:I have curently something like this in my implementation of rebindable. //alias Rebindable!(const C) CC; // todo: opAssign(Rebindable!(T)) is missing so c4 = c3 // todo: T opCast() is missing for explicit cast(T) // todo: opdot needs const // because of this here is own Rebindable template RBMixin(T, RetT) { static if (is(T X == const(U), U) || is(T X == invariant(U), U)) { private union { T original; U stripped; } RetT opAssign(T another) { stripped = cast(U)another; return this; } RetT opAssign(RetT another) { stripped = another.stripped; return this; } static RetT opCall(T initializer) { //return RetT(initializer); RetT result; result = initializer; return result; } T opDot() const { return original; } static if (!is(T.opCast)) { T opCast() { return original; } } } } template RB(T) { static if (!is(T X == const(U), U) && !is(T X == invariant(U), U)) { alias T RB; } else { struct RB { mixin RBMixin!(T, RB); alias original get; // legacy get } } } Then i can just use RB!(const!(mytype)), or mixin it into own struct. I think i will rewrite it to use "alias this", and remove some other limitation What you think?Dnia 2009-05-31, nie o godzinie 15:36 -0400, Jarrett Billingsley pisze:You are running into limitations that are planned to be fixed. For example, rebindable probably shouldn't use opDot anymore... it should use alias this. With opDot, you don't have implicit casting back to the original type. But alias this provides that, not sure if aliasing a union member has been tested... Also, I believe Rebindable!(const C) is what you really want (I've argued in the past that Rebindable should just assume that it's type should be const). Rebindable!(T) is just an alias to T if T is not const, which is IMO absolutely useless. Another thing I just noticed, which probably should be fixed, there is an alias for get which gets the original item. get's a pretty common member name, I don't think it should be overridden by Rebindable. In fact, I think rebindable needs almost a rewrite with the recent developments of D2. The goal of Rebindable is to transparently implement the sort of "tail-const" behavior you want without any of the pain you are currently experiencing. If it doesn't work seamlessly (except for where you wish to explicitly define "this is a rebindable reference"), then it's not finished. Andrei?On Sun, May 31, 2009 at 3:26 PM, Witold Baryluk <baryluk smp.if.uj.edu.pl> wrote:Thanks. I was already thinking about implementing something like this. It is very thin, and probably doesn't eat even single byte more than original reference. So generally we need to cheat: union with const and non-const version + opAssign/opDot, and some hidden casts. If everybody is doing this, why not. Only one problem is that i need to make some wrappers for it: alias Rebindable!(C) CC; first try: auto c1 = CC(new C(1)); auto c2 = CC(new C(2, c1)); // oops doesn't work c2 = c2.b(); second try: auto c1 = CC(new C(1)); auto c2 = CC(new C(2, c1.opDot())); // ok, works c2 = c2.b(); define some function on original data: int something(in C c) { return c.a; } something(c2); // oops, doesn't work something(c2.opDot()); // ok, works So generally now i need to overload all my functions to support also Rebindable!(C), where I will unwrap object and call original function? The same with constructors. Can't be this done more simpler? As I remember there was something like opCast (for explicit casts)? Maybe Rebindable should have it casting to original type (with const)?Horrible. How to ensure constness of data, and still have possibility ofchanging references of local variables? Rebindable. http://www.digitalmars.com/d/2.0/phobos/std_typecons.html#Rebindable-Steve
Jun 23 2009