www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - this() immutable

reply "Stephan Schiffels" <stephan_schiffels mac.com> writes:
Hi,

I have some problems with adopting my code to a breaking change 
introduced in version 2.063. Apparently, now it's not anymore 
possible to instantiate an immutable object via:

auto object = new immutable(SomeClass)(contructor_args...);

without also defining either

this(constructor_args...) immutable {...}

or

this(constructor_args...) pure {...}


As much as I think that this change will make the system more 
consistent, I don't know how to fix my code. Often, in a 
constructor you pass other objects (via constructor dependency 
injection), and it seems that I have to fix a hell of a lot of 
dependency code that way. Is there a simple way of emulating the 
way it "just magically worked" in version 2.062?

For example, is there a way of instantiating an object normally 
(i.e. mutable), and then later "freeze" it to immutable via a 
simple cast or so?

Thanks,

Stephan
Jun 13 2013
parent reply "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
On Thu, 13 Jun 2013 14:17:22 +0200, Stephan Schiffels  
<stephan_schiffels mac.com> wrote:

 For example, is there a way of instantiating an object normally (i.e.  
 mutable), and then later "freeze" it to immutable via a simple cast or  
 so?
In std.exception there is assumeUnique. It's basically just a cast, but might be good enough for you. -- Simen
Jun 13 2013
next sibling parent "Stephan Schiffels" <stephan_schiffels mac.com> writes:
On Thursday, 13 June 2013 at 12:29:57 UTC, Simen Kjaeraas wrote:
 On Thu, 13 Jun 2013 14:17:22 +0200, Stephan Schiffels 
 <stephan_schiffels mac.com> wrote:

 For example, is there a way of instantiating an object 
 normally (i.e. mutable), and then later "freeze" it to 
 immutable via a simple cast or so?
In std.exception there is assumeUnique. It's basically just a cast, but might be good enough for you.
I see, will look at that. I actually need it immutable since I am using it across multiple threads.
Jun 13 2013
prev sibling parent reply "Daniel Davidson" <nospam spam.com> writes:
On Thursday, 13 June 2013 at 12:29:57 UTC, Simen Kjaeraas wrote:
 On Thu, 13 Jun 2013 14:17:22 +0200, Stephan Schiffels 
 <stephan_schiffels mac.com> wrote:

 For example, is there a way of instantiating an object 
 normally (i.e. mutable), and then later "freeze" it to 
 immutable via a simple cast or so?
In std.exception there is assumeUnique. It's basically just a cast, but might be good enough for you.
Is there any other recourse here? Why does making `this(...) immutable` fix things below? Shouldn't that immutable designation mean no members of this will be modified? But that is the whole point of an initializer? Why does immutable make sense in this context at all? My problem is a bit more elaborate and unfortunately to initialize members I need to call standard functions that have not been made pure (but should be). struct T { int[] i; } struct S { int[] i; immutable T t; this(immutable T _t) { t = _t; } } void main() { auto t = immutable T(); auto s = immutable S(t); }
Oct 16 2013
parent reply "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
On 2013-10-16, 18:54, Daniel Davidson wrote:

 On Thursday, 13 June 2013 at 12:29:57 UTC, Simen Kjaeraas wrote:
 On Thu, 13 Jun 2013 14:17:22 +0200, Stephan Schiffels  
 <stephan_schiffels mac.com> wrote:

 For example, is there a way of instantiating an object normally (i.e.  
 mutable), and then later "freeze" it to immutable via a simple cast or  
 so?
In std.exception there is assumeUnique. It's basically just a cast, but might be good enough for you.
Is there any other recourse here? Why does making `this(...) immutable` fix things below? Shouldn't that immutable designation mean no members of this will be modified? But that is the whole point of an initializer? Why does immutable make sense in this context at all?
Immutable in the case of constructors means that the instance will be created using only data implicitly castable to immutable. That way, when construction is finished, it is safe for the type system to mark the result as immutable.
 My problem is a bit more elaborate and unfortunately to initialize  
 members I need to call standard functions that have not been made pure  
 (but should be).
If you're calling functions that are not marked pure in order to create immutable data, you will need to cast to immutable afterwards. If you know this is safe, no problem. It would benefit us all if you reported these functions or created a pull request for Phobos, of course. -- Simen
Oct 16 2013
parent reply "Daniel Davidson" <nospam spam.com> writes:
On Wednesday, 16 October 2013 at 19:55:41 UTC, Simen Kjaeraas 
wrote:
 On 2013-10-16, 18:54, Daniel Davidson wrote:

 On Thursday, 13 June 2013 at 12:29:57 UTC, Simen Kjaeraas 
 wrote:
 On Thu, 13 Jun 2013 14:17:22 +0200, Stephan Schiffels 
 <stephan_schiffels mac.com> wrote:

 For example, is there a way of instantiating an object 
 normally (i.e. mutable), and then later "freeze" it to 
 immutable via a simple cast or so?
In std.exception there is assumeUnique. It's basically just a cast, but might be good enough for you.
Is there any other recourse here? Why does making `this(...) immutable` fix things below? Shouldn't that immutable designation mean no members of this will be modified? But that is the whole point of an initializer? Why does immutable make sense in this context at all?
Immutable in the case of constructors means that the instance will be created using only data implicitly castable to immutable. That way, when construction is finished, it is safe for the type system to mark the result as immutable.
 My problem is a bit more elaborate and unfortunately to 
 initialize members I need to call standard functions that have 
 not been made pure (but should be).
If you're calling functions that are not marked pure in order to create immutable data, you will need to cast to immutable afterwards. If you know this is safe, no problem.
I'm in the learn news group for a reason. I think what you say makes sense - a cast is required. But perhaps you have more confidence that there is no problem. You and dicebot surely disagree on this practice as he sees no real reason to ever circumvent the type system.
 It would benefit us all if you reported these functions or 
 created a pull
 request for Phobos, of course.
I reported my issue with the `chain` function to this NG and tried to start annotating items used by chain with pure to see how far the thread led. Honestly it was quickly clear that it led too far for me to follow it and someone else indicated the problem had to do with Voldermort types. If there is more I could do to "benefit us all", beyond learning how it works and what to avoid in my own code - I will be glad to try.
Oct 16 2013
next sibling parent "Dicebot" <public dicebot.lv> writes:
On Wednesday, 16 October 2013 at 20:09:51 UTC, Daniel Davidson 
wrote:
 You and dicebot surely disagree on this practice as he sees no 
 real reason to ever circumvent the type system.
There are some cases were you have no other options because of language design limitations but it is something that should be done only by experienced D developers who clearly understand the impact upon the generated machine code and hidden behind the library functions. Not a casual casts in user code by any means.
Oct 16 2013
prev sibling next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Oct 16, 2013 at 10:09:50PM +0200, Daniel Davidson wrote:
[...]
 I reported my issue with the `chain` function to this NG and tried
 to start annotating items used by chain with pure to see how far the
 thread led. Honestly it was quickly clear that it led too far for me
 to follow it and someone else indicated the problem had to do with
 Voldermort types. If there is more I could do to "benefit us all",
 beyond learning how it works and what to avoid in my own code - I
 will be glad to try.
Hmm. I just did a quick-n-dirty change to Phobos, and it seems to make chain() usable with pure code. I'm not sure why the compiler didn't infer pure for it -- it should. (Or perhaps I'm missing something obvious -- I didn't run the Phobos unittest so maybe the following change breaks something.) - In the Phobos source, edit std/range.d and look for the function `auto chain(Ranges...)(Ranges rs)` (around line 2022 or thereabouts), then the struct Result inside this function. - Find the ctor for this struct (circa line 2074), and annotate it with pure. - Now the following code compiles: import std.range; auto pureFunc() pure { return chain([1,2,3], [2,3,4]); } void main() { auto r = pureFunc(); } This is just a hack, of course. The compiler *should* be able to correctly infer that the ctor is pure. So the real fix is to find out why the compiler isn't doing that. T -- Leather is waterproof. Ever see a cow with an umbrella?
Oct 16 2013
parent reply "Daniel Davidson" <nospam spam.com> writes:
On Wednesday, 16 October 2013 at 21:11:19 UTC, H. S. Teoh wrote:
 On Wed, Oct 16, 2013 at 10:09:50PM +0200, Daniel Davidson wrote:
 [...]
 I reported my issue with the `chain` function to this NG and 
 tried
 to start annotating items used by chain with pure to see how 
 far the
 thread led. Honestly it was quickly clear that it led too far 
 for me
 to follow it and someone else indicated the problem had to do 
 with
 Voldermort types. If there is more I could do to "benefit us 
 all",
 beyond learning how it works and what to avoid in my own code 
 - I
 will be glad to try.
Hmm. I just did a quick-n-dirty change to Phobos, and it seems to make chain() usable with pure code. I'm not sure why the compiler didn't infer pure for it -- it should. (Or perhaps I'm missing something obvious -- I didn't run the Phobos unittest so maybe the following change breaks something.) - In the Phobos source, edit std/range.d and look for the function `auto chain(Ranges...)(Ranges rs)` (around line 2022 or thereabouts), then the struct Result inside this function. - Find the ctor for this struct (circa line 2074), and annotate it with pure. - Now the following code compiles: import std.range; auto pureFunc() pure { return chain([1,2,3], [2,3,4]); } void main() { auto r = pureFunc(); } This is just a hack, of course. The compiler *should* be able to correctly infer that the ctor is pure. So the real fix is to find out why the compiler isn't doing that. T
I am able to see your code work. However, when I make that change and try to use chain in a pure function: foreach(dateRate; chain(trisection[1], trisection[2])) { Date earlyEnd = min(dateRate.when, end); result = moveValueInTime(result, ccRate, currentDate, earlyEnd); ccRate = dateRate.value; currentDate = earlyEnd; if(earlyEnd == end) break; } I get: pure function 'plus.tvm.rate_curve.RateCurve.scaleFromTo' cannot call impure function 'std.range.chain!(SortedRange!(const(TimePointValue!(Date, CcRate))[], "a.when < b.when"), SortedRange!(const(TimePointValue!(Date, CcRate))[], "a.when < b.when")).chain.Result.empty' So it seems more work is needed for real purity. Thanks Dan
Oct 23 2013
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Oct 23, 2013 at 09:47:00PM +0200, Daniel Davidson wrote:
 On Wednesday, 16 October 2013 at 21:11:19 UTC, H. S. Teoh wrote:
[...]
Hmm. I just did a quick-n-dirty change to Phobos, and it seems to
make chain() usable with pure code. I'm not sure why the compiler
didn't infer pure for it -- it should. (Or perhaps I'm missing
something obvious -- I didn't run the Phobos unittest so maybe the
following change breaks something.)

- In the Phobos source, edit std/range.d and look for the function
  `auto chain(Ranges...)(Ranges rs)` (around line 2022 or
  thereabouts), then the struct Result inside this function.
- Find the ctor for this struct (circa line 2074), and annotate it
  with pure.
- Now the following code compiles:

	import std.range;

	auto pureFunc() pure {
		return chain([1,2,3], [2,3,4]);
	}

	void main() {
		auto r = pureFunc();
	}

This is just a hack, of course. The compiler *should* be able to
correctly infer that the ctor is pure. So the real fix is to find out
why the compiler isn't doing that.


T
I am able to see your code work. However, when I make that change and try to use chain in a pure function: foreach(dateRate; chain(trisection[1], trisection[2])) { Date earlyEnd = min(dateRate.when, end); result = moveValueInTime(result, ccRate, currentDate, earlyEnd); ccRate = dateRate.value; currentDate = earlyEnd; if(earlyEnd == end) break; } I get: pure function 'plus.tvm.rate_curve.RateCurve.scaleFromTo' cannot call impure function 'std.range.chain!(SortedRange!(const(TimePointValue!(Date, CcRate))[], "a.when < b.when"), SortedRange!(const(TimePointValue!(Date, CcRate))[], "a.when < b.when")).chain.Result.empty' So it seems more work is needed for real purity.
[...] Well, I've since remembered that currently the compiler does not perform attribute inference for structs nested inside template functions. That's why chain doesn't work, because its return type is a struct defined inside the function, and while the function itself is correctly inferred as pure, the struct methods aren't. My fix above only addresses the construction of this nested struct, but obviously for it to be *used* by pure code, all of its methods need to be attributed as pure as well. Since the compiler currently fails to do this by inference, you'll have to manually mark all of the Result struct's methods as pure, and then you should be able to get your code to work. However, this is truly just a hack, because now that makes chain() unusable with ranges that have impure methods (the compiler will refuse to compile chain() when instantiated with such ranges, because then Result's methods will no longer be pure since they call the impure methods of the template arguments). One temporary solution that is being done in various places in Phobos is to move such nested structs out of the function proper; that is, instead of: auto chain(...)(...) { struct Result { ... } return Result(...); } move the struct out of the function and turn it into a template: struct ChainResult(...) { ... } auto chain(...)(...) { return ChainResult!(.../* compile-time args here*/)(...); } Since ChainResult is now a module-level template, the compiler will perform attribute inference on its methods, and now things should work properly. This is messy, though, and requires a largish code change in Phobos. An alternative, lazy way to fix the current compiler limitation is to make chain.Result a template of zero parameters. That is, instead of: auto chain(...)(...) { struct Result { ... } return Result(...); } turn Result into a template: auto chain(...)(...) { struct Result() { // now Result is a template of 0 parameters ... } return Result!()(...); // instantiate Result with 0 arguments } Since Result is now a template, the compiler should perform attribute inference on it. This is probably the easiest hack to work around the current compiler limitation. Again, the real fix, as I've said, is to fix the compiler so that it will do attribute inference for all declarations nested inside a template context. In the meantime, though, the above workarounds should suffice. Hopefully. T -- You are only young once, but you can stay immature indefinitely. -- azephrahel
Oct 23 2013
prev sibling next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Wednesday, October 16, 2013 14:10:02 H. S. Teoh wrote:
 This is just a hack, of course. The compiler *should* be able to
 correctly infer that the ctor is pure. So the real fix is to find out
 why the compiler isn't doing that.
Because it sucks at attribute inference. The inference that it does right now is very shallow: http://d.puremagic.com/issues/show_bug.cgi?id=10329 The compiler could use a considerable improvements with regards to how much inference it's doing for templated stuff. Without it, the attribute inference for Phobos is nowhere near good enough. - Jonathan m Davis
Oct 16 2013
prev sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Oct 16, 2013 at 02:10:02PM -0700, H. S. Teoh wrote:
 On Wed, Oct 16, 2013 at 10:09:50PM +0200, Daniel Davidson wrote:
 [...]
 I reported my issue with the `chain` function to this NG and tried
 to start annotating items used by chain with pure to see how far the
 thread led. Honestly it was quickly clear that it led too far for me
 to follow it and someone else indicated the problem had to do with
 Voldermort types. If there is more I could do to "benefit us all",
 beyond learning how it works and what to avoid in my own code - I
 will be glad to try.
Hmm. I just did a quick-n-dirty change to Phobos, and it seems to make chain() usable with pure code. I'm not sure why the compiler didn't infer pure for it -- it should. (Or perhaps I'm missing something obvious -- I didn't run the Phobos unittest so maybe the following change breaks something.)
[...] Actually, I just remembered why. It's because attribute inference only happens for the template function itself, but not for any nested structs or struct members. Arguably, the compiler should also do inference for all nested declarations in a template too. Do you have a bugzilla ticket for this issue? I'd like to add my findings to it. Thanks! T -- Without geometry, life would be pointless. -- VS
Oct 16 2013