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