Difference between revisions of "Voldemort types"

From D Wiki
Jump to: navigation, search
(Purpose: grammar)
(Side effects: Another grammar fix)
 
(11 intermediate revisions by 6 users not shown)
Line 7: Line 7:
 
<syntaxhighlight lang=D>
 
<syntaxhighlight lang=D>
 
// Note: the return type is auto, because we cannot actually name it outside the function!
 
// Note: the return type is auto, because we cannot actually name it outside the function!
auto createVoldemortType()
+
auto createVoldemortType(int value)
 
{
 
{
 
     struct TheUnnameable
 
     struct TheUnnameable
 
     {
 
     {
         int nameableMember=100;
+
         int getValue() { return value; }
 
     }
 
     }
     return TheUnnameable(123);
+
     return TheUnnameable();
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
Line 20: Line 20:
  
 
<syntaxhighlight lang=D>
 
<syntaxhighlight lang=D>
auto voldemort = createVoldemortType();
+
auto voldemort = createVoldemortType(123);
writeln(voldemort.nameableMember);  // prints 123
+
writeln(voldemort.getValue());  // prints 123
 
</syntaxhighlight>
 
</syntaxhighlight>
  
The type of the variable voldemort cannot be explicitly named here, because that name is only accessible inside the function createVoldemortType. However, we can still declare variables of that type by using D's static type inference.
+
The type of the variable voldemort cannot be explicitly named here, because that name is only accessible inside the function createVoldemortType. Furthermore, instances of TheUnnameable cannot be created outside of this function, because its definition relies on a local variable in the function. However, we can still declare variables of that type by using D's static type inference, to store the function's return value.
  
 
==Purpose==
 
==Purpose==
Line 30: Line 30:
 
{{sectionstub}}
 
{{sectionstub}}
  
Voldemort types are widely used in [[Phobos]]'s range-based functions. They are used as internal types that implement that particular range function. Voldemort types were chosen for this in order to improve encapsulation: users of the standard library don't need to, and shouldn't need to, know how a particular range function was implemented.
+
Voldemort types are widely used in [[Phobos]]'s range-based functions. They are used as internal types that implement that particular range function. Voldemort types were chosen for this in order to improve encapsulation: users of the standard library don't need to, and shouldn't need to, know how a particular range function was implemented and instead the simple knowledge of the primitives implemented by the return type is sufficient.
  
In fact, some of the range-based functions will return ''different'' types based on what kind of range was passed in. Rather than forcing the user to remember which type to use for which occasion, the use of Voldemort types forces the user to depend on static type inference, and thus automatically get the right type every time.
+
In fact, some of the range-based functions will return ''different'' types based on what kind of range was passed in. This happens when the function that contains a Voldemort type is actually a templatized function.
 +
 
 +
Rather than forcing the user to remember which type to use for which occasion, the use of Voldemort types forces the user to depend on static type inference, and thus automatically get the right type every time.
  
 
This also allows the Phobos developers to change the underlying implementation without requiring all users to update their code. Otherwise, every time the implementation changes you will have to update every instance of the type to refer to the correct type name. Using Voldemort types alleviates this problem by letting the compiler fill in the correct type for you automatically.
 
This also allows the Phobos developers to change the underlying implementation without requiring all users to update their code. Otherwise, every time the implementation changes you will have to update every instance of the type to refer to the correct type name. Using Voldemort types alleviates this problem by letting the compiler fill in the correct type for you automatically.
  
==Subverting unnameability==
+
==Side effects==
 
 
Although we cannot directly name a Voldemort type, there is an indirect way to refer to it using the '''typeof''' operator:
 
 
 
<syntaxhighlight lang=D>
 
// Declares voldemort to be of the same type as the
 
// return type of the function createVoldemortType.
 
typeof(createVoldemortType()) voldemort;
 
 
 
writeln(voldemort.nameableMember);  // prints 100
 
</syntaxhighlight>
 
 
 
Here, we've successfully created an instance of the Voldemort type without being able to refer to its type name directly. We can even use an alias to make our code more readable:
 
 
 
<syntaxhighlight lang=D>
 
alias typeof(createVoldemortType()) TheNowNameable;
 
 
 
// Look, ma! We now have an explicit name for the Voldemort type:
 
TheNowNameable voldemort;
 
writeln(voldemort);  // prints 100
 
</syntaxhighlight>
 
 
 
What's the point of creating a name for a Voldemort type, when the whole point of a Voldemort type, as mentioned in the previous section, is so that you don't need to know what type is? This is sometimes needed when you're writing your own generic code, and need to store instances of a type that's actually ''unknown'' at the time of writing:
 
 
 
<syntaxhighlight lang=D>
 
struct MyRangeWrapper(R)
 
{
 
    // Note: we actually have no way of knowing what type std.range.cycle will
 
    // return, because it may give us a different type depending on what R is.
 
    alias typeof(std.range.cycle(R.init)) CycledRange;
 
  
    // But we need a name for whatever type that is, because we need to store a
+
Since Voldemort types are often implemented in templatized functions that are used in a CTFE fashion, their usage tends to create huge monolithic symbols that are not useful when debugging and that increase the binary size. In extreme cases the mangle of a call chain could occupy up to 1 MB!
    // variable of that type:
 
    CycledRange cr;
 
  
    this(R range)
+
Solutions exist. One of them would be to change the way that DMD generates the mangles, using a [https://forum.dlang.org/post/nhqivh$2enl$1@digitalmars.com specialized compressor]. Another solution, [http://www.schveiguy.com/blog/2016/05/have-your-voldemort-types-and-keep-your-disk-space-too/ proposed by Steve Schveighoffer], is to move the Voldemort out of the function body.
    {
 
        cr = std.range.cycle(range);
 
    }
 
    ... // other wrapper functions go here
 
}
 
</syntaxhighlight>
 
  
 
==See also==
 
==See also==
  
 +
* [http://www.drdobbs.com/cpp/voldemort-types-in-d/232901591 Walter Bright's article on Voldemort types]
 
* [[Initializing variables]]
 
* [[Initializing variables]]
 +
 +
[[Category:CommonIdiom]]

Latest revision as of 23:45, 10 September 2016

In D, a Voldemort type is a type that cannot be directly named outside of the scope it's declared in, but code outside the scope can still use this type by taking advantage of D's static type inference.

Usage

For example:

// Note: the return type is auto, because we cannot actually name it outside the function!
auto createVoldemortType(int value)
{
    struct TheUnnameable
    {
        int getValue() { return value; }
    }
    return TheUnnameable();
}

Here is how the Voldemort type can be used:

auto voldemort = createVoldemortType(123);
writeln(voldemort.getValue());  // prints 123

The type of the variable voldemort cannot be explicitly named here, because that name is only accessible inside the function createVoldemortType. Furthermore, instances of TheUnnameable cannot be created outside of this function, because its definition relies on a local variable in the function. However, we can still declare variables of that type by using D's static type inference, to store the function's return value.

Purpose

This section is a stub. Please help the wiki by adding more content here.

Voldemort types are widely used in Phobos's range-based functions. They are used as internal types that implement that particular range function. Voldemort types were chosen for this in order to improve encapsulation: users of the standard library don't need to, and shouldn't need to, know how a particular range function was implemented and instead the simple knowledge of the primitives implemented by the return type is sufficient.

In fact, some of the range-based functions will return different types based on what kind of range was passed in. This happens when the function that contains a Voldemort type is actually a templatized function.

Rather than forcing the user to remember which type to use for which occasion, the use of Voldemort types forces the user to depend on static type inference, and thus automatically get the right type every time.

This also allows the Phobos developers to change the underlying implementation without requiring all users to update their code. Otherwise, every time the implementation changes you will have to update every instance of the type to refer to the correct type name. Using Voldemort types alleviates this problem by letting the compiler fill in the correct type for you automatically.

Side effects

Since Voldemort types are often implemented in templatized functions that are used in a CTFE fashion, their usage tends to create huge monolithic symbols that are not useful when debugging and that increase the binary size. In extreme cases the mangle of a call chain could occupy up to 1 MB!

Solutions exist. One of them would be to change the way that DMD generates the mangles, using a specialized compressor. Another solution, proposed by Steve Schveighoffer, is to move the Voldemort out of the function body.

See also