View mode: basic / threaded / horizontal-split · Log in · Help
January 04, 2011
Immutable nested functions
A while ago I pointed out that the result of an immutably pure function (all arguments immutable, doesn't mutate globals) can be safely converted to immutable. More here:
http://d.puremagic.com/issues/show_bug.cgi?id=5081

It helps with building complex immutable structures. Problem is, virtually every construction site is different so one is forced to define a new initializer function every time. To illustrate:

void main() {
   immutable Node* init_leaf = ... ;
   uint breadth = ... ;
   immutable Node* tree = grow_tree(init_leaf, breadth);
}

Node* grow_tree(immutable Node* init_leaf, uint breadth) pure {
   Node* root = new Node;
   foreach (0..breadth) {
       Node* leaf = new Node(init_leaf);
       leaf.parent = root;
       root.leaves ~= leaf;
   }
   return root;
}

I tried to find a way to create ad-hoc functions conveniently. Naturally, I turned to nested functions:

void main() {
   immutable Node* init_leaf = ... ;
   uint breadth = ... ;
   
   Node* grow_tree() pure immutable {
       Node* root = new Node;
       foreach (0..breadth) {
           Node* leaf = new Node(init_leaf);
           leaf.parent = root;
           root.leaves ~= leaf;
       }
       return root;
   }
   
   immutable Node* tree = grow_tree();
}

Nested functions to be immutably pure must also guarantee that nothing gets mutated through its stack frame pointer. But there's a problem -- the compiler won't accept 'immutable' on a nested function. I think it should -- just like an immutable member function (e.g. in a class) is allowed to play only with immutable members of that class, an immutable nested function should be allowed to play only with the immutable members of the stack frame. It may seem a lesser change but I'm pretty excited as it solves the long-standing problems with immutable structure initialization.

Excitement aside, I got questions:

1. I'm proposing to proliferate the concept of immutability from 'this' reference to a stack frame pointer. Although I'm confident it makes sense as these are interchangeable in delegates, I could use some criticism to either reject or strengthen the idea.

2. What about delegates? Should there be means to express a "delegate that doesn't mutate through its 'this'/stack frame pointer"? What should the syntax be for defining such delegate type and for lambdas (delegate literals)?

3. (vaguely related) Should there be means to express annotated delegates in general (e.g. pure, nothrow). What about annotated lambdas?

-- 
Tomek
January 06, 2011
Re: Immutable nested functions
Tomek Sowiński napisał:

> [snip]

Two days, no answer. I was told that silence means agreement on this NG but this is extreme ;-)

Seriously, what did I do wrong? Too long/boring post?

-- 
Tomek
January 07, 2011
Re: Immutable nested functions
"Tomek Sowinski" <just@ask.me> wrote in message 
news:20110104222343.00004d47@unknown...
> Nested functions to be immutably pure must also guarantee that nothing 
> gets mutated through its stack frame pointer. But there's a problem -- the 
> compiler won't accept 'immutable' on a nested function. I think it 
> should -- just like an immutable member function (e.g. in a class) is 
> allowed to play only with immutable members of that class, an immutable 
> nested function should be allowed to play only with the immutable members 
> of the stack frame. It may seem a lesser change but I'm pretty excited as 
> it solves the long-standing problems with immutable structure 
> initialization.
>

>just like an immutable member function (e.g. in a class) is allowed to play 
>only with immutable members of that class

Defining a member function to be immutable doesn't mean it can only access 
immutable member variables, it means it must be called with an immutable 
class reference.

class C
{
 void fun() immutable {}
}

void main()
{
 auto c = new C();
 c.fun(); // error
 auto i = new immutable C();
 i.fun(); // ok
}

For nested functions, the reference is to the enclosing function's stack 
frame.  What does it mean to have this be immutable?  Maybe this makes sense 
if EVERY variable in the enclosing stack frame is immutable?

Is there any reason you couldn't just use static nested pure functions?

void main()
{
 static pure Node* grow_tree(int breadth) // strongly pure
 {
   ....
 }
 immutable Node* tree = grow_tree(...);
}
January 07, 2011
Re: Immutable nested functions
Daniel Murphy napisał:

> Defining a member function to be immutable doesn't mean it can only access 
> immutable member variables, it means it must be called with an immutable 
> class reference.
> 
> class C
> {
>   void fun() immutable {}
> }
> 
> void main()
> {
>   auto c = new C();
>   c.fun(); // error
>   auto i = new immutable C();
>   i.fun(); // ok
> }
> 
> For nested functions, the reference is to the enclosing function's stack 
> frame.  What does it mean to have this be immutable?  Maybe this makes sense 
> if EVERY variable in the enclosing stack frame is immutable?

Good point. I think the concept of immutable nested functions is still a solid improvement, even with requiring every variable of the enclosing stack frame to be immutable.

> Is there any reason you couldn't just use static nested pure functions?
> 
> void main()
> {
>   static pure Node* grow_tree(int breadth) // strongly pure
>   {
>     ....
>   }
>   immutable Node* tree = grow_tree(...);
> }

I fear one would need to tediously add arguments for *every* ingredient in the scope to be able to cook them inside. And if an ingredient turns out not necessary as the software evolves, there's the tedium of removing it from the nested function signature, which -- let's face it -- nobody does. Initializing immutable structures is fairly common and lack of a convenient way to do it has been recognized as one of the shortcomings of the const system. It can and should be solved.

-- 
Tomek
January 07, 2011
Re: Immutable nested functions
Tomek Sowin'ski wrote:
> A while ago I pointed out that the result of an immutably pure function (all arguments immutable, doesn't mutate globals) can be safely converted to immutable. More here:
> http://d.puremagic.com/issues/show_bug.cgi?id=5081
> 
> It helps with building complex immutable structures. Problem is, virtually every construction site is different so one is forced to define a new initializer function every time. To illustrate:
> 
> void main() {
>     immutable Node* init_leaf = ... ;
>     uint breadth = ... ;
>     immutable Node* tree = grow_tree(init_leaf, breadth);
> }
> 
> Node* grow_tree(immutable Node* init_leaf, uint breadth) pure {
>     Node* root = new Node;
>     foreach (0..breadth) {
>         Node* leaf = new Node(init_leaf);
>         leaf.parent = root;
>         root.leaves ~= leaf;
>     }
>     return root;
> }
> 
> I tried to find a way to create ad-hoc functions conveniently. Naturally, I turned to nested functions:
> 
> void main() {
>     immutable Node* init_leaf = ... ;
>     uint breadth = ... ;
>     
>     Node* grow_tree() pure immutable {
>         Node* root = new Node;
>         foreach (0..breadth) {
>             Node* leaf = new Node(init_leaf);
>             leaf.parent = root;
>             root.leaves ~= leaf;
>         }
>         return root;
>     }
>     
>     immutable Node* tree = grow_tree();
> }
> 
> Nested functions to be immutably pure must also guarantee that nothing gets mutated through its stack frame pointer. But there's a problem -- the compiler won't accept 'immutable' on a nested function. I think it should -- just like an immutable member function (e.g. in a class) is allowed to play only with immutable members of that class, an immutable nested function should be allowed to play only with the immutable members of the stack frame. It may seem a lesser change but I'm pretty excited as it solves the long-standing problems with immutable structure initialization.
> 
> Excitement aside, I got questions:
> 
> 1. I'm proposing to proliferate the concept of immutability from 'this' reference to a stack frame pointer. Although I'm confident it makes sense as these are interchangeable in delegates, I could use some criticism to either reject or strengthen the idea.
> 
> 2. What about delegates? Should there be means to express a "delegate that doesn't mutate through its 'this'/stack frame pointer"? What should the syntax be for defining such delegate type and for lambdas (delegate literals)?
> 
> 3. (vaguely related) Should there be means to express annotated delegates in general (e.g. pure, nothrow).

There is.
 int delegate(int) pure square
		= cast( int delegate(int z) pure )
		(int z) { return z*z; };

> What about annotated lambdas?

See the line above -- at the moment it requires a cast. Yuck yuck yuck.
The other option would be for the compiler to automatically determine 
purity. I believe it always has access to the source code of the lambda, 
so there should be no problem to determine purity.
January 08, 2011
Re: Immutable nested functions
On Fri, 07 Jan 2011 17:18:39 -0500, Don <nospam@nospam.com> wrote:

> Tomek Sowin'ski wrote:
>> A while ago I pointed out that the result of an immutably pure function  
>> (all arguments immutable, doesn't mutate globals) can be safely  
>> converted to immutable. More here:
>> http://d.puremagic.com/issues/show_bug.cgi?id=5081
>>  It helps with building complex immutable structures. Problem is,  
>> virtually every construction site is different so one is forced to  
>> define a new initializer function every time. To illustrate:
>>  void main() {
>>     immutable Node* init_leaf = ... ;
>>     uint breadth = ... ;
>>     immutable Node* tree = grow_tree(init_leaf, breadth);
>> }
>>  Node* grow_tree(immutable Node* init_leaf, uint breadth) pure {
>>     Node* root = new Node;
>>     foreach (0..breadth) {
>>         Node* leaf = new Node(init_leaf);
>>         leaf.parent = root;
>>         root.leaves ~= leaf;
>>     }
>>     return root;
>> }
>>  I tried to find a way to create ad-hoc functions conveniently.  
>> Naturally, I turned to nested functions:
>>  void main() {
>>     immutable Node* init_leaf = ... ;
>>     uint breadth = ... ;
>>         Node* grow_tree() pure immutable {
>>         Node* root = new Node;
>>         foreach (0..breadth) {
>>             Node* leaf = new Node(init_leaf);
>>             leaf.parent = root;
>>             root.leaves ~= leaf;
>>         }
>>         return root;
>>     }
>>         immutable Node* tree = grow_tree();
>> }
>>  Nested functions to be immutably pure must also guarantee that nothing  
>> gets mutated through its stack frame pointer. But there's a problem --  
>> the compiler won't accept 'immutable' on a nested function. I think it  
>> should -- just like an immutable member function (e.g. in a class) is  
>> allowed to play only with immutable members of that class, an immutable  
>> nested function should be allowed to play only with the immutable  
>> members of the stack frame. It may seem a lesser change but I'm pretty  
>> excited as it solves the long-standing problems with immutable  
>> structure initialization.
>>  Excitement aside, I got questions:
>>  1. I'm proposing to proliferate the concept of immutability from  
>> 'this' reference to a stack frame pointer. Although I'm confident it  
>> makes sense as these are interchangeable in delegates, I could use some  
>> criticism to either reject or strengthen the idea.
>>  2. What about delegates? Should there be means to express a "delegate  
>> that doesn't mutate through its 'this'/stack frame pointer"? What  
>> should the syntax be for defining such delegate type and for lambdas  
>> (delegate literals)?
>>  3. (vaguely related) Should there be means to express annotated  
>> delegates in general (e.g. pure, nothrow).
>
> There is.
>   int delegate(int) pure square
> 		= cast( int delegate(int z) pure )
> 		(int z) { return z*z; };
>
>> What about annotated lambdas?
>
> See the line above -- at the moment it requires a cast. Yuck yuck yuck.
> The other option would be for the compiler to automatically determine  
> purity. I believe it always has access to the source code of the lambda,  
> so there should be no problem to determine purity.

Umm...  int delegate(int) pure square =  delegate(int z) pure { return  
z*z; }; compiles and runs fine. What doesn't compile is (int z) pure {  
return z*z; }; or anything similar.
January 08, 2011
Re: Immutable nested functions
Robert Jacques napisał:

> >>  3. (vaguely related) Should there be means to express annotated  
> >> delegates in general (e.g. pure, nothrow).  
> >
> > There is.
> >   int delegate(int) pure square
> > 		= cast( int delegate(int z) pure )
> > 		(int z) { return z*z; };
> >  
> >> What about annotated lambdas?  
> >
> > See the line above -- at the moment it requires a cast. Yuck yuck yuck.
> > The other option would be for the compiler to automatically determine  
> > purity. I believe it always has access to the source code of the lambda,  
> > so there should be no problem to determine purity.  
> 
> Umm...  int delegate(int) pure square =  delegate(int z) pure { return  
> z*z; }; compiles and runs fine. What doesn't compile is (int z) pure {  
> return z*z; }; or anything similar.

Hm, you're right. Also, the argument list seems to be required with annotations:

int delegate() pure dg =  delegate pure { return 0; };  // error
int delegate() pure dg =  delegate() pure { return 0; };  // compiles

BTW, the syntax landscape seems densely populated, I wonder if demanded constructs like pure { } wouldn't confuse the parser in some corner-case.

It's all just syntax, though. I'm glad there's any way to get the job done.

-- 
Tomek
Top | Discussion index | About this forum | D home