digitalmars.D.learn - cast(immutable) vs extra variable
- Daniel Davidson (32/32) Sep 19 2013 Multi-part question:
- Namespace (4/37) Sep 19 2013 cast(immutable)data) is not an lvalue, it's a rvalue. ref accepts
- Daniel Davidson (2/4) Sep 19 2013 Thanks... about the other questions?
- Jonathan M Davis (25/38) Sep 19 2013 As Namespace pointed out, ref (including const ref) does not accept rval...
- bearophile (6/14) Sep 19 2013 The Rust language has avoided this problem, you can copy struct
- Daniel Davidson (49/69) Sep 19 2013 Ok. I think you can use in immutable(T)[], just not T[].
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (52/78) Sep 22 2013 And there are two types of read-only data:
- Daniel Davidson (13/17) Sep 23 2013 Ali, thank you for providing great feedback and suggestions. I've
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (8/16) Sep 23 2013 Actually, I've been thinking about you recently. My apologies for
Multi-part question: 1) Why does the last line fail? If cast to immutable how is it different than z? I know it is related to the ref. I'm using ref because I think it is likely more efficient - so assume the char[16] were really char[1024]. 2) If I got rid of the ref, how many copies of the data would be made? And without looking at assembly what is a good way to answer this question? I've tried to add this(this) to other Stuff type structures to print when they are being called, but that does not work because no logic can be in the default constructor. 3) Also, is storing immutable(STUFF) in a struct in the general case (as opposed to just this one) useful or silly? Thanks Dan import std.stdio; alias char[16] Stuff; struct T { immutable(Stuff) myData; this(ref immutable(Stuff) data) { myData = data; } } void main() { immutable(Stuff) iData = "1234567890123456"; Stuff data = "1234567890123456"; writeln(T(iData)); auto iData2 = cast(immutable)data; writeln(T(iData2)); // WHY DOES THIS FAIL WHEN T(iData2) works? // writeln(T(cast(immutable)data)); }
Sep 19 2013
On Thursday, 19 September 2013 at 16:47:13 UTC, Daniel Davidson wrote:Multi-part question: 1) Why does the last line fail? If cast to immutable how is it different than z? I know it is related to the ref. I'm using ref because I think it is likely more efficient - so assume the char[16] were really char[1024]. 2) If I got rid of the ref, how many copies of the data would be made? And without looking at assembly what is a good way to answer this question? I've tried to add this(this) to other Stuff type structures to print when they are being called, but that does not work because no logic can be in the default constructor. 3) Also, is storing immutable(STUFF) in a struct in the general case (as opposed to just this one) useful or silly? Thanks Dan import std.stdio; alias char[16] Stuff; struct T { immutable(Stuff) myData; this(ref immutable(Stuff) data) { myData = data; } } void main() { immutable(Stuff) iData = "1234567890123456"; Stuff data = "1234567890123456"; writeln(T(iData)); auto iData2 = cast(immutable)data; writeln(T(iData2)); // WHY DOES THIS FAIL WHEN T(iData2) works? // writeln(T(cast(immutable)data)); }cast(immutable)data) is not an lvalue, it's a rvalue. ref accepts only lvalues.
Sep 19 2013
On Thursday, 19 September 2013 at 16:50:32 UTC, Namespace wrote:cast(immutable)data) is not an lvalue, it's a rvalue. ref accepts only lvalues.Thanks... about the other questions?
Sep 19 2013
On Thursday, September 19, 2013 18:47:12 Daniel Davidson wrote:Multi-part question: 1) Why does the last line fail? If cast to immutable how is it different than z? I know it is related to the ref. I'm using ref because I think it is likely more efficient - so assume the char[16] were really char[1024].As Namespace pointed out, ref (including const ref) does not accept rvalues - only lvalues.2) If I got rid of the ref, how many copies of the data would be made? And without looking at assembly what is a good way to answer this question? I've tried to add this(this) to other Stuff type structures to print when they are being called, but that does not work because no logic can be in the default constructor.That would depend on what functions are being called and could change if the code is refactored. The compiler will do moves instead of copies whenever it can, so the situation is far better than with C++ (particularly pre-C++11), but how many copies will actually be needed depends on what the functions are doing and how many functions get called. Writing a postblit constructor would be the only way that I know to track it, though I'm not sure why the lack of default construction would be a problem, unless you're trying to name all of your objects and print out exactly how many each individual one is copied. But if you want to do that, then create a constructor that you pass the name, and don't use a default-initialized value.3) Also, is storing immutable(STUFF) in a struct in the general case (as opposed to just this one) useful or silly?As soon as you have a const or immutable member in a struct, you can't ever assign anything to it, even if all of its other members are mutable (you could assign the individual, mutable members but not the whole struct). That means stuff like if the struct is ever default-initialized, you can't even give it another value, which tends to mean that you can't use such a struct in places like arrays. All in all, I think that it's pretty much always a bad idea to have a struct with const or immutable members. It's just begging for problems. Tail-const or tail-immutable is fine, because the members themselves aren't const or immutable (just what they refer to), but having them be directly const or immutable is a bad idea IMHO. - Jonathan M Davis
Sep 19 2013
Jonathan M Davis:All in all, I think that it's pretty much always a bad idea to have a struct with const or immutable members. It's just begging for problems. Tail-const or tail-immutable is fine, because the members themselves aren't const or immutable (just what they refer to), but having them be directly const or immutable is a bad idea IMHO.The Rust language has avoided this problem, you can copy struct instances with const fields. But you can't update their const fields. Bye, bearophile
Sep 19 2013
On Thursday, 19 September 2013 at 20:05:34 UTC, Jonathan M Davis wrote:As soon as you have a const or immutable member in a struct, you can't ever assign anything to it, even if all of its other members are mutable (you could assign the individual, mutable members but not the whole struct). That means stuff like if the struct is ever default-initialized, you can't even give it another value, which tends to mean that you can't use such a struct in places like arrays.Ok. I think you can use in immutable(T)[], just not T[].All in all, I think that it's pretty much always a bad idea to have a struct with const or immutable members. It's just begging for problems. Tail-const or tail-immutable is fine, because the members themselves aren't const or immutable (just what they refer to), but having them be directly const or immutable is a bad idea IMHO.I would like to understand the tail-const/immutable argument better. Per this advice, it is ok to keep my members tail-const/immutable. But it is ok if those structs to have members that are const/immutable. So, why is it ok for members of classes that I hold but not for my members. In the face of const/immutable *transitivity* how is it more beneficial? Here is a setup: Data is coming in - say over the wire or from a database. It is very rich data with nestings of primitives, lists and string keyed associative arrays, recursively - think nested json. Once a data object is read in it is passed off to classes that use the data in read-only fashion. So, for example, assume the root of the rich data is a Portfolio instance. All members of Portfolio recursively are public since it is really plain old data from one perspective and this makes using vibe json serialization simple. Assume that a user of Portfolio data is an Analyzer. This analyzer will need to do lots of complex operations with the portfolio that it uses in readonly fashion. It may have many mutable members for intermediate calculations. Here is a mockup http://pastebin.com/nBLFDgv6 which is making use of immutable(Portfolio) to ensure that (a) the analyzer does not modify the data and (b) no other code can modify the data. To achieve this there needs to be a point in time where the modifiable Portfolio is done being read and made immutable. At this point it is cast to immutable and henceforth only accessed that way. Given this setup - what do you see as the trade-offs? What can not be done or will be challenging in face of refactor? What are some better alternatives/recommendations? I am a fan of D but my struggles with const/immutable transitivity feel endless and are wearing on me. I feel like having an ability to say something will not change and not using that ability is like being teased.
Sep 19 2013
On 09/19/2013 03:07 PM, Daniel Davidson wrote:Here is a setup: Data is coming in - say over the wire or from a database. It is very rich data with nestings of primitives, lists and string keyed associative arrays, recursively - think nested json. Once a data object is read in it is passed off to classes that use the data in read-only fashion.And there are two types of read-only data: const: A promise to not mutate immutable: A requirement that nobody mutates either.So, for example, assume the root of the rich data is a Portfolio instance. All members of Portfolio recursively are public since it is really plain old data from one perspective and this makes using vibe json serialization simple. Assume that a user of Portfolio data is an Analyzer. This analyzer will need to do lots of complex operations with the portfolio that it uses in readonly fashion.Yes, that sounds like immutable(Portfolio), as the Analyzer would not be happy if the data could mutate.It may have many mutable members for intermediate calculations. Here is a mockup http://pastebin.com/nBLFDgv6 which is making use of immutable(Portfolio) to ensure that (a) the analyzer does not modify the data and (b) no other code can modify the data. To achieve this there needs to be a point in time where the modifiable Portfolio is done being read and made immutable. At this point it is cast to immutable and henceforth only accessed that way.I have added a couple of comments to you program: import std.stdio; struct Portfolio { double[string] data; // ... } struct HedgeRecommendation { string recommendation; this(int) { recommendation = "Sell, the market is rigged"; } } struct Analyzer { // [Ali] Since you are worried about cost of copying, why not make this a // pointer as well? immutable(Portfolio)* portfolio; this(ref immutable(Portfolio) portfolio) { // [Ali] Otherwise, this assignment would copy as well. this.portfolio = &portfolio; } HedgeRecommendation createHedge() { auto result = HedgeRecommendation(1); //... return result; } } // [Ali] If possible, make this function pure so that its return value can // automatically be casted to immutable. Portfolio readPortfolio(string id) pure { // read portfolio from database return Portfolio([ "IBM" : 100.0, "SPY" : 300.0 ]); } HedgeRecommendation getHedgeRecommendation(string portfolioId) { // [Ali] No explicit cast needed because of that 'pure'. immutable(Portfolio) portfolio = readPortfolio(portfolioId); auto analyzer = Analyzer(portfolio); return analyzer.createHedge(); } void main() { writeln(getHedgeRecommendation("1234")); }Given this setup - what do you see as the trade-offs? What can not be done or will be challenging in face of refactor? What are some better alternatives/recommendations? I am a fan of D but my struggles with const/immutable transitivity feel endless and are wearing on me. I feel like having an ability to say something will not change and not using that ability is like being teased.You are not alone. I tried to answer some of these questions in my DConf 2013 talk. I think I have only scratched the surface: http://dconf.org/talks/cehreli.html Ali
Sep 22 2013
On Sunday, 22 September 2013 at 17:26:01 UTC, Ali Çehreli wrote:You are not alone. I tried to answer some of these questions in my DConf 2013 talk. I think I have only scratched the surface: http://dconf.org/talks/cehreli.html AliAli, thank you for providing great feedback and suggestions. I've stepped away from D for several months and just getting back into it. I will try to out the use of pure when returning data that is to be passed into immutable contexts. Regarding, "the why not make this a pointer as well?" for immutable(Portfolio)* portfolio - that might be good to consider. So far I have not used pointers in D directly - so just to be sure, my use of a direct pointer ensures that even if my one pointer is the last remaining reference to the data, it will not be garbage collected? I really enjoyed your talk the first time at D Conf. Now I'm enjoying it again online. Thanks.
Sep 23 2013
On 09/23/2013 06:34 AM, Daniel Davidson wrote:I've stepped away from D for several months and just getting back into it.the first time at D ConfActually, I've been thinking about you recently. My apologies for forgetting your last name. That's why I couldn't recognize you. :(I will try to out the use of pure when returning data that is to be passed into immutable contexts.I don't think there is anything wrong with marking as many functions as pure. Indeed, the compiler does that for lambdas anyway. I am not sure where things are regarding regular functions.just to be sure, my use of a direct pointer ensures that even if my one pointer is the last remaining reference to the data, it will not be garbage collected?Correct. Ali
Sep 23 2013