This post will tell about the unique chip D - Voldemort types. Types that can be used but not named. This name is not very suitable for them, but Walter Bright likes to call them that. Voldemort types are very common in the standard library Phobos, especially in the modules
std.algorithm and
std.array . Master D can spend hours studying documentation in search of the type returned from the splitter or joiner, and they return exactly the Voldemort types. After this post, you can safely open the source code std.algorithm, because no You-Know-Who will not be afraid of you.

Sometimes, the interaction of existing opportunities can lead to unexpected surprises. I like to think that we originally laid the Voldemort types in D, but in fact they were found by Andrei Alexandrescu. What are these Voldermort types? Read on.
')
First, some background information. To understand the material, you must have an idea of
Ranges . As a first approximation, they were designed as a replacement for iterators and are very similar to the C # approach with its IEnumerable interface. In D, any type that implements the following methods will be InputRange:
front - get the first element of the range popFront - remove the first element of the range empty - are there more elements in the range?
These methods implement the basis for iteration by type. Now just fo fun, let's design an InputRange that returns an infinite sequence of random numbers. (We also call these functions generators.)
It may look like this (not a very good random number generator, but it will soon be):
module rnd; struct RandomNumberGenerator { this(uint seed) { next = seed; popFront();
And the function that returns it:
RandomNumberGenerator generator(uint seed) { return RandomNumberGenerator(seed); }
And a great program that prints 10 such numbers:
import std.stdio; import rnd; void main() { int count; foreach (n; generator(5)) { writeln(n); if (++count == 10) break; } }
This is usually all and stop. But there are a few annoying moments. Ideally, I should only know about the rnd.generator function, but in the module there is a type RandomNumberGenerator that can exist by itself. It looks like a violation of encapsulation, as it seeps out of my generator abstraction.
I could mark it with the attribute private and other modules besides rnd would not be able to access it. But this type is still here, outside the zone where it belongs, and other members of the module can still access it, whether it is blocked or not (in D private announcements are not hidden from other announcements inside one module).
We now turn to more fun things. First, D supports type inference for declarations, so I can write this:
auto g = RandomNumberGenerator(seed);
And g will be automatically assigned the type RandomNumberGenerator. This is a standard thing. Tugging this thread a little more, and we can deduce the types returned from the functions:
auto square(double d) { return d * d; } auto x = square(2.3);
And the compiler will understand that the function square will return double, since this is the type of expression after return. And of course, the variable x will also be of type double. Now let's rewrite our function for the generator like this:
module rnd; auto generator(uint seed) { struct RandomNumberGenerator { @property int front() { return ((seed / 0x10000) * seed) >> 16; } void popFront() { seed = seed * 1103515245 + 12345; } @property bool empty() { return false; } } RandomNumberGenerator g; g.popFront();
Something charming has happened. The RandomNumberGenerator has become a type that is inside the region of the generator function. It is simply not visible outside the function. Nor can it be named - this is the Voldemort type.
We can only get an instance of this type:
auto g = generator(5);
And then use g. I know what you're thinking of - use typeof and create another instance of the RandomNumberGenerator:
auto g = generator(4); typeof(g) h;
Sorry, it will not work, the compiler will not allow Voldermort to declare a type outside its scope (technical reason - there is no access to the local seed variable).
Now there is only one detail that annoys me, the cycle:
int count; foreach (n; generator(5)) { writeln(n); if (++count == 10) break; }
He looks so old-fashioned. Using the ranges, we can do without the thick loop, and use range
take instead to simply take the first 10 elements of this range:
void main() { foreach (n; take(generator(5), 10)) writeln(n); }
And then use
writeln to completely get rid of the cycle:
void main() { writeln(take(generator(5), 10)); }
Are the Voldemort types really only existential types?
Given type T, and type U, which can be extracted from T and which is in a certain relation c T, is an existential type.
For example, if we have type T, which is a pointer, we can derive from it the base existential type U using:
import std.stdio; void extractU(T)(T t) { static if (is(TU : U*)) writefln("type %s is a pointer to an %s", typeid(T), typeid(U)); else writefln("type %s is not a pointer", typeid(T)); } void main() { int* t; extractU(t); double d; extractU(d); }
What will display:
type int* is a pointer to an int type double is not a pointer
While the Voldemort types unconditionally hide their implementation, this, however, does not make them existential types. Since Voldemort types cannot be derived from a certain über type, they are not existential.
Conclusion
Voldemort types have become a remarkable discovery in D, which allowed us to encapsulate types so that they can be used but not called.
Source: Walter Whight
Voldemort Types In D