Mutable enums

Timon Gehr timon.gehr at gmx.ch
Wed Nov 16 13:16:48 PST 2011


On 11/16/2011 09:00 PM, Steven Schveighoffer wrote:
> On Wed, 16 Nov 2011 14:26:57 -0500, Timon Gehr <timon.gehr at gmx.ch> wrote:
>
>> On 11/16/2011 02:22 PM, Steven Schveighoffer wrote:
>>> On Tue, 15 Nov 2011 13:45:02 -0500, Timon Gehr <timon.gehr at gmx.ch>
>>> wrote:
>>>
>>>> Note that this is an explicit allocation:
>>>>
>>>> int[] a = [1,2,3]; // just as explicit as a NewExpression
>>>>
>>>> Only the enums "hide" it sometimes, but actually you should know the
>>>> involved types.
>>>
>>> As I've said, there are already ways to explicitly allocate memory. A
>>> suggested replacement for this is:
>>>
>>> int[] a = array(1, 2, 3);
>>>
>>> And you could always do:
>>>
>>> int[] a = [1, 2, 3].dup;
>>>
>>> Nobody complains about having to do:
>>>
>>> char[] a = "hello".dup;
>>>
>>> I don't see why we couldn't do the same for all array literals.
>>>
>>
>> Because 'immutable' behaves nicely on built-in value types, but not on
>> arbitrary reference types.
>
> string is a reference type. We hear no complaints about strings being
> stored in ROM and the type of literals being immutable.
>

string is not an immutable type. It is immutable(char)[] and char is a 
built-in value type.

static assert(isMutable!string);

> Make no mistake, this doesn't cover every current instance array
> literals, such as ones which contain necessarily-heap-allocated
> entities. Those would be covered by a library function (e.g.
> array(...)). But those are not array literals anyways, they are
> constructors.
>

It is true that they are constructors, but they are currently also 
called array literals.

>>>> Well, there is a function called writeln that can do that. That is a
>>>> different function. But the one that gets actually called is not const
>>>> correct as well.
>>>>
>>>>
>>>> This is writeln:
>>>>
>>>> // Most general instance
>>>> void writeln(T...)(T args)
>>>> if (T.length > 1 || T.length == 1 && !is(typeof(args[0]) :
>>>> const(char)[]))
>>>> {
>>>> stdout.write(args, '\n');
>>>> }
>>>>
>>>>
>>>> =>
>>>> writeln([1,2,3]);
>>>> // modulo IFTY:
>>>> writeln!(int[])([1,2,3]);
>>>> // const correct?
>>>> writeln!(int[])([1,2,3].idup); // nope!
>>>>
>>>> Error: cannot implicitly convert expression (_adDupT(&
>>>> _D11TypeInfo_Ai6__initZ,[1,2,3])) of type immutable(int)[] to int[]
>>>
>>> I'm not sure what this means. If [1, 2, 3] is typed as immtuable(int)[],
>>> it will compile.
>>>
>>> Simple test:
>>>
>>> immutable(int)[] a = [1, 2, 3];
>>> writeln(a);
>>>
>>> works with 2.056.
>>
>> That is like saying
>>
>> void foo(int[] a){...} // prints a
>> void bar(immutable(int)[] a){...} // prints a
>>
>> int[] a = [1,2,3];
>> immutable(int)[] b = [1, 2, 3];
>> foo(a);
>> bar(b);
>>
>> "=> Oh, bar is const correct because I can call foo with mutable data
>> and bar with immutable data."
>>
>> foo is writeln!(int[])
>> bar is writeln!(immutable(int)[])
>>
>> in effect, if you do:
>>
>> writeln("hello".dup);
>>
>> The compiler cannot assume that the IFTI'd writeln!(char[]) will not
>> change the argument, ergo it cannot optimize away the .dup, even
>> though it would be a valid transformation as long as the programmer
>> does not make the program semantics depend on the identity relation on
>> immutable references (doing so defeats the purpose of immutability).
>
> writeln is not contractually const correct, but that's only because most
> of D is not const correct either. For example, Object.toString is not
> const, so if you const-ified all writeln args, you couldn't print
> objects. But writeln itself does not change any data (a rogue toString
> might change data, but that is not common practice).
>
> Once D becomes const correct, writeln *can* apply const to all it's
> parameters.

Ok, I see. writeln still could be const-correct in principle. It would 
need to apply const to the parameters that have a const toString method. 
But the language is not powerful enough to do that. There is no way to 
intercept IFTI... I am commonly missing that feature.

>
>>> If the data is stored in ROM, it should be typed as immutable. If it's
>>> not, then it could be modified. The only other option is heap allocation
>>> and construction on use, which I've already said is undesirable.
>>>
>>> If we start from "all array literals and enums that contain references
>>> store the referenced data in ROM," then we will find ways to deal with
>>> any inconveniences. It's all a matter of what's more important in a
>>> system language, performance or convenience.
>>
>> Both are more important. It is D.
>> Everything you need to do is make the enum static immutable instead.
>> D is also a scripting language and an application programming language.
>>
>> auto a = [new Foo, new Bar, new Qux]; // I want that to work.
>
> auto a = array(new Foo, new Bar, new Qux);
>

Requiring that works modulo template bloat and breakage of existing 
code. Also, it does not really buy us anything imho.

> The one case which is difficult to do is initializing a fixed-size array
> with a literal that uses runtime data. I suppose we'd need a function
> that returns a fixed-sized array made of its arguments, and doing the
> init builds it in place. i.e.:
>
> Object[3] objs = array_fixed(new Foo, new Bar, new Qux);
>
> would not do any moving of references, it would construct the fixed
> sized array in-place. Initializing fixed sized arrays with array
> literals already needs attention anyway.
>

 From the compiler side. Not necessarily from the std lib side.








More information about the Digitalmars-d-learn mailing list