May 29, 2013
On Wednesday, 29 May 2013 at 13:21:21 UTC, Maxim Fomin wrote:
>
> Fundamental issue here is that
>
> const T t;
>
> is almost useless, it is essentially immutable since you cannot change it and you cannot alias it. As such, it probably should be changed to a mutable object or an immutable one.

It's generic code. See the linked code and you'll see that this is not an option.

> However, since const/mutable aliasing is allowed, you can do:
>
> union U (T)
> {
>    const T ct;
>    T mt;
> }
>
> because const doesn't mean that object would never change, you can mutate it through alias. From the opposite view, there is also no problem in reinterpeting a mutable object as const object (since mutables can be converted to const).
>
> Depending on your mutable indirection situation this can work or may not.

Mutable indirection is actually not the problem with this solution, it's the casting away of const. There's no guarantee here that the data wasn't actually immutable when it was created.
May 29, 2013
On Wednesday, 29 May 2013 at 13:30:09 UTC, Jakob Ovrum wrote:
>> However, since const/mutable aliasing is allowed, you can do:
>>
>> union U (T)
>> {
>>   const T ct;
>>   T mt;
>> }
>>
>> because const doesn't mean that object would never change, you can mutate it through alias. From the opposite view, there is also no problem in reinterpeting a mutable object as const object (since mutables can be converted to const).
>>
>> Depending on your mutable indirection situation this can work or may not.
>
> Mutable indirection is actually not the problem with this solution, it's the casting away of const. There's no guarantee here that the data wasn't actually immutable when it was created.

It is not casting away const and it has nothing to do with immutable, also this does not suffer from data was actually immutable problem.
May 29, 2013
On Wednesday, 29 May 2013 at 13:35:18 UTC, Maxim Fomin wrote:
> It is not casting away const and it has nothing to do with immutable, also this does not suffer from data was actually immutable problem.

The data is not initially constructed by the local function, it's clearly coming from somewhere else and there's no guarantee that it's not returning a const view of immutable data. The code is not limited to accepting const, of course, and exhibits the same problem when the function returns immutable data.

Unions are a convenience feature, it boils down to the same as a cast and has the same problems (actually worse because the conversion is not explicit).
May 29, 2013
On Wednesday, 29 May 2013 at 13:44:15 UTC, Jakob Ovrum wrote:
> On Wednesday, 29 May 2013 at 13:35:18 UTC, Maxim Fomin wrote:
>> It is not casting away const and it has nothing to do with immutable, also this does not suffer from data was actually immutable problem.
>
> The data is not initially constructed by the local function, it's clearly coming from somewhere else and there's no guarantee that it's not returning a const view of immutable data. The code is not limited to accepting const, of course, and exhibits the same problem when the function returns immutable data.
>
> Unions are a convenience feature, it boils down to the same as a cast and has the same problems (actually worse because the conversion is not explicit).

If you accept const object as a parameter, then yes.
May 29, 2013
On 5/29/13 9:25 AM, Jakob Ovrum wrote:
> On Wednesday, 29 May 2013 at 13:19:34 UTC, deadalnix wrote:
>> I guess the first assignment of a variable should be considered as a
>> declaration unless it has been read in between. This is the same
>> problem as immutable constructor, and also relate to extra copies
>> elimination.
>
> Yeah, and languages like C# solve these problems quite beautifully. I
> don't think there's a reason we can't do almost the same thing in D,
> except it requires implementation effort and overcoming Walter's
> protests. Regardless, it's probably not something to expect in the
> short-term.

How does C# solve it?

> I was unable to leverage std.exception. collectException looks promising
> at first glance - and maybe if the return value and out parameter were
> switched, it would work - but then I suspect the function's
> implementation would face exactly the same problem.

I must have missed part of this - how would the desired setup work?


Andrei
May 29, 2013
With 2.063, this code would work.

struct S { int* ptr; }  // has mutable indirection

void main()
{
    immutable S si = function () pure
    {
        S sm;
        sm.ptr = new int;
        *sm.ptr = 10;
        return sm;  // construct and return mutable object
    }();
    static assert(is(typeof(*si.ptr) == immutable int));
    assert(*si.ptr == 10);
}

The local function would return an unique object, so it is implicitly convertible to immutable.

Kenji Hara


2013/5/29 Jakob Ovrum <jakobovrum@gmail.com>

> On Wednesday, 29 May 2013 at 12:40:39 UTC, Dicebot wrote:
>
>> Why something like this is not usable?
>> -----------------------
>> int tmp;
>> try
>> {
>>    tmp = ...;
>> }
>> catch(..)
>> {
>> }
>> const(int) i = tmp;
>> -----------------------
>> Not really pretty but nothing crazy.
>>
>
> const(int) i = tmp; // fails when the type has mutable indirection.
>


May 29, 2013
On Wednesday, 29 May 2013 at 15:11:53 UTC, Andrei Alexandrescu wrote:
> I must have missed part of this - how would the desired setup work?
>
>
> Andrei

Maybe something like the following would work. collectException's return value and out parameter have been swapped for demonstration - an out parameter for the result just moves the problem to the body of collectException:
----
// Can throw, and we want to catch
int createTheVar();

// Can also throw, but we don't want to catch it here
int transform(int a);

int foo()
{
	Exception e;
	const(int) i = collectException(createTheVar(), e);

	if(e)
		// Exception handling code

	return transform(i);
}
----
By removing the scope created by try-catch, the variable can be declared and initialized at the same time, satisfying the requirements of const/immutable. Further, the transform() call is left unguarded as specified.

Such a collectionException works because it can use `return` inside the try scope without broadening what it catches:
----
E collectException(T = Exception, E)(lazy E exp, out T ex)
{
	try
		return exp();
	catch(T e)
	{
		ex = e;
		return E.init; // Kind of a drawback
	}
}
----
So, I guess using another function to do the try-catch is a workable solution.
May 29, 2013
On Wednesday, 29 May 2013 at 15:11:53 UTC, Andrei Alexandrescu wrote:
> How does C# solve it?

I'm failing to find this really good blog post by a C# designer I read a while ago that explains how it works. I'll keep looking; my memory is fuzzy on the details and I wouldn't be able to explain it well.
May 29, 2013
On Wednesday, 29 May 2013 at 15:14:54 UTC, Kenji Hara wrote:
> With 2.063, this code would work.
>
> struct S { int* ptr; }  // has mutable indirection
>
> void main()
> {
>     immutable S si = function () pure
>     {
>         S sm;
>         sm.ptr = new int;
>         *sm.ptr = 10;
>         return sm;  // construct and return mutable object
>     }();
>     static assert(is(typeof(*si.ptr) == immutable int));
>     assert(*si.ptr == 10);
> }
>
> The local function would return an unique object, so it is implicitly
> convertible to immutable.
>
> Kenji Hara

Hm, well the problem isn't to make something mutable into immutable, so the purity and implicit conversion is not necessary, but using a nested or anonymous function would indeed be a good way to move the problematic try-catch into another function.
May 29, 2013
On Wednesday, 29 May 2013 at 15:11:53 UTC, Andrei Alexandrescu wrote:
> How does C# solve it?

I can't find the blog posts, but I think it's basically just a very good implementation of definite assignment analysis[1], without any inter-procedural analysis. So, nothing spectacular (though I think it's really nice).

[1] https://en.wikipedia.org/wiki/Definite_assignment_analysis