November 29, 2012
On Thursday, 29 November 2012 at 21:43:30 UTC, Jonathan M Davis wrote:
> On Thursday, November 29, 2012 21:08:58 Jacob Carlborg wrote:
>> BTW, how does Java handle this? And C# if it has something similar.
>
> They just let you blow your foot off. All static variables can be directly
> initialized at runtime, so it's easy to use variables before they're actually
> initialized. I don't know how they decide what order to run static
> constructors in, but AFAIK, it never worries about circular dependencies.
> We're only running into this problem beacuse we're trying to provide higher
> safety and better guarantees with regards to when and how variables are
> initialized.
>
> - Jonathan M Davis

Java have static block to pre initialize stuff before it is used. But I'm not sure how they react in case of cyclic dependancies.
November 29, 2012
On 11/29/2012 09:30 PM, Manfred Nowak wrote:
> Max Samukha wrote:
>
>> there must not be circular dependency issues
>
> The model shown does not force circular dependencies, because
> every receiver module only imports one module: `module reflect'.
> And I can not see, that this module imports any other module.
>
> Therefore `module relect' and its `import' statements are not
> responsible for any circular dependency issue.
>
> Where am I wrong?
>
> -manfred
>

A precondition for (pseudo-) circular dependency issues of this kind are static constructors. The static constructors are what module reflect imposes in its users in order to operate.
November 29, 2012
On 11/29/2012 10:45 PM, deadalnix wrote:
> On Thursday, 29 November 2012 at 21:43:30 UTC, Jonathan M Davis wrote:
>> On Thursday, November 29, 2012 21:08:58 Jacob Carlborg wrote:
>>> BTW, how does Java handle this? And C# if it has something similar.
>>
>> They just let you blow your foot off. All static variables can be
>> directly
>> initialized at runtime, so it's easy to use variables before they're
>> actually
>> initialized. I don't know how they decide what order to run static
>> constructors in, but AFAIK, it never worries about circular dependencies.
>> We're only running into this problem beacuse we're trying to provide
>> higher
>> safety and better guarantees with regards to when and how variables are
>> initialized.
>>
>> - Jonathan M Davis
>
> Java have static block to pre initialize stuff before it is used. But
> I'm not sure how they react in case of cyclic dependancies.

In Java and C# static constructors are run whenever the class is first referenced. This means that almost any statement in Java or C# may cause arbitrary side-effecting code to run. Circular dependencies are not considered at all. If two static constructors depend on each other's statically initialized parts, at least one of them will see uninitialized data.
November 29, 2012
On 11/29/2012 01:17 PM, Jonathan M Davis wrote:
> In the past when I've brought up similar solutions, he's been completely opposed to them.
> ...

It is not a solution, it is a workaround.
November 29, 2012
On Thursday, November 29, 2012 23:28:07 Timon Gehr wrote:
> On 11/29/2012 01:17 PM, Jonathan M Davis wrote:
> > In the past when I've brought up similar solutions, he's been completely opposed to them. ...
> 
> It is not a solution, it is a workaround.

What do you mean? The runtime sees circular dependencies between modules even when there's no actual circular dependency between static constructors. We need to fix that. One way is to just make the runtime not care, which wouldn't be particularly safe. Another is to explicitly tell it that there are no such dependencies. I don't see how that's not a solution. And unless someone can come up with a way for the runtime to somehow determine on its own that there's no actual, circular dependency, I don't see how anything better could be done.

Granted, I think that having a single pragma for the whole module like Walter is suggesting (as opposed to marking static constructors individually) is a bad idea because of the silent breakage that it can cause, but the basic idea is solid.

- Jonathan M Davis
November 29, 2012
On 11/29/2012 11:58 PM, Manfred Nowak wrote:
> This is sufficient only for a simple cycle without any branches
> or a trivial clique like the one shown. Please recall, that in a
> clique every member is connected to every other member---in this
> case by an import statement.
>
> It is already not sufficient for a simple clique consisting of
> three modules, because now orderinh would be given for the
> remaining two modules.

If you have:

   a imports b,c
   b imports a,c
   c imports a,b

then two of those will need the pragma, that is correct. I don't see an issue with that, because there are two cycles.

> And it is ambiguous if both of the modules in the example given
> are marked with that pragma.

If:

   a imports b
   b imports a

and a has the pragma, then the order of construction is a,b. If both have the pragma, then the order is a,b or b,a, yes, it is ambiguous, but it is not an error. One gets picked arbitrarily.


> In general a topological sorting has to be specified for all
> strongly connected components of the graph of imports.

I believe that the pragma does that.

> This cannot be done within the module, because this would bind
> the module to that special strongly connected component in that
> special set of  modules. Which in turn would destroy reusability
> of the module for other programming tasks.

I just don't see the problem.

November 29, 2012
On 11/30/2012 12:09 AM, Daniel Murphy wrote:
> I don't think this is sufficient.  Imagine a group of modules that really
> _do_ have a cyclic dependency, and a mixin that adds an independent static
> this.  Ideally you'd be able to mark the mixed-in constructor as independent
> without tainting the whole module.
>
> So just make the pragma apply to declarations, you either mark specific
> functions (which can then be mixed in) or put `pragma(...):` at the top of
> your module and you get your behaviour.

It is possible for each static constructor to specify independently of the other static constructors which imports must be constructed first. But do we really want to go that far?

November 29, 2012
On 11/30/2012 9:43 AM, Walter Bright wrote:
> It is possible for each static constructor to specify independently of
> the other static constructors which imports must be constructed first.
> But do we really want to go that far?

One way to do that might be to borrow syntax from classes:

   static this() : std.stdio, a, c
   {
       ...
   }

and the  this static constructor only requires that modules std.stdio, a, and c be constructed first.

   static this() : void
   {
       ...
   }

means it has no dependencies on other imports.

   static this()
   {
      ...
   }

has the current behavior (all imported modules must be constructed first).

November 29, 2012
On Thursday, 29 November 2012 at 16:51:29 UTC, Max Samukha wrote:
> On Thursday, 29 November 2012 at 15:18:11 UTC, Paulo Pinto wrote:
>> On Thursday, 29 November 2012 at 12:04:28 UTC, Max Samukha wrote:
>>> On Thursday, 29 November 2012 at 11:39:20 UTC, Paulo Pinto wrote:
>>>> On Thursday, 29 November 2012 at 03:19:55 UTC, Andrei Alexandrescu wrote:
>>>>> On 11/28/12 9:34 PM, Walter Bright wrote:
>>>>>> For discussion:
>>>>> [snip]
>>>>>
>>>>> I'd say we better finish const, immutable, and shared first.
>>>>>
>>>>> Andrei
>>>>
>>>> +1
>>>>
>>>> Fully agree.
>>>>
>>>> Cyclic imports are a minor nuisance that can be easily solvable with better code architecture.
>>>
>>> Show me please how to solve that problem easily with acceptable results, would you?
>>
>> You just need to have a better architecture.
>>
>> In 20 years of software development experience I never found a case were this wasn't possible.
>
> That's an argument from authority, sorry.
>
>>
>> Maybe you care to provide an example?
>>
>
> The general problem is constructing global data structures based on data introspected at compile-time.
>
> My specific problem is extending scarce runtime type information provided by the language with something usable for runtime reflection. With lots of detail omitted:
>
> module reflect;
>
> Meta[string] metas;
> mixin template Reflect(alias object) {
>     static this()
>     {
>         auto m = meta!(object);
>         metas[m.fullName] ~= m;
>     }
> }
>
>
> module a;
> import reflect;
>
> struct S
> {
> }
> mixin Reflect!S;
>
> The meta-object for S is automatically made available at runtime through the global metas array. Note that we do not want to force the user to register the meta-object manually because then it would not be a "better architecture".
>

For me too much use of meta capabilities is not a better architecture,
as quite often it leads to write only code.

If you are alreading writing code on the client side for initialization, like your mixin definition, the client already needs to make the reflect module of its existence, so why not call an initialization function that avoids the static constructors issues altogether?


--
Paulo

November 30, 2012
On 11/29/12 5:43 PM, Walter Bright wrote:
> On 11/30/2012 12:09 AM, Daniel Murphy wrote:
>> I don't think this is sufficient. Imagine a group of modules that really
>> _do_ have a cyclic dependency, and a mixin that adds an independent
>> static
>> this. Ideally you'd be able to mark the mixed-in constructor as
>> independent
>> without tainting the whole module.
>>
>> So just make the pragma apply to declarations, you either mark specific
>> functions (which can then be mixed in) or put `pragma(...):` at the
>> top of
>> your module and you get your behaviour.
>
> It is possible for each static constructor to specify independently of
> the other static constructors which imports must be constructed first.
> But do we really want to go that far?

I think we either do it right or leave it as it is. It's not like there's no workaround so if we take a stand here we better have something compelling.

Andrei