December 15, 2012
On Saturday, 15 December 2012 at 21:10:19 UTC, Jonathan M Davis wrote:
> On Saturday, December 15, 2012 12:18:21 H. S. Teoh wrote:
>> It seems that the only clean way to do this is to use a class instead of
>> a struct, since the .init value will conveniently just be null, thereby
>> sidestepping the problem.
>
> That would incur unnecessary overhead and probably break all kinds of code,
> because they're then reference types instead of value types, and a _lot_ of
> code doesn't use save propperly. If structs can't do what we need as Voldemort
> types, it's just better to make it so that they're not Voldemort types.
> Voldemort types are a cute idea, and in principle are great, but I don't think
> that it's worth fighting to keep them if they have problems.
>
> - Jonathan M Davis

I always found them inconsistent with the behavior they have in classes (where no outer pointer is created).

This is a lot of work to do in the standard lib however.
December 15, 2012
On Sat, Dec 15, 2012 at 01:09:33PM -0800, Jonathan M Davis wrote:
> On Saturday, December 15, 2012 12:18:21 H. S. Teoh wrote:
> > It seems that the only clean way to do this is to use a class instead of a struct, since the .init value will conveniently just be null, thereby sidestepping the problem.
> 
> That would incur unnecessary overhead and probably break all kinds of code, because they're then reference types instead of value types, and a _lot_ of code doesn't use save propperly. If structs can't do what we need as Voldemort types, it's just better to make it so that they're not Voldemort types.  Voldemort types are a cute idea, and in principle are great, but I don't think that it's worth fighting to keep them if they have problems.
[...]

Well, the current way many Phobos ranges work, they're kind of pseudo-Voldemort types (except they don't actually need/use the context pointer):

	auto cycle(R)(R range) {
		struct CycleImpl {
			R r;
			this(R _r) { r = _r; }
			... // range methods
		}
		return CycleImpl(range);
	}

	auto r = cycle(myRange);

While it's true that you can write:

	typeof(cycle(myRange)) s;

and thereby break encapsulation, if someone is desperate enough to do such things they probably have a good reason for it, and they should be able to deal with the consequences too.

But anyway, thinking a bit more about the .init problem, couldn't we just say that .init is not accessible outside the scope of the function that defines the type, and therefore you cannot declare a variable of that type (using typeof or whatever other workaround) without also assigning it to an already-initialized instance of the type?

This way, the type still has an .init, except that it's only accessible inside the function itself. Or are there unintended consequences here?


T

-- 
It is impossible to make anything foolproof because fools are so ingenious. -- Sammy
December 15, 2012
On Saturday, December 15, 2012 13:44:13 H. S. Teoh wrote:
> But anyway, thinking a bit more about the .init problem, couldn't we just say that .init is not accessible outside the scope of the function that defines the type, and therefore you cannot declare a variable of that type (using typeof or whatever other workaround) without also assigning it to an already-initialized instance of the type?
> 
> This way, the type still has an .init, except that it's only accessible inside the function itself. Or are there unintended consequences here?

That's pretty much how it is now. The problem is all of the stuff that requires .init. For instance, lots of template constraints and static if use .init to check stuff about a type. Not having an .init gets in the way of a lot of template metaprogramming. And sometimes, you have to have .init or some things are impossible.

For instance, takeNone will attempt to return an empty range of the given type (something which some algorithms need, or they won't work), and if the range doesn't have init or slicing, then it's forced to return the result of takeExactly, which is then a different range type. For some stuff, that's fine, for other stuff, that renders it unusable. I think that the only place that that affects Phobos at the moment is that you lose an optimization path in one of find's overloads, but I've been in other situations where I've had to make a range empty without changing its type and popping all of the elements off was unacceptable, and Voldemort types make that impossible.

We may be able to work around enough of the problems caused by a lack of a usable init property to be able to continue to use Voldemort types, but some issues can't be fixed with them (like that of takeNone), and until we do find a solution for even the problems that we can fix, the lack of an init property tends to be crippling for metaprogramming.

- Jonathan M Davis
December 15, 2012
On 12/15/2012 10:36 AM, H. S. Teoh wrote:
> With latest git dmd:
>
> 	auto makeVoldemort(int x) {
> 		struct Voldemort {
> 			@property int value() { return x; }
> 		}
> 		return Voldemort();
> 	}
> 	void main() {
> 		auto v = makeVoldemort();
> 		writeln(v.value);
> 	}
>
> Compile error:
>
> 	test.d(3): Error: function test.makeVoldemort.Voldemort.value cannot access frame of function test.makeVoldemort

This compiles if the @property is elided.

Definitely a bug.

December 16, 2012
Good finds.

The definition of a "nested struct" is not consistently or well defined, so there's no wonder it's not working as anyone expects.

--rt
December 16, 2012
On 12/15/2012 10:44 PM, H. S. Teoh wrote:
> ...
> This way, the type still has an .init, except that it's only accessible
> inside the function itself. Or are there unintended consequences here?
>

Lazy initialization of a member of such a type would require unsafe language features and not work in CTFE.

December 16, 2012
On Saturday, 15 December 2012 at 20:20:10 UTC, H. S. Teoh wrote:
> On Sat, Dec 15, 2012 at 12:02:16PM -0800, Jonathan M Davis wrote:
>> On Saturday, December 15, 2012 11:45:10 H. S. Teoh wrote:
>> > Ironically enough, Andrei in the subsequent paragraph discourages
>> > the use of such nested structs, whereas Walter's article promotes
>> > the use of such Voldemort types as a "happy discovery". :)
>> 
>> No, the real irony is that it's Andrei who promoted them in the first
>> place. :)
>
> Heh. :)
>
>
>> We _are_ finding some serious issues with them though (e.g. they don't
>> work with init and apparently can't work with init), and there has
>> been some discussion of ditching them due to such issues, but no
>> consensus has been reached on that.
> [...]
>
> Hmm, that's true. They can't possibly work with init: if you do
> something like:
>
> 	auto func(int x) {
> 		struct V {
> 			@property int value() { return x; }
> 		}
> 		return V();
> 	}
> 	auto v = func(123);
> 	auto u = v.init;	// <-- what happens here?
> 	writeln(u.value);	// <-- and here?
>
> It seems that we'd have to make .init illegal on these Voldemort
> structs. But that breaks consistency with the rest of the language that
> every type must have an .init value.
>
> Alternatively, .init can implicitly create a context where the value of
> x is set to int.init. But this is unnecessary (it's supposed to be a
> *Voldemort* type!) and very ugly (why are we creating a function's local
> context when it isn't even being called?), not to mention useless
> (u.value will return a nonsensical value).
>
> Or, perhaps a less intrusive workaround, is to have .init set the hidden
> context pointer to null, and you'll get a null deference when accessing
> u.value. Which is not pretty either, since no pointers or references are
> apparent in V.value; it's implicit.
>
> It seems that the only clean way to do this is to use a class instead of
> a struct, since the .init value will conveniently just be null, thereby
> sidestepping the problem.
>
>
> T


	auto func(int x) {
		struct V {
			int value() { return x; }

			@property V init() { return this; }
		}

		return V();
	}
	auto v = func(123);
	auto u = v.init;	// <-- what happens here?
	auto x = u.value;
	writeln(x);

If you add an init property then it is not null. I'm not sure if this is the correct behavior as I just jumped into this discussion and I'm not sure if I understand exactly what the problem with these types of structs are. (as far as I can tell they simply let you pass "sets" of data from a function rather easily while also keeping the details hidden(impossible to directly instantiate the struct since it is hidden inside the func)

In any case, when stepping through the code above, I noticed that init being called but resulting in u.value throwing a null exception. so I added an init property to V and then got the expected behavior. Probably too easy to be the correct solution but who knows ;)
December 16, 2012
As an alternative possible solution,

The following allows efficient nested structs in classes:

http://dpaste.dzfl.pl/64025e0a

It is very similar to the idea of voldemort structs except that instead of outer being a function it is a class.

Therefore, it makes sense that one could simply use functors in replace of the functions to get the same behavior(I'm not sure if D supports functors yet as I haven't got that far but I imagine it does).

To be useful, of course, one would have to use static functors so instantiation would not be necessary and direct replacement could be observed only if functor notation is identical to function notation.
December 17, 2012
I have recently added a note about "init property is sometimes unsafe".

https://github.com/D-Programming-Language/d-programming-language.org/pull/201


To reduce the risk, I have proposed an enhancement for .init property usage.

http://d.puremagic.com/issues/show_bug.cgi?id=8752

Kenji Hara

2012/12/16 js.mdnq <js_adddot+mdng@gmail.com>

> On Saturday, 15 December 2012 at 20:20:10 UTC, H. S. Teoh wrote:
>
>> On Sat, Dec 15, 2012 at 12:02:16PM -0800, Jonathan M Davis wrote:
>>
>>> On Saturday, December 15, 2012 11:45:10 H. S. Teoh wrote:
>>> > Ironically enough, Andrei in the subsequent paragraph > discourages the use of such nested structs, whereas Walter's article > promotes the use of such Voldemort types as a "happy discovery". :)
>>>
>>> No, the real irony is that it's Andrei who promoted them in the first place. :)
>>>
>>
>> Heh. :)
>>
>>
>>
>>  We _are_ finding some serious issues with them though (e.g. they don't
>>> work with init and apparently can't work with init), and there has been some discussion of ditching them due to such issues, but no consensus has been reached on that.
>>>
>> [...]
>>
>>
>> Hmm, that's true. They can't possibly work with init: if you do something like:
>>
>>         auto func(int x) {
>>                 struct V {
>>                         @property int value() { return x; }
>>                 }
>>                 return V();
>>         }
>>         auto v = func(123);
>>         auto u = v.init;        // <-- what happens here?
>>         writeln(u.value);       // <-- and here?
>>
>> It seems that we'd have to make .init illegal on these Voldemort structs. But that breaks consistency with the rest of the language that every type must have an .init value.
>>
>> Alternatively, .init can implicitly create a context where the value of x is set to int.init. But this is unnecessary (it's supposed to be a *Voldemort* type!) and very ugly (why are we creating a function's local context when it isn't even being called?), not to mention useless (u.value will return a nonsensical value).
>>
>> Or, perhaps a less intrusive workaround, is to have .init set the hidden context pointer to null, and you'll get a null deference when accessing u.value. Which is not pretty either, since no pointers or references are apparent in V.value; it's implicit.
>>
>> It seems that the only clean way to do this is to use a class instead of a struct, since the .init value will conveniently just be null, thereby sidestepping the problem.
>>
>>
>> T
>>
>
>
>         auto func(int x) {
>                 struct V {
>                         int value() { return x; }
>
>                         @property V init() { return this; }
>
>                 }
>
>                 return V();
>         }
>         auto v = func(123);
>         auto u = v.init;        // <-- what happens here?
>         auto x = u.value;
>         writeln(x);
>
> If you add an init property then it is not null. I'm not sure if this is the correct behavior as I just jumped into this discussion and I'm not sure if I understand exactly what the problem with these types of structs are. (as far as I can tell they simply let you pass "sets" of data from a function rather easily while also keeping the details hidden(impossible to directly instantiate the struct since it is hidden inside the func)
>
> In any case, when stepping through the code above, I noticed that init being called but resulting in u.value throwing a null exception. so I added an init property to V and then got the expected behavior. Probably too easy to be the correct solution but who knows ;)
>


1 2
Next ›   Last »