digitalmars.D.learn - Why do immutable variables need reference counting?
- norm (11/11) Apr 10 2022 Hi All,
- rikki cattermole (5/5) Apr 10 2022 immutable isn't tied to lifetime semantics.
- norm (17/20) Apr 10 2022 This is clearly where I am misunderstanding. In my mind immutable
- =?UTF-8?Q?Ali_=c3=87ehreli?= (49/62) Apr 10 2022 Yes, it would be a bug to attempt to read data that is not live anymore.
- Salih Dincer (30/40) Apr 11 2022 2 is (already) dead.
- Paul Backus (32/62) Apr 11 2022 Because `S` does not contain any pointers or references, you are
- =?UTF-8?Q?Ali_=c3=87ehreli?= (4/5) Apr 11 2022 To add, Salih and I were in an earlier discussion where that concept
- Salih Dincer (53/58) Apr 12 2022 I tried the following and I didn't understand one thing: Why is
- =?UTF-8?Q?Ali_=c3=87ehreli?= (24/52) Apr 13 2022 In every test case that 'fileName' is char[].
- H. S. Teoh (10/14) Apr 13 2022 [...]
- wjoe (15/86) Apr 11 2022 To my understanding immutable data should reside in a separate
- =?UTF-8?Q?Ali_=c3=87ehreli?= (64/76) Apr 11 2022 We are getting into implementation details which a programming language
- Dom DiSc (10/17) Apr 12 2022 I think this second case should not be allowed. Use
- =?UTF-8?Q?Ali_=c3=87ehreli?= (21/42) Apr 12 2022 Only if there are no indirections as in 'int'. If I use a struct
- wjoe (30/75) Apr 12 2022 I was thinking during compile time. By initializing a variable
- ag0aep6g (6/16) Apr 12 2022 No, you could not. You're relying on undefined behavior there.
- wjoe (8/27) Apr 14 2022 Undefined behavior yes, but regardless the example proves it can
- ag0aep6g (7/14) Apr 14 2022 No, it cannot be done in @system code. The example only proves that you
- wjoe (8/24) Apr 17 2022 Well I'm not using Windows so I wouldn't know but I compiled an
- H. S. Teoh (9/13) Apr 17 2022 The spec explicitly makes an exception(!) in this case. See 10.24.11.3:
- ag0aep6g (7/21) Apr 17 2022 But the failing assert is not part of the unittest directly. It's in a
- ag0aep6g (9/12) Apr 17 2022 Yes.
- H. S. Teoh (10/19) Apr 17 2022 Not entirely true. See paragraph 3 in:
- wjoe (6/11) Apr 18 2022 Thanks. Either I missed that the last time I checked or it wasn't
- H. S. Teoh (6/18) Apr 18 2022 My guess: the spec was amended after the fact. :-D
- =?UTF-8?Q?Ali_=c3=87ehreli?= (18/57) Apr 12 2022 Yes! 'in' for parameters! :D
- wjoe (11/12) Apr 14 2022 Looking at this from a technical perspective - everything you say
- rikki cattermole (4/4) Apr 10 2022 Storage classes like immutable/const/shared are not tied to any memory
- IGotD- (20/25) Apr 11 2022 I was thinking about that, often when using const you use it when
- user1234 (5/16) Apr 11 2022 refcounting would require a concept of "tail const" / "tail
Hi All, I am clearly misunderstanding something fundamental, and probably obvious :D Reading some of the discussions on __metadata I was wondering if someone could explain why a immutable reference counting type is needed. By definition a reference counter cannot be immutable, so what would be the use case that requires it? It cannot really be pure nor safe either because the ref goes out of scope and the allocation is freed. How is this immutable? Thanks, Norm
Apr 10 2022
immutable isn't tied to lifetime semantics. It only says that this memory will never be modified by anyone during its lifetime. Anyway, the real problem is with const. Both mutable and immutable become it automatically.
Apr 10 2022
On Sunday, 10 April 2022 at 23:19:47 UTC, rikki cattermole wrote:immutable isn't tied to lifetime semantics. It only says that this memory will never be modified by anyone during its lifetime.This is clearly where I am misunderstanding. In my mind immutable data means the data will not change and neither will the result of reading that data, ever. I don't get how you can have thread safety guarantees based on immutable if reading that data in a thread suddenly becomes undefined behaviour and could return anything. That isn't immutable then. Once instantiated immutable data persists for the remainder of the program. You may not have access if the variable goes out of scope, but if you do it will always be there and always return the same value when you read from memory. Thanks for replying, I am not trying to be argumentative here, just stating what I thought it meant and why I am confused. I'll be doing some more reading of the D spec to better understand immutability. Cheers, Norm
Apr 10 2022
On 4/10/22 20:05, norm wrote:On Sunday, 10 April 2022 at 23:19:47 UTC, rikki cattermole wrote:In my mind immutable data means the data will not change and neither will the result of reading that data, ever.Yes.I don't get how you can have thread safety guarantees based on immutable if reading that data in a thread suddenly becomes undefined behaviour and could return anything.Yes, it would be a bug to attempt to read data that is not live anymore.That isn't immutable then.The lifetime of immutable data can start and end. import std.stdio; struct S { int i; this(int i) { this.i = i; writeln(i, " is (about to be) alive!"); } ~this() { writeln(i, " is (already) dead."); } } void main() { foreach (i; 0 .. 3) { immutable s = S(i); } } The output: 0 is (about to be) alive! 0 is (already) dead. 1 is (about to be) alive! 1 is (already) dead. 2 is (about to be) alive! 2 is (already) dead. Module-level immutable and 'static const' would live much longer but they have a lifetime as well. However, today, the destructor cannot be executed on an immutable object, so I remove the qualifiers with cast(), which sohuld be undefined behavior (today?). immutable(S) moduleS; shared static this() { moduleS = S(42); } shared static ~this() { destroy(cast()moduleS); } void main() { }Once instantiated immutable data persists for the remainder of the program.That seems to be the misunderstanding. Again, I think module-level 'immutable' and 'static const' data fits that description.You may not have access if the variable goes out of scope, but if you do it will always be there and always return the same value when you read frommemory. That description fits D's GC-owned data (including immutables). The lifetime ends when there is no reference to it. Another example is immutable messages passed between threads with std.concurrency: That kind of data clearly originates at run time and the receiving end keeps the data alive as long as it needs. Ali
Apr 10 2022
On Monday, 11 April 2022 at 03:24:11 UTC, Ali Çehreli wrote:The output: 0 is (about to be) alive! 0 is (already) dead. 1 is (about to be) alive! 1 is (already) dead. 2 is (about to be) alive! 2 is (already) dead.It worked for me in a different way.1 is (about to be) alive!2 is (already) dead. 2 is (already) dead. 2 is (already) dead.2 is (already) dead.2 is (already) dead. Hello D! 1 is (already) dead. Because I changed the code like this. ```d struct S { . . . string toString() { return ""; } } //S test(inout S s)/* S test(S s)//*/ { s.i = 2; return s; } void main() { immutable s = S(1); test(s).writeln; "Hello".writefln!"%s D!"; } ``` If the inout is set, it does not allow compilation. Thanks, SDB79
Apr 11 2022
On Monday, 11 April 2022 at 12:12:39 UTC, Salih Dincer wrote:It worked for me in a different way.Because `S` does not contain any pointers or references, you are allowed to create a mutable *copy* of an `S` from an `immutable` source. That's what happens when you pass it to `test`. The extra destructor calls come from the copies made by `test` and `writeln`. If you add a copy constructor, you can see this happening in the output: ```d struct S { /* ... */ this(ref inout typeof(this) other) inout { this.i = other.i; writeln(i, " was copied."); } } ``` ``` 1 is (about to be) alive! 1 was copied. 2 was copied. 2 is (already) dead. 2 was copied. 2 was copied. 2 was copied. 2 is (already) dead. 2 is (already) dead. 2 is (already) dead. 2 is (already) dead. Hello D! 1 is (already) dead. ``` 1 constructor call + 5 copies = 6 destructor calls.1 is (about to be) alive!2 is (already) dead. 2 is (already) dead. 2 is (already) dead.2 is (already) dead.2 is (already) dead. Hello D! 1 is (already) dead. Because I changed the code like this. ```d struct S { . . . string toString() { return ""; } } //S test(inout S s)/* S test(S s)//*/ { s.i = 2; return s; } void main() { immutable s = S(1); test(s).writeln; "Hello".writefln!"%s D!"; } ``` If the inout is set, it does not allow compilation.
Apr 11 2022
On 4/11/22 08:02, Paul Backus wrote:any pointers or referencesTo add, Salih and I were in an earlier discussion where that concept appeared as "indirections." Ali
Apr 11 2022
On Monday, 11 April 2022 at 21:48:56 UTC, Ali Çehreli wrote:On 4/11/22 08:02, Paul Backus wrote:I tried the following and I didn't understand one thing: Why is there no need to use dup when slicing? ```d struct S(T) { T fileName; this(T fileName) { this.fileName = fileName; report(); } ~this() { report(); } void report(string func = __FUNCTION__) { import std.stdio; writefln!"%s\nworking with %s"(func, fileName); } } alias T1 = const(char)[]; alias T2 = const char[]; alias T3 = const(char[]); // T3 == T2 alias T4 = immutable char[]; // Not compiling! void main() { auto fileName = "foo.txt".dup; auto s = S!T1(fileName); fileName[0..3] = "bar"; }/* Results: * *T1: * source.S!(const(char)[]).S.this working with foo.txt. source.S!(const(char)[]).S.~this working with bar.txt. * *T2: * source.S!(const(char[])).S.this working with foo.txt source.S!(const(char[])).S.~this working with bar.txt * *T3: * source.S!(const(char[])).S.this working with foo.txt source.S!(const(char[])).S.~this working with bar.txt * *T4 * Error: slice `fileName[0..3]` is not mutable */ ``` SDB 79any pointers or referencesTo add, Salih and I were in an earlier discussion where that concept appeared as "indirections." Ali
Apr 12 2022
On 4/12/22 21:34, Salih Dincer wrote:I tried the following and I didn't understand one thing: Why is there no need to use dup when slicing?I don't think I understand you fully.```d struct S(T) { T fileName; this(T fileName) { this.fileName = fileName; report(); } ~this() { report(); } void report(string func = __FUNCTION__) { import std.stdio; writefln!"%s\nworking with %s"(func, fileName); } } alias T1 = const(char)[]; alias T2 = const char[]; alias T3 = const(char[]); // T3 == T2 alias T4 = immutable char[]; // Not compiling! void main() { auto fileName = "foo.txt".dup;In every test case that 'fileName' is char[].auto s = S!T1(fileName); fileName[0..3] = "bar";For that reason, that expression will always succeed. And that's the point I tried to make: Without immutable, struct S *must* make a copy in order to guarantee that its member will remain the same.}/* Results:[...]* *T4 * Error: slice `fileName[0..3]` is not mutableYour code was different because I get an error that I expect: The following line fails. auto s = S!T4(fileName); cannot pass argument `fileName` of type `char[]` to parameter `immutable(string) fileName` Exactly! struct S wants immutable (to guarantee that nobody will mutate it) but 'fileName' in main is not immutable. You can make it pass in the T4 case a) by making an immutable copy with .idup: auto s = S!T4(fileName.idup); b) by converting 'fileName' to immutable without a copy: import std.exception; auto s = S!T4(fileName.assumeUnique); // But then, the following will fail. Awesome! // fileName[0..3] = "bar"; I am amazed everyday how great D is; such a fun engineering tool! Ali
Apr 13 2022
On Wed, Apr 13, 2022 at 08:39:17AM -0700, Ali Çehreli via Digitalmars-d-learn wrote:On 4/12/22 21:34, Salih Dincer wrote:[...] Because of two things: (1) there is a GC, and (2) characters in a string are immutable. Without (1), you will end up with either a memory leak or a dangling pointer; without (2), your slice may randomly mutate when you don't expect it to. T -- May you live all the days of your life. -- Jonathan SwiftI tried the following and I didn't understand one thing: Why is there no need to use dup when slicing?
Apr 13 2022
On Monday, 11 April 2022 at 03:24:11 UTC, Ali Çehreli wrote:On 4/10/22 20:05, norm wrote:To my understanding immutable data should reside in a separate data segment which itself could reside in ROM. So when the variable, or 'pointer' to the data, goes out of scope just the 'pointer' is gone, the actual data unreachable, but still there. Due to its immutable nature immutable data can't be changed and this, to my understanding, includes deallocation. And because the data could be in ROM any modification is an error. How would you deallocate ROM anyways? Your foreach could be unrolled at compile time, however it could easily be changed to a runtime only loop but this entire concept of creating immutable data on the fly doesn't make sense to me - that should be const's domain. Strings, I know. But the way things are, I hardly see a difference between immutable and const.On Sunday, 10 April 2022 at 23:19:47 UTC, rikki cattermolewrote:In my mind immutable data means the data will not change and neither will the result ofreadingthat data, ever.Yes.I don't get how you can have thread safety guarantees basedon immutableif reading that data in a thread suddenly becomes undefinedbehaviourand could return anything.Yes, it would be a bug to attempt to read data that is not live anymore.That isn't immutable then.The lifetime of immutable data can start and end. import std.stdio; struct S { int i; this(int i) { this.i = i; writeln(i, " is (about to be) alive!"); } ~this() { writeln(i, " is (already) dead."); } } void main() { foreach (i; 0 .. 3) { immutable s = S(i); } } The output: 0 is (about to be) alive! 0 is (already) dead. 1 is (about to be) alive! 1 is (already) dead. 2 is (about to be) alive! 2 is (already) dead. Module-level immutable and 'static const' would live much longer but they have a lifetime as well. However, today, the destructor cannot be executed on an immutable object, so I remove the qualifiers with cast(), which sohuld be undefined behavior (today?). immutable(S) moduleS; shared static this() { moduleS = S(42); } shared static ~this() { destroy(cast()moduleS); } void main() { }Once instantiated immutable data persists for the remainder of the program.That seems to be the misunderstanding. Again, I think module-level 'immutable' and 'static const' data fits that description.You may not have access if the variable goes out of scope, but if you doit willalways be there and always return the same value when youread from memory. That description fits D's GC-owned data (including immutables). The lifetime ends when there is no reference to it. Another example is immutable messages passed between threads with std.concurrency: That kind of data clearly originates at run time and the receiving end keeps the data alive as long as it needs. Ali
Apr 11 2022
On 4/11/22 05:57, wjoe wrote:To my understanding immutable data should reside in a separate data segment which itself could reside in ROM.We are getting into implementation details which a programming language acts as not to care (but has to do especially when it's a system programming language like D. :) ).So when the variable, or 'pointer' to the data, goes out of scope just the 'pointer' is gone, the actual data unreachable, but still there.I think it translates to the destructor never being executed. Otherwise, nobody would even know whether the memory location is reused for other pursposes later on.Due to its immutable nature immutable data can't be changed and this, to my understanding, includes deallocation.D one language where object lifetime is deliberately separate from memory allocation.And because the data could be in ROM any modification is an error.Fully agreed. However, how could I initialize such an object then? (You may have meant a read-only memory page instead of ROM.)immutable data on the fly doesn't make sense to me - that should be const's domain.Even 'const' cause confusions because it's used in at least two different ways (even e.g. in C++): 1) I will not mutate data through this reference. For example, a parameter that is pointer to const achieves that: void foo (const(int)[] arr); 2) This variable is const: const i = 42; Well, there is the confusion: There is no "reference" in the second case at all! I don't agree with you when you say immutability should be const's domain because const covers item 1 above as well, where there is no immutability of data whatsoever. The data may be perfectly mutable or immutable, where my access will be readonly. Perhaps you are saying the same thing but enters D's immutable: The data is immutable. immutable data on the fly can be very useful because e.g. it makes multithreaded programming trivial in some cases by removing the need for locking.Strings, I know. But the way things are, I hardly see a difference between immutable and const.Imagine a File struct that holds on to a file name. In C++, we would have to make a copy of the constructor argument for two reasons: 1) Lifetime of the object might be short. This is not a problem in D because of the GC. 2) The data might change after I start holding on to it through a reference. This is not a problem *only if* data were immutable because my const parameter cannot preclude the producer from mutating it further. Example: import std.stdio; import std.format; struct S { const(char)[] fileName; this(const(char)[] fileName) { this.fileName = fileName; report(); } ~this() { report(); } void report(string func = __FUNCTION__) { writefln!"%s working with %s."(func, fileName); } } void main() { char[] fileName = "foo.txt".dup; auto s = S(fileName); fileName[0..3] = "bar"; } The output shows that the file name changed between construction and destruction: deneme.S.this working with foo.txt. deneme.S.~this working with bar.txt. If fileName were immutable, then the owner would not be able to mutate anyway, so the struct could get away without copying the file name. Ali
Apr 11 2022
On Monday, 11 April 2022 at 22:10:07 UTC, Ali Çehreli wrote:1) I will not mutate data through this reference. For example, a parameter that is pointer to const achieves that: void foo (const(int)[] arr); 2) This variable is const: const i = 42; Well, there is the confusion: There is no "reference" in the second case at all!I think this second case should not be allowed. Use immutable i = 42; instead. The meaning is identical, but we could remove the burden of two different meanings from const if it is not allowed. const should only be allowed in function declarations. A variable must be declared either mutable or immutable. It's only functions that may guarantee not to modify a parameter or the objects they belong to, and so are allowed to work on both mutable and immutable objects.
Apr 12 2022
On 4/12/22 03:28, Dom DiSc wrote:On Monday, 11 April 2022 at 22:10:07 UTC, Ali Çehreli wrote:Only if there are no indirections as in 'int'. If I use a struct struct S { const(char[]) arr; } void foo(const(char[]) arr) { immutable s = immutable(S)(arr); // <-- ERROR: // Error: cannot implicitly convert expression `arr` of type `const(char[])` to `immutable(string)` } void main() { int[] arr; foo(arr); } The reason is a property of immutable that I like to describe as "selective" (or "demanding"). The struct variable cannot be immutable because it would be demanding that its constructor argument be immutable, which cannot be because foo's 'const' parameter hides that information even when it were immutable e.g. in main().1) I will not mutate data through this reference. For example, a parameter that is pointer to const achieves that: void foo (const(int)[] arr); 2) This variable is const: const i = 42; Well, there is the confusion: There is no "reference" in the second case at all!I think this second case should not be allowed. Use immutable i = 42; instead. The meaning is identical,but we could remove the burden of two different meanings from const if it is not allowed. const should only be allowed in function declarations. A variable must be declared either mutable or immutable. It's only functions that may guarantee not to modify a parameter or the objects they belong to, and so are allowed to work on both mutable and immutable objects.I understand but the guarantee of immutable seems to leave no other choice. Ali
Apr 12 2022
On Monday, 11 April 2022 at 22:10:07 UTC, Ali Çehreli wrote:On 4/11/22 05:57, wjoe wrote:I was thinking during compile time. By initializing a variable with immutable data or a pointer that points to an address e.G. an EEPROM.And because the data could be in ROM any modification is an error.Fully agreed. However, how could I initialize such an object then? (You may have meant a read-only memory page instead of ROM.)Even 'const' cause confusions because it's used in at least two different ways (even e.g. in C++): 1) I will not mutate data through this reference. For example, a parameter that is pointer to const achieves that: void foo (const(int)[] arr); 2) This variable is const: const i = 42; Well, there is the confusion: There is no "reference" in the second case at all!In general, I guess, it's a bad idea to reuse the same word for 2 or more distinctly different ideas. Maybe the const keyword in 1) should have a better name. Especially since it's only a promise and the compiler accepts this: void foo (const(char)[] arr) { cast(char[])arr[0..3] = "baz"; } string bar = "123"; foo(bar); assert(bar=="baz"); But I could cast away const and modify the string bar. So with that saidI don't agree with you when you say immutability should be const's domain because const covers item 1 above as well, where there is no immutability of data whatsoever. The data may be perfectly mutable or immutable, where my access will be readonly.When I said immutability should be the domain of const I am referring only to 2). I.e. immutable is constant data which is created at compile time - like laws of physics, const as in 2) is constant data which is created at run time - like man made laws, and 1) should get a better name - maybe 'in' and get rid of const. And to be able to use immutable anywhere other than where immutable is explicitly specified, a copy is necessary. I know it's not as simple as that. But one can dream, right? :)producer from mutating it further. Example: import std.stdio; import std.format; struct S { const(char)[] fileName; this(const(char)[] fileName) { this.fileName = fileName; report(); } ~this() { report(); } void report(string func = __FUNCTION__) { writefln!"%s working with %s."(func, fileName); } } void main() { char[] fileName = "foo.txt".dup; auto s = S(fileName); fileName[0..3] = "bar"; } If fileName were immutable, then the owner would not be able to mutate anyway, so the struct could get away without copying the file name. AliI presume you refer to fileName in main() ? And if yes, if it were const, it couldn't be mutated either, so isn't immutable and const sort of synonymous in that case or am I missing your point?
Apr 12 2022
On Tuesday, 12 April 2022 at 19:54:13 UTC, wjoe wrote:Especially since it's only a promise and the compiler accepts this: void foo (const(char)[] arr) { cast(char[])arr[0..3] = "baz"; } string bar = "123"; foo(bar); assert(bar=="baz"); But I could cast away const and modify the string bar.No, you could not. You're relying on undefined behavior there. Just because the compiler accepts something, doesn't mean it's ok. If you want to be guarded against wandering into undefined territory, that's what safe does. With safe, the cast doesn't compile.
Apr 12 2022
On Tuesday, 12 April 2022 at 22:23:18 UTC, ag0aep6g wrote:On Tuesday, 12 April 2022 at 19:54:13 UTC, wjoe wrote:Undefined behavior yes, but regardless the example proves it can be done in system code. A few versions ago, possibly due to a bug or regression, the compiler didn't complain in safe code either. Of course you are correct academically. However, since it's possible, I'd wager my last hat that code like this is out in the wild.Especially since it's only a promise and the compiler accepts this: void foo (const(char)[] arr) { cast(char[])arr[0..3] = "baz"; } string bar = "123"; foo(bar); assert(bar=="baz"); But I could cast away const and modify the string bar.No, you could not. You're relying on undefined behavior there. Just because the compiler accepts something, doesn't mean it's ok. If you want to be guarded against wandering into undefined territory, that's what safe does. With safe, the cast doesn't compile.
Apr 14 2022
On 14.04.22 13:42, wjoe wrote:Undefined behavior yes, but regardless the example proves it can be done in system code. A few versions ago, possibly due to a bug or regression, the compiler didn't complain in safe code either. Of course you are correct academically. However, since it's possible, I'd wager my last hat that code like this is out in the wild.No, it cannot be done in system code. The example only proves that you can write nonsense code that has no defined meaning. Note that the nonsense you wrote behaves as you describe only in Windows. Elsewhere, you get a segfault. But it doesn't matter how the executable actually behaves. You cannot cite the result of undefined behavior when arguing language semantics.
Apr 14 2022
On Thursday, 14 April 2022 at 12:10:04 UTC, ag0aep6g wrote:On 14.04.22 13:42, wjoe wrote:Well I'm not using Windows so I wouldn't know but I compiled an ran that program on Linux and it didn't segfault. If it had I wouldn't have included that part in my reply. On the matter of undefined behavior. Technically a program is in undefined behavior land after throwing an error, thus every unittest that continues after assertThrown is therefore nonsense code, is it not ?Undefined behavior yes, but regardless the example proves it can be done in system code. A few versions ago, possibly due to a bug or regression, the compiler didn't complain in safe code either. Of course you are correct academically. However, since it's possible, I'd wager my last hat that code like this is out in the wild.No, it cannot be done in system code. The example only proves that you can write nonsense code that has no defined meaning. Note that the nonsense you wrote behaves as you describe only in Windows. Elsewhere, you get a segfault. But it doesn't matter how the executable actually behaves. You cannot cite the result of undefined behavior when arguing language semantics.
Apr 17 2022
On Sun, Apr 17, 2022 at 01:06:36PM +0000, wjoe via Digitalmars-d-learn wrote: [...]On the matter of undefined behavior. Technically a program is in undefined behavior land after throwing an error, thus every unittest that continues after assertThrown is therefore nonsense code, is it not ?The spec explicitly makes an exception(!) in this case. See 10.24.11.3: https://dlang.org/spec/expression.html#assert_expressions as well as 27.3: https://dlang.org/spec/unittest.html T -- An elephant: A mouse built to government specifications. -- Robert Heinlein
Apr 17 2022
On 17.04.22 15:27, H. S. Teoh wrote:On Sun, Apr 17, 2022 at 01:06:36PM +0000, wjoe via Digitalmars-d-learn wrote: [...]But the failing assert is not part of the unittest directly. It's in a function that is called from the unittest. It takes a generous interpretation of the spec to include such asserts in the exception. Letting failing asserts cause UB is a mistake anyway. It stems from a misguided desire to re-use asserts for optimization, when nobody actually uses them that way.On the matter of undefined behavior. Technically a program is in undefined behavior land after throwing an error, thus every unittest that continues after assertThrown is therefore nonsense code, is it not ?The spec explicitly makes an exception(!) in this case. See 10.24.11.3: https://dlang.org/spec/expression.html#assert_expressions as well as 27.3: https://dlang.org/spec/unittest.html
Apr 17 2022
On 17.04.22 15:06, wjoe wrote:On the matter of undefined behavior. Technically a program is in undefined behavior land after throwing an error, thus every unittest that continues after assertThrown is therefore nonsense code, is it not ?Yes. Failing asserts are a messy part of the language. They are supposed to be: 1) not catchable, because they indicate a bug in the program; 2) catchable in order to be testable; 3) assumed impossible for optimization purposes. Those goals are at odds with each other, and I don't think the spec manages to consolidate them. But mutating `immutable` data is not messy. It's simply not allowed.
Apr 17 2022
On Sun, Apr 17, 2022 at 04:09:12PM +0200, ag0aep6g via Digitalmars-d-learn wrote: [...]Failing asserts are a messy part of the language. They are supposed to be: 1) not catchable, because they indicate a bug in the program; 2) catchable in order to be testable; 3) assumed impossible for optimization purposes. Those goals are at odds with each other, and I don't think the spec manages to consolidate them.Not entirely true. See paragraph 3 in: https://dlang.org/spec/unittest.html and 10.24.11.3 in: https://dlang.org/spec/expression.html#assert_expressions T -- Latin's a dead language, as dead as can be; it killed off all the Romans, and now it's killing me! -- Schoolboy
Apr 17 2022
On Sunday, 17 April 2022 at 14:14:37 UTC, H. S. Teoh wrote:Not entirely true. See paragraph 3 in: https://dlang.org/spec/unittest.html and 10.24.11.3 in: https://dlang.org/spec/expression.html#assert_expressions TThanks. Either I missed that the last time I checked or it wasn't there ;) But the interesting question is: does the compiler go out of its way to make that happen or is it just paragraphs written in the spec?
Apr 18 2022
On Mon, Apr 18, 2022 at 12:55:26PM +0000, wjoe via Digitalmars-d-learn wrote:On Sunday, 17 April 2022 at 14:14:37 UTC, H. S. Teoh wrote:[...]Not entirely true. See paragraph 3 in: https://dlang.org/spec/unittest.html and 10.24.11.3 in: https://dlang.org/spec/expression.html#assert_expressionsThanks. Either I missed that the last time I checked or it wasn't there ;) But the interesting question is: does the compiler go out of its way to make that happen or is it just paragraphs written in the spec?My guess: the spec was amended after the fact. :-D T -- Those who don't understand D are condemned to reinvent it, poorly. -- Daniel N
Apr 18 2022
On 4/12/22 12:54, wjoe wrote:I.e. immutable is constant data which is created at compile time - like laws of physics,For completeness, we already have 'enum' and 'static const' for that.should get a better name - maybe 'in' and get rid of const.Yes! 'in' for parameters! :DNo, I meant the member 'fileName'. If the member is 'const' (as in C++ and as in the example above), you have to take a copy because the caller may mutate. (The example is demonstrating the case where the struct assumes data won't changed and gets surprised by the mutation.)import std.stdio; import std.format; struct S { const(char)[] fileName; this(const(char)[] fileName) { this.fileName = fileName; report(); } ~this() { report(); } void report(string func = __FUNCTION__) { writefln!"%s working with %s."(func, fileName); } } void main() { char[] fileName = "foo.txt".dup; auto s = S(fileName); fileName[0..3] = "bar"; } If fileName were immutable, then the owner would not be able to mutate anyway, so the struct could get away without copying the file name. AliI presume you refer to fileName in main() ?And if yes, if it were const, it couldn't be mutated either,You started as assuming 'fileName' in main, so, I will continue with that. If the struct had 'const fileName' and pleaded that the caller also define a 'const' variable, that would be wishful thinking. The struct cannot trust that all callers will obey that plea. That kind of trust (more like guarantee) comes with 'immutable' and this is a good example of how immutable is different from const. (immutable does not exist in some languages like C++.)so isn't immutable and const sort of synonymous in that case or am I missing your point?If on the other hand, the member were immutable, the caller would have to provide immutable data and the struct could rely on the fact that the data was immutable forever. No need for a copy... Ali
Apr 12 2022
On Tuesday, 12 April 2022 at 23:23:59 UTC, Ali Çehreli wrote:[...]Looking at this from a technical perspective - everything you say is true - and thanks for clearing up some of my confusion in that department. Looking at this from a natural language (English) perspective - words prompt ideas and expectations. If these expectations aren't met confusion and misunderstanding happens (and that's not just true for software development). Although, perhaps, that's only a problem for (some) non native English speakers ? The fact remains that there's confusion and misunderstandings regarding const, immutable and const 2.
Apr 14 2022
Storage classes like immutable/const/shared are not tied to any memory management strategy. Nor does it dictate memory lifetime. It only dictates how it can be interacted with when you have a reference to it.
Apr 10 2022
On Sunday, 10 April 2022 at 23:19:47 UTC, rikki cattermole wrote:immutable isn't tied to lifetime semantics. It only says that this memory will never be modified by anyone during its lifetime. Anyway, the real problem is with const. Both mutable and immutable become it automatically.I was thinking about that, often when using const you use it when passing parameters to functions. This is essentially borrowing. The situation is similar with C++ with unique_ptr and shared_ptr. Often C++ interfaces use const* when using pointers and not their smart pointer counterparts, so essentially the ownership remains, while "borrowing" are using raw pointers. A C++ interface only accepts the smart pointers when you want to change ownership. D could use a similar approach, when using pointers/references you shouldn't alter the internal data including reference count. What I would interested in is if D could have move by default depending on type. In this case the RC pointer wrapper could be move by default. Increasing is only done when calling "clone" (similar to Rust). This way RC increases are optimized naturally. What I don't want from Rust is the runtime aliasing check (RefCell) on at all times. I rather go with that the compiler assumes no aliasing but the programmer is responsible for this. You can have runtime aliasing/borrowing check in debug mode but in release build it can be removed. This is similar to bounds checking where you can choose to have it or not.
Apr 11 2022
On Sunday, 10 April 2022 at 23:05:24 UTC, norm wrote:Hi All, I am clearly misunderstanding something fundamental, and probably obvious :D Reading some of the discussions on __metadata I was wondering if someone could explain why a immutable reference counting type is needed. By definition a reference counter cannot be immutable, so what would be the use case that requires it? It cannot really be pure nor safe either because the ref goes out of scope and the allocation is freed. How is this immutable? Thanks, Normrefcounting would require a concept of "tail const" / "tail immutable" so that transitivity of the qualifier does not affect the data used to refcount (basically the field that hold the count) but only the data that **are** refcounted.
Apr 11 2022