www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - getting rid of immutable (or const)

reply berni <someone somewhere.com> writes:
I still struggle with the concept of immutable and const:

 import std.stdio;
 
 void main()
 {
     auto p = Point(3);
     auto q = p.x;
     writeln(typeof(q).stringof);
 }
 
 struct Point
 {
      property immutable long x;
 }
The type of q is immutable(long). But I need a mutable q. I found two ways: a) long q = p.x; b) auto q = cast(long)p.x; Either way I've to specify the type "long" which I dislike (here it's not a real burdon, but with more complicated types it might be). Is there a way, to make q mutable without having to write the type explicitly?
Sep 05 2019
parent reply Daniel Kozak <kozzi11 gmail.com> writes:
On Thu, Sep 5, 2019 at 9:55 AM berni via Digitalmars-d-learn
<digitalmars-d-learn puremagic.com> wrote:
 I still struggle with the concept of immutable and const:

 import std.stdio;

 void main()
 {
     auto p = Point(3);
     auto q = p.x;
     writeln(typeof(q).stringof);
 }

 struct Point
 {
      property immutable long x;
 }
The type of q is immutable(long). But I need a mutable q. I found two ways: a) long q = p.x; b) auto q = cast(long)p.x; Either way I've to specify the type "long" which I dislike (here it's not a real burdon, but with more complicated types it might be). Is there a way, to make q mutable without having to write the type explicitly?
in this case you can just use: auto q = cast()p.x;
Sep 05 2019
next sibling parent Andrew Edwards <edwards.ac gmail.com> writes:
On Thursday, 5 September 2019 at 08:16:08 UTC, Daniel Kozak wrote:
 On Thu, Sep 5, 2019 at 9:55 AM berni via Digitalmars-d-learn 
 <digitalmars-d-learn puremagic.com> wrote:
 I still struggle with the concept of immutable and const:

 import std.stdio;

 void main()
 {
     auto p = Point(3);
     auto q = p.x;
     writeln(typeof(q).stringof);
 }

 struct Point
 {
      property immutable long x;
 }
The type of q is immutable(long). But I need a mutable q. I found two ways: a) long q = p.x; b) auto q = cast(long)p.x; Either way I've to specify the type "long" which I dislike (here it's not a real burdon, but with more complicated types it might be). Is there a way, to make q mutable without having to write the type explicitly?
in this case you can just use: auto q = cast()p.x;
or: auto q = p.x + 0;
Sep 05 2019
prev sibling parent reply berni <someone somewhere.com> writes:
On Thursday, 5 September 2019 at 08:16:08 UTC, Daniel Kozak wrote:
 in this case you can just use:

 auto q = cast()p.x;
Ahh, great! :-) But that directly gets me to the next question:
 import std.stdio;
 
 void main()
 {
     Point[] q = [Point(1),Point(3),Point(2)];
 
     import std.algorithm.searching: minElement;
     writeln(q.minElement!(a=>a.x).x);
 }
 
 struct Point
 {
     property immutable long x;
 }
This doesn't compile:
 /usr/include/dmd/phobos/std/algorithm/searching.d(1365): Error: 
 cannot modify struct extremeElement Point with immutable members
 /usr/include/dmd/phobos/std/algorithm/searching.d(1307): Error: 
 template instance `test.main.extremum!(__lambda1, "a < b", 
 Point[], Point)` error instantiating
 /usr/include/dmd/phobos/std/algorithm/searching.d(3445):        
 instantiated from here: extremum!(__lambda1, "a < b", Point[])
 test.d(8):        instantiated from here: minElement!((a) => 
 a.x, Point[])
Any idea, how to get around this?
Sep 05 2019
parent reply berni <someone somewhere.com> writes:
On Thursday, 5 September 2019 at 08:44:35 UTC, berni wrote:
 This doesn't compile:

 [...]

 Any idea, how to get around this?
Found the answer myself: q.map!(a=>a.x).minElement; :-)
Sep 05 2019
parent reply berni <someone somewhere.com> writes:
On Thursday, 5 September 2019 at 08:56:42 UTC, berni wrote:
 [..]
And one more question:
 import std.algorithm: reverse;
 writeln(q.reverse);
Here the compiler complains with:
 test.d(8): Error: template std.algorithm.mutation.reverse 
 cannot deduce function from argument types !()(Point[]), 
 candidates are:
 /usr/include/dmd/phobos/std/algorithm/mutation.d(2483):        
 std.algorithm.mutation.reverse(Range)(Range r) if 
 (isBidirectionalRange!Range && (hasSwappableElements!Range || 
 hasAssignableElements!Range && hasLength!Range && 
 isRandomAccessRange!Range || isNarrowString!Range && 
 isAssignable!(ElementType!Range)))
I allready tried to use q.dup.reverse but that didn't work either. How to get this working? (I hope I don't annoy you by asking that much questions, but I've got the feeling, that I've got only two choices: To shy away from using immutable (like I did in the last three years) or ask a lot of questions in the hope of understanding what's going on...
Sep 05 2019
parent reply Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Thursday, 5 September 2019 at 09:07:30 UTC, berni wrote:
 import std.algorithm: reverse;
 writeln(q.reverse);
How to get this working? (I hope I don't annoy you by asking that much questions, but I've got the feeling, that I've got only two choices: To shy away from using immutable (like I did in the last three years) or ask a lot of questions in the hope of understanding what's going on...
https://dlang.org/library/std/range/retro.html Difference is, retro lazily iterates in reverse order, while reverse eagerly reverses in-place. Don't worry about asking questions - it's a good way to learn, and we like helping. :) Immutable is not very well supported everywhere in the library, sadly. It seems an important building block would be something like Reassignable!T, which would hold a struct with immutable members, and still be reassignable with different values. -- Simen
Sep 05 2019
parent reply berni <someone somewhere.com> writes:
On Thursday, 5 September 2019 at 10:47:56 UTC, Simen Kjærås wrote:
 https://dlang.org/library/std/range/retro.html
Yeah, that worked. Thanks. :-)
 Don't worry about asking questions
OK. Then here's the next one:
 Point[long] q;

 q[1] = Point(3);
Leads to:
test.d(7): Error: cannot modify struct q[1L] Point with 
immutable members
Sep 05 2019
parent reply drug <drug2004 bk.ru> writes:
05.09.2019 14:17, berni пишет:
 Point[long] q;

 q[1] = Point(3);
Leads to:
 test.d(7): Error: cannot modify struct q[1L] Point with immutable members
But why do you try to modify immutable data? What is your point? Could you describe you use case?
Sep 05 2019
parent reply berni <someone somewhere.com> writes:
On Thursday, 5 September 2019 at 11:22:15 UTC, drug wrote:
 05.09.2019 14:17, berni пишет:
 Point[long] q;

 q[1] = Point(3);
Leads to:
 test.d(7): Error: cannot modify struct q[1L] Point with 
 immutable members
But why do you try to modify immutable data? What is your point? Could you describe you use case?
That's probably, what I don't understand. I've got a Point, which should not be modified. I put it in a container (q) and later I get it out there again. It should still be the same Point as before. I modify the container, not the Point, don't I?
Sep 05 2019
parent reply drug <drug2004 bk.ru> writes:
05.09.2019 14:28, berni пишет:
 On Thursday, 5 September 2019 at 11:22:15 UTC, drug wrote:
 05.09.2019 14:17, berni пишет:
 Point[long] q;

 q[1] = Point(3);
Leads to:
 test.d(7): Error: cannot modify struct q[1L] Point with immutable 
 members
But why do you try to modify immutable data? What is your point? Could you describe you use case?
That's probably, what I don't understand. I've got a Point, which should not be modified. I put it in a container (q) and later I get it out there again. It should still be the same Point as before. I modify the container, not the Point, don't I?
One solution could be using either pointer to `const(Point)` or class here (to avoid pointer using) https://run.dlang.io/is/rfKKAJ
Sep 05 2019
parent reply berni <someone somewhere.com> writes:
On Thursday, 5 September 2019 at 12:15:51 UTC, drug wrote:
 One solution could be using either pointer to `const(Point)` or 
 class here (to avoid pointer using) 
 https://run.dlang.io/is/rfKKAJ
OK. This are two solutions and although I'll probably not going to use any of those (due to other reasons), I still don't understand, why the original approach does not work. If I've got a book an put it in a box and later I'll get it out again, it's still the same book. So why has a struct changed when I put it into an AA and get it out again? It's not supposed to change...
Sep 05 2019
next sibling parent reply drug <drug2004 bk.ru> writes:
05.09.2019 15:46, berni пишет:
 On Thursday, 5 September 2019 at 12:15:51 UTC, drug wrote:
 One solution could be using either pointer to `const(Point)` or class 
 here (to avoid pointer using) https://run.dlang.io/is/rfKKAJ
OK. This are two solutions and although I'll probably not going to use any of those (due to other reasons), I still don't understand, why the original approach does not work. If I've got a book an put it in a box and later I'll get it out again, it's still the same book. So why has a struct changed when I put it into an AA and get it out again? It's not supposed to change...
Because structs are value types so when you put it into an AA you modify old value and because it is const/immutable compiler gives an error. But if you just want to initialize an AA by immutable members then this can be usefull to read https://dlang.org/spec/hash-map.html#runtime_initialization
Sep 05 2019
parent reply berni <someone somewhere.com> writes:
On Thursday, 5 September 2019 at 13:27:55 UTC, drug wrote:
 [...]when you put it into an AA you modify old value
Why?!? :-o When putting it into an AA it will be copied to a different place in memory, but the value is still the same, it's not modified. Sorry, but I still think, there is something fundamentally wrong about how I think about immutability.
 But if you just want to initialize an AA by immutable members 
 then this can be usefull to read 
 https://dlang.org/spec/hash-map.html#runtime_initialization
Well, yes and no. I want to initialize an AA with structs that contain immutable members. And that AA resides at runtime inside of a function body. I don't see, how this can be done with he approach given by the link. :-(
Sep 05 2019
next sibling parent drug <drug2004 bk.ru> writes:
05.09.2019 17:31, berni пишет:
 On Thursday, 5 September 2019 at 13:27:55 UTC, drug wrote:
 [...]when you put it into an AA you modify old value
Why?!? :-o When putting it into an AA it will be copied to a different place in memory, but the value is still the same, it's not modified. Sorry, but I still think, there is something fundamentally wrong about how I think about immutability.
Because structs are value types - assigning new value to old value means the old value modification. In case of reference types like pointers or classes assigning new value to old one also means modifying old value but this old value is a reference to value so in case of reference types only reference modified but the value isn't
 
 But if you just want to initialize an AA by immutable members then 
 this can be usefull to read 
 https://dlang.org/spec/hash-map.html#runtime_initialization
Well, yes and no. I want to initialize an AA with structs that contain immutable members. And that AA resides at runtime inside of a function body. I don't see, how this can be done with he approach given by the link. :-(
Sep 05 2019
prev sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 09/05/2019 07:31 AM, berni wrote:
 On Thursday, 5 September 2019 at 13:27:55 UTC, drug wrote:
 [...]when you put it into an AA you modify old value
Why?!? :-o When putting it into an AA it will be copied to a different place in memory,
That's the misunderstanding: The existing object is assigned over. The address is the same: void main() { int[int] aa; aa[1] = 1; const oldPointer = (1 in aa); aa[1] = 11; assert(oldPointer is (1 in aa)); // Passes }
 but the value is still the same, it's not modified.
const or immutable members make structs unassignable. Whether the members of a type are const or immutable should not be dictated by where the objects of that type will be used. If it makes sense otherwise, sure... If you are worried about existing elements being modified, you can provide a different container that wraps an AA, but provides an opIndex that returns 'ref const(T)'. That would solve the immutability of the elements in the container. Ali
Sep 05 2019
parent reply berni <someone somewhere.com> writes:
On Thursday, 5 September 2019 at 15:48:40 UTC, Ali Çehreli wrote:
 That's the misunderstanding: The existing object is assigned 
 over. The address is the same:

 void main() {
   int[int] aa;
   aa[1] = 1;
   const oldPointer = (1 in aa);
   aa[1] = 11;
   assert(oldPointer is (1 in aa));  // Passes
 }
First, I thought, I've got understood it now, but then I wrote this little program and the result was not, what I thought it would be:
 void main()
 {
     int[int] a;

     immutable int b = 17;
     a[1] = b;                          // <-- expecting error 
 here
     const oldPointer = (1 in a);
     immutable int c = 10;
     a[1] = c;
     assert(oldPointer is (1 in a));

     Point[int] d;

     immutable Point e = Point(17);
     d[1] = e;                           // <-- but error is here
 }

 struct Point
 {
     immutable int x;
 }
What's the difference? I can put an immutable int in an AA and I can overwrite it (pointer is still the same), but I can't do that with a struct, having an immutable member. When I remove that immutable inside of the struct it works. ?!? While writing this, I had an other idea, namely, that changing d[1] would make e to be something different (x inside Point is mutable this time):
 Point e = Point(17);
 d[1] = e;
 d[1] = Point(19);
 writeln(e);
But it's still 17.
 const or immutable members make structs unassignable.
But why? Here:
 Point[3] f;
 f[0] = Point(3);
I understand, that I cannot change f[0], because it's allready got a default value and that value would be overwritten. But in an aa, that member does not exist before putting the Point in there, hence there is nothing, that could be overwritten...
 Whether the members of a type are const or immutable should not 
 be dictated by where the objects of that type will be used. If 
 it makes sense otherwise, sure...
I'm not sure if I understand that right. It's sort of an advice on how to decide if one want's to make a member immutable or not, is it?
 If you are worried about existing elements being modified, you 
 can provide a different container that wraps an AA, but 
 provides an opIndex that returns 'ref const(T)'. That would 
 solve the immutability of the elements in the container.
No, I don't worry about such things. My program runs smoothly when I don't make that members immutable. And I could perfectly live with that But that's the maxim I used the last three years: "If avoidable, don't use 'immutable' at all, it only causes problems." But that is not satisfiable. Because if immutable were that useless, why would it exist at all? So I would like to understand, what's happening; being able to predict, what works and what not. At the moment it's almost always the opposite of what I think it should be...
Sep 05 2019
next sibling parent reply ag0aep6g <anonymous example.com> writes:
On 05.09.19 21:51, berni wrote:
 void main()
 {
     int[int] a;

     immutable int b = 17;
     a[1] = b;                          // <--
expecting error here
     const oldPointer = (1 in a);
     immutable int c = 10;
     a[1] = c;
     assert(oldPointer is (1 in a));

     Point[int] d;

     immutable Point e = Point(17);
     d[1] = e;                           // <--
but error is here
 }

 struct Point
 {
     immutable int x;
 }
What's the difference? I can put an immutable int in an AA and I can overwrite it (pointer is still the same),
You're not putting an immutable int into an AA. You're copying the value of an immutable int to a mutable one.
 but I can't do that with a 
 struct, having an immutable member. When I remove that immutable inside 
 of the struct it works. ?!?
`Point` is effectively the same as `immutable long`. A better simile would be this: `immutable(int)[int] a; a[1] = 17;`. And now you get the same error. You can't overwrite the element, because its immutable. You could argue that there is no element before assigning (initializing) `d[1]` for the first time. So the assignment should go through the first time, and it should only be an error when you try to write a second time. But figuring that out mechanically is hard, and DMD isn't smart enough to do it. So, to be on the safe side, it just says that you can't do that at all.
Sep 05 2019
parent berni <someone somewhere.com> writes:
On Thursday, 5 September 2019 at 20:10:03 UTC, ag0aep6g wrote:
 You're not putting an immutable int into an AA. You're copying 
 the value of an immutable int to a mutable one.

 but I can't do that with a struct, having an immutable member. 
 When I remove that immutable inside of the struct it works. ?!?
`Point` is effectively the same as `immutable long`. A better simile would be this: `immutable(int)[int] a; a[1] = 17;`. And now you get the same error. You can't overwrite the element, because its immutable.
Ah, the point is not, that my object contains an immutable element, but that the base type of the AA is a type with an immutable element! I understand now. Thanks a lot!
Sep 06 2019
prev sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 09/05/2019 12:51 PM, berni wrote:

     int[int] a;

     immutable int b = 17;
     a[1] = b;                          // <-- expecting error here
As explained elsewhere, a[1] is a mutable int. That assignment is copying b on top of the element.
     const oldPointer = (1 in a);
     immutable int c = 10;
     a[1] = c;
     assert(oldPointer is (1 in a));

     Point[int] d;

     immutable Point e = Point(17);
     d[1] = e;                           // <-- but error is here
That is the equivalent of d[1].x = e.x; It can't work because the left-hand side is immutable. (immutable or const members make objects of those types unassignable.)
 const or immutable members make structs unassignable.
But why? Here:
Otherwise the immutability guarantees woul be violated. I understand your questioning the AA design but at the lowest level it's just assignment operation. I understand that it could be "emplacement" on top of the existing element but the guarantees would be violated even then because the 'in' operator returns a pointer. Placing a new object on the same address would make existing pointer-holders unhappy.
 I understand, that I cannot change f[0], because it's allready got a
 default value and that value would be overwritten. But in an aa, that
 member does not exist before putting the Point in there, hence there is
 nothing, that could be overwritten...
Yeah, it's highly likely just assignment on a default-valued object.
 Whether the members of a type are const or immutable should not be
 dictated by where the objects of that type will be used. If it makes
 sense otherwise, sure...
I'm not sure if I understand that right. It's sort of an advice on how to decide if one want's to make a member immutable or not, is it?
If it makes for the type to have immutable (or const) members, then fine; with the understanding that objects of that type cannot be assigned or mutated any other way, we can define them like that. What I meant is, because we want to use such a type in an AA and we don't want the element to change should not dictate the type's members. Using in an AA should be yet another usage of the type.
 if immutable were that useless, why would it exist
 at all?
immutable is useful: You can have immutable objects, immutable AAs (different from what we are discussing here), etc. You can use immutable at a different level: not members but their members can be immutable. For example, a 'string' member would not be immutable itself but its chars would be immutable. There is no problem in having an AA with types having such a member: struct Person { string name; } You can assign to Person objects but their names are immutable. If you wanted a Person where the name should never change, then you could make a const(Person), immutable(Person), or provide read-only access to 'name', etc.
 So I would like to understand, what's happening; being able to
 predict, what works and what not. At the moment it's almost always the
 opposite of what I think it should be...
As a general rule, I never make members const or immutable; this is a guideline that I carried over from C++. A recent issue I had with const members in C++ has been silent skipping of move assignment of objects. Unless one uses functional programming style, that's how it should be in D as well. Otherwise, assignment is disabled and AAs don't work as expected because they use assignment under the hood as well. Ali
Sep 05 2019
parent berni <someone somewhere.com> writes:
On Thursday, 5 September 2019 at 21:22:12 UTC, Ali Çehreli wrote:
 If it makes for the type to have immutable (or const) members, 
 then fine; with the understanding that objects of that type 
 cannot be assigned or mutated any other way, we can define them 
 like that. What I meant is, because we want to use such a type 
 in an AA and we don't want the element to change should not 
 dictate the type's members. Using in an AA should be yet 
 another usage of the type.
Got it. ;-)
 if immutable were that useless, why would it exist
 at all?
immutable is useful: You can have immutable objects, immutable AAs (different from what we are discussing here), etc.
So, what I'm just trying all the time is not to have a struct with immutable elements but a immutable struct-object with mutable elements. I think, that's what I confused all the time.
 As a general rule, I never make members const or immutable; 
 this is a guideline that I carried over from C++.
I have almost no experience with C++, but I just accept that as a good advise. After all that discussion here, it seems to be sound. Thank you very much too! I think I made a big jump forward in understanding the concept of immutability in the last 24 hours. :-)
Sep 06 2019
prev sibling parent reply Kagamin <spam here.lot> writes:
On Thursday, 5 September 2019 at 12:46:06 UTC, berni wrote:
 OK. This are two solutions and although I'll probably not going 
 to use any of those (due to other reasons), I still don't 
 understand, why the original approach does not work. If I've 
 got a book an put it in a box and later I'll get it out again, 
 it's still the same book. So why has a struct changed when I 
 put it into an AA and get it out again? It's not supposed to 
 change...
Physical objects work like reference types. A place on bookshelf is at one coordinate and a book is at another coordinate, you don't copy the book, you fill a place on bookshelf with a reference to the book.
Sep 06 2019
parent berni <someone somewhere.com> writes:
On Friday, 6 September 2019 at 08:47:07 UTC, Kagamin wrote:
 Physical objects work like reference types. A place on 
 bookshelf is at one coordinate and a book is at another 
 coordinate, you don't copy the book, you fill a place on 
 bookshelf with a reference to the book.
So it's more like a piece of paper, where there is place for a note. And when assigning, I erase that note (whatever it was) and write the new note at that place. Got it! :-)
Sep 06 2019