this() immutable

H. S. Teoh hsteoh at quickfur.ath.cx
Wed Oct 23 13:15:51 PDT 2013


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


More information about the Digitalmars-d-learn mailing list