www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - cast(immutable) vs extra variable

reply "Daniel Davidson" <nospam spam.com> writes:
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
next sibling parent reply "Namespace" <rswhite4 googlemail.com> writes:
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
parent "Daniel Davidson" <nospam spam.com> writes:
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
prev sibling parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
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
next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
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
prev sibling parent reply "Daniel Davidson" <nospam spam.com> writes:
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
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
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
parent reply "Daniel Davidson" <nospam spam.com> writes:
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

 Ali
Ali, 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
parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
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 Conf
Actually, 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