In one nice file put this:
var Hashtable<String, ()->Thing> factory = hashtable();
------------ in another put this:
var boolean thing_initialized = thing_initialize();
boolean thing_initialize() {
factory.put("p2pmud.Thing", int i => new Thing(id: i));
return true;
}
This will sometimes produce a null pointer exception, a
thing which should not be possible, since factory is
not of an optional type (and it violates the
no-null-poiner-exception claim of Nice;) ). To work
around it, I had to do use lazy initailization:
var boolean hasFactory = false;
var Hashtable<String, int->Thing> factoryTable;
boolean addHook(String className, int->Thing hook) {
if (!hasFactory) {
factoryTable = hashtable();
hasFactory = true;
}
factoryTable.put(className, hook);
return true;
}
---- The other file:
var boolean thing_initialized = thing_initialize();
boolean thing_initialize() = addHook("p2pmud.Thing",
(int i) => new Thing(id: i));
[noticed compiler bug when I tried to inline the
thing_initialized value -- I'll submit another bug
report for that one]
Static initialization issues have plagued us at least
since C++ made the problem popular. Perhaps there is a
solution to this problem. Would it be too hard to use
a dependency graph for thing_initialized to dertermine
that it depends on factory having a value initialized?
Logged In: YES
user_id=88952
Yes, this is a problem. I think in Java this is handled by
specifying strictly the evaluation order. It might be more
tricky in Nice because global variables are not inside
classes. There could be a partial order for variables in the
same file...
Yes, there could be a dependency analysis for simple cases.
But in general you could call arbitraty code, that calls
methods. The implementation of the method could be in a
other package that is not yet known, and will make use of a
variable of this package. So I think a global solution is
not possible like that.
For the moment, lazy initialization is a good idea. Or you
could decide to put the factory and the addHook function in
an object (a singleton), which would give the proper
initialization order automatically.
Actually what I would favor is that complex code like
addHook(...) shoud be called from an explicit initialization
function init()).
I am open to suggestions to prevent this kind of problem
automatically. It is indeed bothering to have
NullPointerException from a type-safe program, although in a
restricted area (static initialization).
Daniel
PS: with lazy initialization, you could use "if (factory ==
nul)" and save the use of the boolean. However it is
strange, since the type says factory cannot be null. A
future version of the compiler might issue a warning or an
error because of that.
Logged In: YES
user_id=88952
There has been some progress in this domain recently. The
global values used to be initialized in a somewhat
unpredictable order, causing problems as reported here when
one global depended on another. A recent change in the
compiler (present in the development version, future 0.9.1)
now guarantees much more properties:
1) if the value of a global g1 references another one g2,
then g2 will be initialized before g1 (provided there is no
cycle of course).
2) if g2 occurs after g1 in the same file, and neither is
referenced by another global, then g1 is initialized before g2.
This solves a lot of situations already. What is not handled
yet is "hidden references" though a method call (which
happens in your example, so I won't close this report). It
might be possible to extend the analysis for these cases,
although it is more complex. So I keep an eye on this issue.
Logged In: YES
user_id=88952
I just implemented a more thourough handling of dependencies
between packages variables. Now both direct references and
indirect depency through the default implementation of a
method are considered. This second case in particular covers
the original case in this report.
This system can still fail, notably if the dependency comes
from a specialization of the a called method. It would be
more difficult to handle that, especially as that
specialization could be made in another package (this would
rarely affect the initialization of the current package if
we don't import that other one, but still might, at least
when using reflexion). The current implementation still
covers safely much more situations than before, so it's a
good step ahead. I think it's fair to now close this report,
which hopefully will hold the longevity record for a long
time ;-)
(BTW, Bill, how are you doing? :-)