Program size, linking matter, and static this()
Jonathan M Davis
jmdavisProg at gmx.com
Fri Dec 16 16:54:57 PST 2011
On Friday, December 16, 2011 18:05:56 Andrei Alexandrescu wrote:
> On 12/16/11 5:50 PM, Jonathan M Davis wrote:
> http://en.wikipedia.org/wiki/Singleton_pattern
>
> Second paragraph.
Valid points, but it's still useful under some circumstances. I don't actually
use it very often personally. It just made sense here. Thanks for the link.
> You're using a stilted version of it. Most often the singleton object is
> created lazily upon the first access, whereas std.datetime creates the
> object (and therefore shotguns linkage with the garbage collector) even
> if never needed.
>
> But what I'm trying here is to lift the level of discourse. The
> Singleton sounds like the solution of choice already presupposing that
> inheritance and polymorphism are good decisions. What I'm trying to say
> is that D should be rich enough to allow you considerable freedom in the
> design space, so we should have enough means to navigate around this one
> particular issue. I don't think we can say with a straight face we can't
> avoid use of static this inside std.datetime.
The only reason that it's not lazily loading is because of the purity issue an
the fact that it would require a mutex. The mutex we can live with. pure can't
be gotten around easily, but I'll figure it out.
As for the general design, SysTime needs to be able to dynamically adjust its
value based on the time zone upon request (e.g. asking for the SysTime as a
string or asking for the that SysTime's year). That essentially requires that
the set of functions required for the calculations be swappable (preferably as
a group, since that's far cleaner). Encapsulating it in a class gives you that
polymorphic behavior quite nicely and also groups the various functions quite
nicely. It also gives you a nice place to put some stuff like the time zone's
name. Sure, we could theoretically change it to' be struct which holds
function pointers, but that seems to me like you're pretty much just trying to
redesign classes that way. I think that the basic design is solid.
> > There would be fewer potential issues with circular dependencies if
> > std.datetime were broken up, but the consensus seems to be that we don't
> > want to do that. Regardless, if I find a way to lazily load the
> > singletons in spite of immutable and pure, then there won't be any more
> > need for the static constructors for them. There's still one for the
> > unit tests, but worse comes to worst, that functionality could be moved
> > to a function which is called by the first unittest block.
>
> Maybe the choice of immutable and pure is too restrictive. How about
> making the object returned const?
SysTime holds an immutable TimeZone (currently with Rebindable). In theory,
this should have the advantage of making it possible to pass a SysTime across
with send and receive, but bugs in the compiler currently make it impossible
to construct and immutable SysTime. So, all TimeZone objects are const, or
they won't work with SysTime. And since there's not normally a reason to
change any of the values in a TimeZone (they don't hold much data in the first
place), that's really not a problem.
The only problem with making it immutable has to do with the singleton. I
suppose that it could be change to Rebindable!(immutable TimeZone) like in
SysTime, but when I designed it, there didn't seem much point to that, since
it had to be constructed at runtime and required a static constructor
regardless. And I was trying to make absolutely as much in std.datetime pure
as possible, which inevitably led to the singletons being pure. Making them
impure makes it so that a variety of other functions can't be pure and would
break code. I don't remember how much however.
Regardless, to avoid breaking code, it has to pure. It's possible that the
code breakage would be worth it, but I'd have to mess around with it to see.
With appropriate casts, pure can be subverted, but that's obviously ugly.
> Under what circumstances it doesn't work,
I couldn't move the singletons out of std.datetime in that way. pure disallows
it.
> and how would adding _more_
> support for _less_ safety would be better than a glorified cast that you
> can use _today_?
>
> > Clearly, I'm not going to win any arguments on this, given that both you
> > and Walter are definitely opposed, but I definitely think that the
> > current situation with circular dependencies is one of D's major warts.
>
> I'm not nailed to the floor. Any good arguments would definitely change
> my opinion.
I don't think that I have ever seen an _actual_ circular dependency when a
program blows up because of it. It's always a case of the two modules doing
completely unrelated stuff with their static constructors. It's generally
incredibly obvious that there's no interdependency, but the compiler/runtime
isn't smart enough to see that. And if you use static constructors much (which
invariably happens if you have much in the way of immutable variables which
are commonly used enough to put at module or class scope), you run into this
problem fairly easily. And given the large amount of inter-module importing in
Phobos, it's _very_ easy to run into the problem there if we use static
constructors.
When such circular dependencies happen, it's a royal pain to sort out what's
going on - especially if the modules to import each other directly. The error
messages have improved, but it's still nasty to sort out exactly what's
happening. And then fixing it? Assuming that you can use the solution that some
of Phobos' modules use by having a secondary module for the initialization,
then there's a way to do it, but that solution is quite ugly IMHO, and
regardless of that, it's _not_ in the least bit obvious. I don't know that I
ever would have thought of it myself (maybe, maybe not).
So, the programmer is essentially faced with a situation where they have two
modules with static constructors that they can clearly see are completely
unrelated, but they're going to have to do some major refactoring to get
around the issue that the compiler and runtime _aren't_ smart enough to see
that there order that the modules are initialized doesn't matter at all. _If_
they think of the solution that Phobos uses or are lucky enough to have
someone else points it out to them _and_ it's actually possible to refactor
the static constructor out like that, then the solution is doable, albeit
arguably on the ugly side. But that's assuming a lot IMHO.
By contrast, we could have a simple feature that was explained in the
documenation along with static constructors which made it easy to tell the
compiler that the order doesn't matter - either by saying that it doesn't
matter at all or that it doesn't matter in regards to a specific module. e.g.
@nodepends(std.file)
static this()
{
}
Now the code doesn't have to be redesigned to get around the fact that the
compiler just isn't smart enough to figure it out on its own. Sure, the feature
is potentially unsafe, but so are plenty of other features in D. The best
situation would be if the compiler was smart enough to figure it out for
itself, but barring that this definitely seems like a far cleaner solution than
having to try and figure out how to break up some of the initialization code
for a module into a separate module, especially when features such as
immutable and pure tend to make such separation impossible without some nasty
casts. It would just be way simpler to have a feature which allowed you to
tell the compiler that there was no dependency.
I'd probably feel differently about this if static constructors tended to have
actual interdependencies, but they are almost invariably used for initializing
immutable variables and the like and have no dependencies on other modules at
all. It's other stuff in the modules which have those interdependencies.
- Jonathan M Davis
More information about the Digitalmars-d
mailing list