www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Idea: Immutable blocks within functions

reply "Xinok" <xinok live.com> writes:
For me, one of the annoyances of working with immutable data is 
simply initializing data. Immutable variables have to be 
initialized upon declaration and cannot be modified after that 
point. This is made more difficult if you have several immutable 
variables to initialize in conjunction with one another

Currently, the following syntax is not allowed within functions:
immutable{ }

I propose enabling this syntax to have special usage within 
functions for initializing immutable variables. Variables would 
be mutable within the scope of the immutable block, and immutable 
outside of that scope. That scope would be limited to the same 
access restrictions as pure functions to ensure mutable 
references to the data can't escape.

While something similar could be accomplished using nested 
functions, they're limited to initializing one immutable variable 
at a time and less efficient since they must utilize a context 
pointer to access outside data. I think my idea has a nice allure 
for greater simplicity and flexibility for initializing immutable 
variables.


An impractical example:

void foo()
{
	immutable
	{
		int[] arr = ...;
		int[] arr2 = arr.dup;
		sort(arr); // arr is still mutable
		
		int[] arr2 = arr.dup;
		reverse(arr2);
		arr ~= arr2;
	}
	
	sort(arr); // Error is thrown because arr is now immutable
}


Considerations:
* Global and thread-local variables within an immutable block 
would be immutable; otherwise, the control flow could re-enter 
the immutable scope and change the values of those variables.
* There may be issues with reflection since the type of the 
variable suddenly changes from mutable to immutable.

So what do nested immutable blocks imply? The same as having a 
pure function within a pure function.

void foo()
{
	immutable
	{
		int[] arr = ...;
		
		immutable
		{
			// This statement would throw an error
			int value = arr[0];
			// Here, arr is still mutable.
			// So like a pure function, access to outside
			// mutable variables isn't allowed.
		}
	}
}
Sep 07 2013
next sibling parent "Peter Alexander" <peter.alexander.au gmail.com> writes:
On Saturday, 7 September 2013 at 22:57:40 UTC, Xinok wrote:
 	immutable
 	{
 		int[] arr = ...;
 		int[] arr2 = arr.dup;
 		sort(arr); // arr is still mutable
 		
 		int[] arr2 = arr.dup;
 		reverse(arr2);
 		arr ~= arr2;
 	}

How do you stop mutable references escaping in functions called from the immutable block? e.g. int* p; void foo(ref int x) { p = &x; } void main() { immutable { int x = 1; foo(x); } *p = 0; }
Sep 07 2013
prev sibling next sibling parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
08-Sep-2013 02:57, Xinok пишет:
 For me, one of the annoyances of working with immutable data is simply
 initializing data. Immutable variables have to be initialized upon
 declaration and cannot be modified after that point. This is made more
 difficult if you have several immutable variables to initialize in
 conjunction with one another

[snip]
 An impractical example:

 void foo()
 {
      immutable
      {
          int[] arr = ...;
          int[] arr2 = arr.dup;
          sort(arr); // arr is still mutable

          int[] arr2 = arr.dup;
          reverse(arr2);
          arr ~= arr2;
      }

      sort(arr); // Error is thrown because arr is now immutable
 }

A pure lambda can achieve the same unless, of course, purity is out of question: immutable arr = { int[] arr = ...; int[] arr2 = arr.dup; sort(arr); // arr is still mutable int[] arr2 = arr.dup; reverse(arr2); arr ~= arr2; return arr; } pure (); But if you don't require purity you may escape a mutable reference somewhere (from within the immutable block where it is mutable): int[] global; { immutable { int[] arr = ...; global = arr; } ... whopse now immutable arr can be changed elsewhere... } Basically even not yet completely outlined the feature already pulls in escape analysis. Not going to fly IMHO. -- Dmitry Olshansky
Sep 07 2013
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 09/08/2013 01:22 AM, Dmitry Olshansky wrote:
 But if you don't require purity [...]
 Basically even not yet completely outlined the feature already pulls in escape
analysis. Not going to fly IMHO.

But if he requires purity, this works.
Sep 07 2013
prev sibling next sibling parent "Xinok" <xinok live.com> writes:
On Saturday, 7 September 2013 at 23:05:31 UTC, Peter Alexander 
wrote:
 On Saturday, 7 September 2013 at 22:57:40 UTC, Xinok wrote:
 	immutable
 	{
 		int[] arr = ...;
 		int[] arr2 = arr.dup;
 		sort(arr); // arr is still mutable
 		
 		int[] arr2 = arr.dup;
 		reverse(arr2);
 		arr ~= arr2;
 	}

How do you stop mutable references escaping in functions called from the immutable block? e.g. int* p; void foo(ref int x) { p = &x; } void main() { immutable { int x = 1; foo(x); } *p = 0; }

Immutable blocks would have the same restrictions as pure functions. And like pure functions, you could only call other pure functions. Since foo is not pure, this code wouldn't compile.
Sep 07 2013
prev sibling next sibling parent "Xinok" <xinok live.com> writes:
On Saturday, 7 September 2013 at 23:22:59 UTC, Dmitry Olshansky 
wrote:
 08-Sep-2013 02:57, Xinok пишет:
 For me, one of the annoyances of working with immutable data 
 is simply
 initializing data. Immutable variables have to be initialized 
 upon
 declaration and cannot be modified after that point. This is 
 made more
 difficult if you have several immutable variables to 
 initialize in
 conjunction with one another

[snip]
 An impractical example:

 void foo()
 {
     immutable
     {
         int[] arr = ...;
         int[] arr2 = arr.dup;
         sort(arr); // arr is still mutable

         int[] arr2 = arr.dup;
         reverse(arr2);
         arr ~= arr2;
     }

     sort(arr); // Error is thrown because arr is now immutable
 }

A pure lambda can achieve the same unless, of course, purity is out of question: immutable arr = { int[] arr = ...; int[] arr2 = arr.dup; sort(arr); // arr is still mutable int[] arr2 = arr.dup; reverse(arr2); arr ~= arr2; return arr; } pure ();

Both arr and arr2 would exist as immutable arrays outside of that scope. That's what I meant by, nested functions could only initialize one immutable variable at a time, because you can only return one value to initialize the immutable variable with. With immutable blocks, you can initialize several immutable variables simultaneously.
 Basically even not yet completely outlined the feature already 
 pulls in escape analysis. Not going to fly IMHO.

They follow the same rules as pure functions. They can only access immutable data, can only call pure functions, etc. No need for escape analysis, no need to reinvent the wheel. Furthermore, if the capabilities of pure functions is extended, so is the capabilities of immutable blocks.
Sep 07 2013
prev sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Xinok:

 For me, one of the annoyances of working with immutable data is 
 simply initializing data. Immutable variables have to be 
 initialized upon declaration and cannot be modified after that 
 point. This is made more difficult if you have several 
 immutable variables to initialize in conjunction with one 
 another

 Currently, the following syntax is not allowed within functions:
 immutable{ }

See also: http://www.digitalmars.com/d/archives/digitalmars/D/Transients_or_scoped_immutability_135204.html Bye, bearophile
Sep 11 2013