Jump to page: 1 2
Thread overview
How to initialize immutable variables with an expression that throws an exception to catch?
Apr 03, 2020
FeepingCreature
Apr 03, 2020
FeepingCreature
Apr 03, 2020
Atila Neves
Apr 03, 2020
Ali Çehreli
Apr 07, 2020
FeepingCreature
Apr 07, 2020
tsbockman
Apr 07, 2020
Max Samukha
Apr 07, 2020
H. S. Teoh
Apr 08, 2020
FeepingCreature
Apr 09, 2020
Nick Treleaven
April 03, 2020
Consider the following code:

struct S { }
...
  S s = void;
  try
    s = fun();
  catch (Exception)
    return;
  call(s);

Now you change S to be immutable.

How are you supposed to initialize s? You can't assign to it anymore.

Assume that we explicitly don't want to pull the call into the try body, for instance because we want exceptions from call to not be caught.

The only way I've found is to make fun() return Algebraic!(S, Exception) but that's kind of ugly and bypasses a basic language feature.

Maybe D could allow to initialize an immutable variable from the try{} body if the catch{} body is statically known to exit by return/throw?
April 03, 2020
On 4/3/20 2:56 AM, FeepingCreature wrote:
> Consider the following code:
> 
> struct S { }
> ....
>    S s = void;
>    try
>      s = fun();
>    catch (Exception)
>      return;
>    call(s);
> 
> Now you change S to be immutable.
> 
> How are you supposed to initialize s? You can't assign to it anymore.
> 
> Assume that we explicitly don't want to pull the call into the try body, for instance because we want exceptions from call to not be caught.
> 
> The only way I've found is to make fun() return Algebraic!(S, Exception) but that's kind of ugly and bypasses a basic language feature.
> 
> Maybe D could allow to initialize an immutable variable from the try{} body if the catch{} body is statically known to exit by return/throw?

Do it the old-fashioned way -- use casting ;)

S s_val = void;
try
  s_val = fun();
catch (Exception)
  return;

immutable s = s_val.assumeUnique;
call(s);

It's not as pretty and has a "code-by-assumption" issue. I wish you could just lambda-initialize this, but the return statement throws a wrench into that.

-Steve
April 03, 2020
On Friday, 3 April 2020 at 12:49:27 UTC, Steven Schveighoffer wrote:
> Do it the old-fashioned way -- use casting ;)
>
> S s_val = void;
> try
>   s_val = fun();
> catch (Exception)
>   return;
>
> immutable s = s_val.assumeUnique;
> call(s);
>

Doesn't work - `immutable struct` means S is always immutable.
April 03, 2020
On Friday, 3 April 2020 at 06:56:27 UTC, FeepingCreature wrote:
> Consider the following code:
>
> struct S { }
> ...
>   S s = void;
>   try
>     s = fun();
>   catch (Exception)
>     return;
>   call(s);
>
> Now you change S to be immutable.
>
> How are you supposed to initialize s? You can't assign to it anymore.
>
> Assume that we explicitly don't want to pull the call into the try body, for instance because we want exceptions from call to not be caught.
>
> The only way I've found is to make fun() return Algebraic!(S, Exception) but that's kind of ugly and bypasses a basic language feature.
>
> Maybe D could allow to initialize an immutable variable from the try{} body if the catch{} body is statically known to exit by return/throw?

--------------------
immutable struct S { }

void main() {
    S impl() {
        try
            return fun();
        catch (Exception) {
            S s = void;
            return s;
        }
    }

    call(impl);
}


void call(S s) {

}

S fun() {
    return S();
}

--------------------
April 03, 2020
On 4/3/20 9:21 AM, Atila Neves wrote:
> On Friday, 3 April 2020 at 06:56:27 UTC, FeepingCreature wrote:
>> Consider the following code:
>>
>> struct S { }
>> ...
>>   S s = void;
>>   try
>>     s = fun();
>>   catch (Exception)
>>     return;
>>   call(s);
>>
>> Now you change S to be immutable.
>>
>> How are you supposed to initialize s? You can't assign to it anymore.
>>
>> Assume that we explicitly don't want to pull the call into the try body, for instance because we want exceptions from call to not be caught.
>>
>> The only way I've found is to make fun() return Algebraic!(S, Exception) but that's kind of ugly and bypasses a basic language feature.
>>
>> Maybe D could allow to initialize an immutable variable from the try{} body if the catch{} body is statically known to exit by return/throw?
> 
> --------------------
> immutable struct S { }
> 
> void main() {
>      S impl() {
>          try
>              return fun();
>          catch (Exception) {
>              S s = void;
>              return s;
>          }
>      }
> 
>      call(impl);
> }
> 
> 
> void call(S s) {
> 
> }
> 
> S fun() {
>      return S();
> }
> 
> --------------------

That's not the same. The original code does not call `call` with a void-initialized S.

However, this might do the trick:

void main() {
   bool voided = false;
   S impl() {
     try
         return fun();
     catch (Exception) {
         voided = true;
         S s = void;
         return s;
     }
   }
   auto s = impl();
   if(!voided) call(s);
}

But there is a catch here. Assigning an S to a void-initialized S is still copying stuff. It's not the same as never touching that memory.

-Steve
April 03, 2020
On 4/3/20 9:14 AM, FeepingCreature wrote:
> On Friday, 3 April 2020 at 12:49:27 UTC, Steven Schveighoffer wrote:
>> Do it the old-fashioned way -- use casting ;)
>>
>> S s_val = void;
>> try
>>   s_val = fun();
>> catch (Exception)
>>   return;
>>
>> immutable s = s_val.assumeUnique;
>> call(s);
>>
> 
> Doesn't work - `immutable struct` means S is always immutable.

totally misread your statement as making s immutable, not S.

I don't know how to solve that problem correctly.

-Steve
April 03, 2020
On 4/2/20 11:56 PM, FeepingCreature wrote:

> The only way I've found is to make fun() return Algebraic!(S, Exception) but that's kind of ugly and bypasses a basic language feature.

How about using a Nullable!(immutable(S)):

import std.typecons;
alias NS = Nullable!(immutable(S));

NS makeS() {
  try {
    return NS(fun());

  } catch (Exception) {
    return NS();
  }
}

struct S { }

S fun() {
  return S();
}

void call(immutable(S) s) {
}

void foo() {
  auto ns = makeS();
  if (!ns.isNull) {
    call(ns.get);
  }
}

void main() {
  foo();
}

Ali
April 07, 2020
On Friday, 3 April 2020 at 15:43:51 UTC, Ali Çehreli wrote:
> On 4/2/20 11:56 PM, FeepingCreature wrote:
>
>> The only way I've found is to make fun() return Algebraic!(S, Exception) but that's kind of ugly and bypasses a basic language feature.
>
> How about using a Nullable!(immutable(S)):
>
> [snip]

Right, but that's still the exact same workaround, except with Nullable instead of Algebraic. I know how to work around it. I'm saying what should be changed in the language so I don't *have to* work around it?
April 07, 2020
On Tuesday, 7 April 2020 at 14:15:20 UTC, FeepingCreature wrote:
> Right, but that's still the exact same workaround, except with Nullable instead of Algebraic. I know how to work around it. I'm saying what should be changed in the language so I don't *have to* work around it?

The problem in the language is that immutability is considered a permanent and fundamental property of a value, whereas it is actually a state that is entered after the initialization/construction of the value is complete, and exited when the value's memory is later reclaimed.

Construction of an immutable value should be done by making a mutable variable, mutating it to the desired state via any arbitrary sequence of normal operations, and then declaring the mutable stage of its life cycle complete via `cast(immutable)` or something. The only problem with this is making the compiler smart enough to prove that no mutable references to the data are used after that point, so that the operation can be @safe.

The current rules for immutable constructors are sort of like this, but they're still overly strict to the point that I often need to make the constructor @trusted and use (cast() this) to get the job done without excessive reliance on the optimizer, or excessive code duplication. Also, not everything can or should be done in a constructor.
April 07, 2020
On Tuesday, 7 April 2020 at 19:52:36 UTC, tsbockman wrote:
> On Tuesday, 7 April 2020 at 14:15:20 UTC, FeepingCreature wrote:
>> Right, but that's still the exact same workaround, except with Nullable instead of Algebraic. I know how to work around it. I'm saying what should be changed in the language so I don't *have to* work around it?
>
> The problem in the language is that immutability is considered a permanent and fundamental property of a value, whereas it is actually a state that is entered after the initialization/construction of the value is complete, and exited when the value's memory is later reclaimed.
>

Exactly (with a small correction that the state is exited before the destruction if there is any)!
« First   ‹ Prev
1 2