Walter Bright Wrote:
> I'm not happy with this solution, but it seems to be the best compromise 
> I can come up with.
> 
> What do you think?

The first thought that occurred to me was "how this was handled in other 
languages".  As C# is one of the more recent languages, and has some static 
construction I thought I'd try to create a loop-like-dependency test case.  C# 
does not have modules, but it seems to me that a class with only static members 
and a static constructor is roughly analogous.

I'm not sure how similar a test case this really is, to be honest it's been a 
while since I wrote some D and I haven't used mixin and static construction in 
depth but perhaps someone can take my example and improve it.  I can at least 
explain what C# is doing in my test case and why, and it raises a question 
about how the D implementation works.

First, the C# test case, based on ...

http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=107373

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace StaticConstruct
{
        class Program
        {
                static void Main(string[] args)
                {
                        System.Console.Out.WriteLine("A = {0}", A.a);
                        System.Console.Out.WriteLine("B = {0}", B.b);
                }
        }

        class A
        {
                public static int a;

                static A()
                {
                        a = 1;
                }
        }

        class B
        {
                public static int b;

                static B()
                {
                        b = 2;
                }
        }
}

Simple enough, neither A nor B actually depend on each other.  But what does C# 
do with the above.  It's actually pretty simple, when it executes the first 
WriteLine it evaluates A.a, when it does that it calls the static constructor 
for A.  Likewise for B.b the same thing occurs.  So, it's effectively 
performing lazy construction/evaluation.  Does D do this, or does D attempt to 
construct _all_ modules on program start?  Could this be the solution, to make 
module construction lazy?

Lets try a few more complex examples...

1)
Change "public static int a;" to "public static int a = B.b;" (create 
dependency on B)
Change "public static int b;" to "public static int b = A.a;" (create 
dependency on A)
This creates a circular dependency.

What happens:
01.When it executes the A.a WriteLine it needs to evaluate A.a, so it needs to 
initialize A.
02.To initialise A it must initialize the 'globals' i.e. "public static int a = 
B.b;"
03.This causes it to evaluate B.b, causing initialization of B
04.To initialise B it must initialize the 'globals' i.e. public static int b = 
A.a;".
05.At this point it assigns 0 to b, as A.a is currently 0 and as it has already 
entered initialization of A (preventing infinite loop)
06.It then calls the static constructor for B, assigning b = 2;
07.It then completes "public static int a = B.b;" assigning a = 2;
08.It then calls the static constructor for A, assigning a = 1;
09.It returns control to the WriteLine outputting "A = 1"
10.The call to the 2nd WriteLine simply outputs "B = 2"

So, here we see that the lazy construction/evaluation, paired with a flag for 
detecting re-entrant initialization resolves the circular dependency.

2)
Add a static function to 'module' A, eg.

        class A
        {
                public static int a = B.b;

                static A()
                {
                        a = 42;
                }

                public static int Foo()
                {
                        return A.a;
                }
        }

and a call to main:

                        System.Console.Out.WriteLine("A.Foo = {0}", A.Foo());
                        System.Console.Out.WriteLine("A = {0}", A.a);
                        System.Console.Out.WriteLine("B = {0}", B.b);

So, what happens now:

When it executes the Foo() WriteLine it triggers initialization of A (because 
foo is a static member of A), this triggers the process shown in 1) above, 
lines 02 thru 08 before returning control to the WriteLine.  As a consequence 
all the initialization is done by the next WriteLine call, so it simply 
completes etc.

Thoughts?

Reply via email to