May 24, 2013
On 5/24/13 5:09 AM, Regan Heath wrote:
> On Thu, 23 May 2013 19:52:14 +0100, Andrei Alexandrescu
> <SeeWebsiteForEmail@erdani.org> wrote:
>
>> On 5/23/13 2:08 PM, Marco Leise wrote:
>>> Am Thu, 23 May 2013 13:06:44 -0400
>>> schrieb Andrei Alexandrescu<SeeWebsiteForEmail@erdani.org>:
>>>
>>>> TDPL 8.4 discusses a raw/cooked model of construction. The same
>>>> principle should apply to const/immutable member construction: you get
>>>> to cook the field, but you can't taste it while raw.
>>>
>>> You are not making friends with Japanese D users.
>>
>> Good one! And incidentally I love sashimi :o). Well let me amend it:
>> if your plan is to cook it, don't taste it before.
>
> mmm cookie dough/cake batter. :p

I'm officially pwned.

Andrei

May 24, 2013
On Friday, 24 May 2013 at 10:55:09 UTC, Artur Skawina wrote:
> On 05/24/13 02:33, Steven Schveighoffer wrote:
>> On Thu, 23 May 2013 19:03:25 -0400, Artur Skawina <art.08.09@gmail.com> wrote:
>> 
>>> On 05/23/13 23:06, Steven Schveighoffer wrote:
>> 
>>>> compiles:
>>>>
>>>> struct S
>>>> {
>>>>    const int x;
>>>>    this(int n)
>>>>    {
>>>>     x = n;
>>>>    }
>>>> }
>>>
>>> It's the 'const int x = 42;' case we're talking about. *That* one does not
>>> compile and doesn't need to. It /could/, but I see no reason to allow this;
>> 
>> My example is essentially:
>> 
>> const int x = 0;

No, that's not the same.
const int w;
is the same syntax used in C++. It's a declaration without an initializer. It signals your intention to initialize it later.

Now, D does default-initialize everything, so that there really isn't any such thing as an uninitialized variable in D, but it still signals the intention that you will modify it later.

int is a misleading example since the default initializer is 0, and it's common for people to rely on that.
Better to use char or float, or a class, where the default initializer is not usable, and it's clearer that it's a bug to use it.


The only way I can understand this feature is that it is changing the meaning of initializers inside aggregates.

Previously, they were initializers. Now, they're not. They're setting the value of .init for the class.
And they use the same syntax which is used for actual initializers.

In the past, the compiler didn't check if you from set a field in a constructor, which you had already initialized with an initializer.
But, it didn't really matter.
With a const field, it makes a much bigger difference, since if an initializer was specified, the only way that the field can make sense is if you modify it somewhere else. So the initializer MUST be able to be overwritten.

This has lead us to this oddity that under the new regime,

const char c = 'e';

is setting the value of c, unless it is inside an aggregate, in which case it's not initializing c, it's just specifying what .init is for that struct.

So, it's overloading the syntax to mean two very different things.

---

> Hence, you are arguing for a change in behavior. And the arguments for that
> are extremely weak. The goal should be to allow const initialization to happen
> exactly once.

I agree. This is the crux of the argument.
Are you and I the the only ones who see that as a valuable property of the language? I'm astonished that others are so willing to sacrifice it.
May 24, 2013
On Friday, 24 May 2013 at 11:57:53 UTC, Don wrote:
> On Friday, 24 May 2013 at 10:55:09 UTC, Artur Skawina wrote:
>> Hence, you are arguing for a change in behavior. And the arguments for that
>> are extremely weak. The goal should be to allow const initialization to happen
>> exactly once.
>
> I agree. This is the crux of the argument.
> Are you and I the the only ones who see that as a valuable property of the language? I'm astonished that others are so willing to sacrifice it.

If const variable initialization is allowed to happen exactly once, then you'd prevent more complex initializations like the following:

struct Test
{
    immutable int[] lut;
    immutable int cnt;

    this(int mod)
    {
        lut.length = 999;
        cnt = 0;
        size_t last = 0;

        foreach (i, ref e; lut)
        {
            if (i % mod == 0)
            {
                e = 42;
                ++cnt;
                last = i;
            }
        }
        lut.length = last + 1;
    }
}
May 24, 2013
On Friday, 24 May 2013 at 11:57:53 UTC, Don wrote:
> ...

I have finally understood your point about default initializer vs changing T.init :) It was not that easy because there are no similar terms in other languages. Anyway, it does make sense. Somehow separating concept of compile-time default for a type from default initialization of specific variable does make sense. This may be a good reason for CTFE-limited default constructor instead of current approach.
May 24, 2013
On Thursday, 23 May 2013 at 16:44:03 UTC, deadalnix wrote:
> On Tuesday, 21 May 2013 at 20:36:20 UTC, Walter Bright wrote:
>>
>> Join the dmd beta mailing list to keep up with the betas. This one is pretty much good to go, unless something disastrous crops up.
>>
>> http://ftp.digitalmars.com/dmd2beta.zip
>>
>> Remaining regressions:
>>
>> http://d.puremagic.com/issues/buglist.cgi?query_format=advanced&bug_severity=regression&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED
>
> Add that one : http://d.puremagic.com/issues/show_bug.cgi?id=10151
>
> Once again my code is broken. I can understand both behavior, but the change don't really make sense. This time the ROI is doubtful.

Solved, but now I fail on :
Error: template std.utf.decodeFront does not match any function template declaration. Candidates are:
 ... // Many candidates.
May 24, 2013
On Friday, 24 May 2013 at 13:06:03 UTC, deadalnix wrote:
> Solved, but now I fail on :
> Error: template std.utf.decodeFront does not match any function template declaration. Candidates are:
>  ... // Many candidates.

So I fixed it, now I have another regression (or a new feature) : "Foo is not a template" en masse.

dustmiting right now.
May 24, 2013
On Fri, 24 May 2013 07:57:51 -0400, Don <turnyourkidsintocash@nospam.com> wrote:

> On Friday, 24 May 2013 at 10:55:09 UTC, Artur Skawina wrote:
>> On 05/24/13 02:33, Steven Schveighoffer wrote:
>>> On Thu, 23 May 2013 19:03:25 -0400, Artur Skawina <art.08.09@gmail.com> wrote:
>>>
>>>> On 05/23/13 23:06, Steven Schveighoffer wrote:
>>>
>>>>> compiles:
>>>>>
>>>>> struct S
>>>>> {
>>>>>    const int x;
>>>>>    this(int n)
>>>>>    {
>>>>>     x = n;
>>>>>    }
>>>>> }
>>>>
>>>> It's the 'const int x = 42;' case we're talking about. *That* one does not
>>>> compile and doesn't need to. It /could/, but I see no reason to allow this;
>>>  My example is essentially:
>>>  const int x = 0;
>
> No, that's not the same.
> const int w;
> is the same syntax used in C++. It's a declaration without an initializer. It signals your intention to initialize it later.

This isn't C++.  C++ has implicit default constructors that are built using the given initializations.  D does it differently.

> Now, D does default-initialize everything, so that there really isn't any such thing as an uninitialized variable in D, but it still signals the intention that you will modify it later.
>
> int is a misleading example since the default initializer is 0, and it's common for people to rely on that.
> Better to use char or float, or a class, where the default initializer is not usable, and it's clearer that it's a bug to use it.

OK, how about this:

struct MyFloat
{
   float f = 0.0;
}

struct S
{
   const MyFloat x;
}

:P

> The only way I can understand this feature is that it is changing the meaning of initializers inside aggregates.
>
> Previously, they were initializers. Now, they're not. They're setting the value of .init for the class.

This was ALWAYS the case.

struct S
{
   int x = 5; // sets S.init.x to 5
}

You are not looking at the change correctly.  The change is simply, instead of const int x = 5 magically (and incorrectly) implying static, it doesn't.  Then it defaults to the same behavior as non-const member initializers -- it sets the .init value for the struct.  Previously, this was difficult to do, see my above example.

> And they use the same syntax which is used for actual initializers.

They use the same syntax that is used to dictate the init value for non-const members.

> In the past, the compiler didn't check if you from set a field in a constructor, which you had already initialized with an initializer.
> But, it didn't really matter.
> With a const field, it makes a much bigger difference, since if an initializer was specified, the only way that the field can make sense is if you modify it somewhere else. So the initializer MUST be able to be overwritten.
>
> This has lead us to this oddity that under the new regime,
>
> const char c = 'e';
>
> is setting the value of c, unless it is inside an aggregate, in which case it's not initializing c, it's just specifying what .init is for that struct.

As is the same (and always has been) for all instance member initializers.

char c = 'e';

>> Hence, you are arguing for a change in behavior. And the arguments for that
>> are extremely weak. The goal should be to allow const initialization to happen
>> exactly once.
>
> I agree. This is the crux of the argument.
> Are you and I the the only ones who see that as a valuable property of the language? I'm astonished that others are so willing to sacrifice it.

It's still available, just put static on the member, and it's the same as before.

-Steve
May 24, 2013
On Friday, 24 May 2013 at 13:46:49 UTC, Steven Schveighoffer wrote:
> This was ALWAYS the case.
>
> struct S
> {
>    int x = 5; // sets S.init.x to 5
> }
>

As far as I understand Don, this behavior should be considered misleading too, it does not matter if variable is const or not. And that does have some sense, because you have initializer syntax which does not really initialize anything on its own, contrary to very same initializer syntax used with variable.

There is an option to prohibit initializers for struct member declarations at all and allow CTFE-able default constructors instead, but that would have been a major change.

It does not change the fact that old behavior was completely broken beyond imagination though.
May 24, 2013
On Fri, 24 May 2013 09:54:20 -0400, Dicebot <m.strashun@gmail.com> wrote:

> On Friday, 24 May 2013 at 13:46:49 UTC, Steven Schveighoffer wrote:
>> This was ALWAYS the case.
>>
>> struct S
>> {
>>    int x = 5; // sets S.init.x to 5
>> }
>>
>
> As far as I understand Don, this behavior should be considered misleading too, it does not matter if variable is const or not. And that does have some sense, because you have initializer syntax which does not really initialize anything on its own, contrary to very same initializer syntax used with variable.

Being able to dictate the .init data is very powerful and useful.  You can't remove that feature.

But the point I'm making is that the syntax IS consistent.  It just never worked before, because of the implicit 'static'

-Steve
May 24, 2013
On Friday, 24 May 2013 at 13:58:32 UTC, Steven Schveighoffer wrote:
> Being able to dictate the .init data is very powerful and useful.  You can't remove that feature.

Sure, I completely agree, thus the idea adding of CTFE-able constructor which will become the T.init for structs.

> But the point I'm making is that the syntax IS consistent.  It just never worked before, because of the implicit 'static'

No it is not. It never worked before because it was simply broken. Now it works properly from the point of spec, but it is inconsistent from the point of language design:

int x = 5; // can you tell without looking at scope if it really initializes x or just defines .init for aggregate?

Initializer syntax does not really make sense for non-static aggregate members at all, mutable or not.