Mutable enums

Steven Schveighoffer schveiguy at yahoo.com
Mon Nov 14 11:37:01 PST 2011


On Mon, 14 Nov 2011 13:37:18 -0500, Timon Gehr <timon.gehr at gmx.ch> wrote:

> On 11/14/2011 02:13 PM, Steven Schveighoffer wrote:
>> On Mon, 14 Nov 2011 03:27:21 -0500, Timon Gehr <timon.gehr at gmx.ch>  
>> wrote:
>>
>>> On 11/14/2011 01:02 AM, bearophile wrote:
>>>> Jonathan M Davis:
>>>>
>>>>>> import std.algorithm;
>>>>>> void main() {
>>>>>> enum a = [3, 1, 2];
>>>>>> enum s = sort(a);
>>>>>> assert(equal(a, [3, 1, 2]));
>>>>>> assert(equal(s, [1, 2, 3]));
>>>>>> }
>>>>>
>>>>> It's not a bug. Those an manifest constants. They're copy-pasted
>>>>> into whatever
>>>>> code you used them in. So,
>>>>>
>>>>> enum a = [3, 1, 2];
>>>>> enum s = sort(a);
>>>>>
>>>>> is equivalent to
>>>>>
>>>>> enum a = [3, 1, 2];
>>>>> enum s = sort([3, 1, 2]);
>>>>
>>>> You are right, there's no DMD bug here. Yet, it's a bit surprising to
>>>> sort in-place a "constant". I have to stop thinking of them as
>>>> constants. I don't like this design of enums...
>>>
>>> It is the right design. Why should enum imply const or immutable? (or
>>> inout, for that matter). They are completely orthogonal.
>>
>> There is definitely some debatable practice here for wherever enum is
>> used on an array.
>>
>> Consider that:
>>
>> enum a = "hello";
>>
>> foo(a);
>>
>> Does not allocate heap memory, even though "hello" is a reference type.
>>
>> However:
>>
>> enum a = ['h', 'e', 'l', 'l', 'o'];
>>
>> foo(a);
>>
>> Allocates heap memory every time a is *used*. This is counter-intuitive,
>> one uses enum to define things using the compiler, not during runtime.
>> It's used to invoke CTFE, to avoid heap allocation. It's not a glorified
>> #define macro.
>>
>> The deep issue here is not that enum is used as a manifest constant, but
>> rather the fact that enum can map to a *function call* rather than the
>> *result* of that function call.
>>
>> Would you say this should be acceptable?
>>
>> enum a = malloc(5);
>>
>> foo(a); // calls malloc(5) and passes the result to foo.
>>
>> If the [...] form is an acceptable enum, I contend that malloc should be
>> acceptable as well.
>>
>
> a indeed refers to the result of the evaluation of ['h', 'e', 'l', 'l',  
> 'o'].
>
> enum a = {return ['h', 'e', 'l', 'l', 'o'];}(); // also allocates on  
> every use
>
> But malloc is not CTFE-able, that is why it fails.

You are comparing apples to oranges here.  Whether it's CTFE able or not  
has nothing to do with it, since the code is executed at runtime, not  
compile time.

>
>
>> My view is that enum should only be acceptable on data that is
>> immutable, or implicitly cast to immutable,
>
> Too restrictive imho.

It allows the compiler to evaluate the enum at compile time, and store any  
referenced data in ROM, avoiding frequent heap allocations (similar to  
string literals).

IMO, type freedom is lower on the priority list than performance.

You can already define a symbol that calls arbitrary code at runtime:

@property int[] a() { return [3, 1, 2];}

Why should we muddy enum's goals with also being able to call functions  
during runtime?

>
>> and should *never* map to an
>> expression that calls a function during runtime.
>>
>
> Well, I would not miss that at all.
> But being stored as enum should not imply restrictions on type  
> qualifiers.

The restrictions are required in order to avoid calling runtime functions  
for enum usage.  Without the restrictions, you must necessarily call  
runtime functions for any reference-based types (to avoid modifying the  
original).

Note that I'm not saying literals in general should not trigger heap  
allocations, I'm saying assigning such literals to enums should require  
unrestricted copying without runtime function calls.

I don't think you would miss this as much as you think.  Assigning a  
non-immutable array from an immutable one is as easy as adding a .dup, and  
then the code is more clear that an allocation is taking place.

-Steve


More information about the Digitalmars-d-learn mailing list