Re: Why do immutable variables need reference counting?
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
Re: Why do immutable variables need reference counting?
On 4/11/22 08:02, Paul Backus wrote: > any pointers or references To add, Salih and I were in an earlier discussion where that concept appeared as "indirections." Ali
Re: Why do immutable variables need reference counting?
On Monday, 11 April 2022 at 12:12:39 UTC, Salih Dincer wrote: 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. 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.
Re: arsd-minigui - couple of questions
On Monday, 11 April 2022 at 13:14:16 UTC, Adam D Ruppe wrote: On Monday, 11 April 2022 at 12:40:29 UTC, sai wrote: One more request, is it possible for you to do to ImageBox what you did to Button to show the transparent images (png)? Because Imagebox is showing black color for transparent pixels. oh yeah this one is different because the imagebox custom draws it, so it is really as simple as passing `true` to the opacity there too when it converts. Try this diff: https://github.com/adamdruppe/arsd/commit/912047ccdda7e7775f64431a631519e6024d2494 i think it fixes it, let me know. Done, it works. Awesome thanks.
Re: arsd-minigui - couple of questions
On Monday, 11 April 2022 at 12:40:29 UTC, sai wrote: One more request, is it possible for you to do to ImageBox what you did to Button to show the transparent images (png)? Because Imagebox is showing black color for transparent pixels. oh yeah this one is different because the imagebox custom draws it, so it is really as simple as passing `true` to the opacity there too when it converts. Try this diff: https://github.com/adamdruppe/arsd/commit/912047ccdda7e7775f64431a631519e6024d2494 i think it fixes it, let me know.
Re: Why do immutable variables need reference counting?
On Monday, 11 April 2022 at 03:24:11 UTC, Ali Çehreli wrote: 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 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 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.
Re: arsd-minigui - couple of questions
One more request, is it possible for you to do to ImageBox what you did to Button to show the transparent images (png)? Because Imagebox is showing black color for transparent pixels. I tried to see the git history to see if I can make that change to Imagebox and submit a pull request, but I am lost and don't think I understand the code well enough.
Re: Importing version identifiers from another file?
On Monday, 11 April 2022 at 08:57:12 UTC, KytoDragon wrote: [...] Sadly this results in an identifier conflict, as the version set in config.d does not seem to affect library.d. Is there any way to import version specifiers from a separate file? I don't want to pollute the users build files (dub.json etc.) with dozens of versions they need to pass to the compiler. No, version identifiers don't escape module scope. See 24.1.4 https://dlang.org/spec/version.html for reference.
Re: Why do immutable variables need reference counting?
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
Re: Lambdas Scope
On 11.04.22 11:11, Salih Dincer wrote: How is this possible? Why is it compiled? Don't the same names in the same scope conflict? ```d int function(int) square; void main() { square = (int a) => a * a; int square = 5.square; assert(square == 25); } ``` Thanks, SDB@79 - Local variables in a function can hide variables in less nested scopes. (Such hiding is only an error for nested block scopes within the same function.) - UFCS always looks up names in module scope, it does not even check locals.
Re: Lambdas Scope
On Monday, 11 April 2022 at 09:11:06 UTC, Salih Dincer wrote: How is this possible? Why is it compiled? Don't the same names in the same scope conflict? ```d int function(int) square; void main() { square = (int a) => a * a; int square = 5.square; assert(square == 25); } ``` Thanks, SDB@79 you can use explicit call parenthesis to make the difference between the local var and the global func. Also you have the module access operator and the fully qualified name as alternatives to select the one you wish to.
Lambdas Scope
How is this possible? Why is it compiled? Don't the same names in the same scope conflict? ```d int function(int) square; void main() { square = (int a) => a * a; int square = 5.square; assert(square == 25); } ``` Thanks, SDB@79
Re: Why do immutable variables need reference counting?
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, Norm refcounting 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.
Importing version identifiers from another file?
I am currently maintaining a port of a c++ library that uses conditional compilation to integrate into the users program. e.g.: config.h (edited by the user of the library) ``` #define USE_MY_ASSERT void MY_ASSERT(bool expr) {...} ``` library.c ``` include "config.h" #ifndef USE_MY_ASSERT void MY_ASSERT(bool expr) {...} // default implementation #end ``` Given that static if at top-level has been broken for some time (https://issues.dlang.org/show_bug.cgi?id=17883, https://issues.dlang.org/show_bug.cgi?id=20905, https://issues.dlang.org/show_bug.cgi?id=21171), I tried to use version identifiers for this in D: config.d ``` version = USE_MY_ASSERT; void MY_ASSERT(bool expr) {...} ``` library.d ``` import config; version (USE_MY_ASSERT) {} else { void MY_ASSERT(bool expr) {...} } ``` Sadly this results in an identifier conflict, as the version set in config.d does not seem to affect library.d. Is there any way to import version specifiers from a separate file? I don't want to pollute the users build files (dub.json etc.) with dozens of versions they need to pass to the compiler.
Re: Why do immutable variables need reference counting?
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.