On Thursday, 20 September 2012 at 07:26:01 UTC, Jonathan M Davis wrote:
On Thursday, September 20, 2012 08:51:28 monarch_dodra wrote:
On Tuesday, 18 September 2012 at 17:59:04 UTC, Jonathan M Davis

wrote:
> On Tuesday, September 18, 2012 17:05:26 monarch_dodra wrote:
>> This is issue #1: I'd propose that all objects in >> std.random be >> migrated to classes (or be made reference structs), sooner >> than >> later. This might break some code, so I do not know how >> this is >> usually done, but I think it is necessary. I do not, >> however,
>> propose that they should all derive from a base class.
> > Moving to classes would definitely break code, but it should > be
> possible to
> make them reference types simply by making it so that their
> internal state is
> in a separate object held by a pointer.

I was thinking of doing that. The problem with this (as I've run
into and stated in another thread), is a problem of
initialization: The simpler PRNGs are init'ed seeded, and are
ready for use immediately. Changing to this approach would break
the initialization, as shown in this post:

http://forum.dlang.org/thread/bvuquzwfykiytdwsq...@forum.dlang.org#post-yvts
ivozyhqzscgddbrl:40forum.dlang.org

A "used to be valid" PRNG has now become an un-initialized PRNG". This is extremely insidious, as the code still compiles, but will
crash.

There's always the check that the internals have been initialized on every call and initialize it if it hasn't been solution. It's not pretty, but it won't break code. It's actually a use case that makes me wish that we had something like the invariant which ran before every public function call except that it was always there (even in -release) and let you do anything you
want.

In any case, while it's a bit ugly, I believe that simply adding checks for initialization in every function call is the cleanest solution from the standpoint of backwards compatibility, and the ugliness is all self-contained. As far as performance goes, it's only an issue if you're iterating over it in a tight loop, but the actual random number generation is so much more expensive than a check for a null pointer that it probably doesn't matter.

#2
Change to class, but leave behind some "opCall"s for each old
constructor, plus an extra one for default:

Is this second solution something you think I should look into?

Since

A a;

will just blow up in your face if you switch it to a class, it's not a non- breaking change even as a migration path, so I don't see that as really being viable. Even if you've found a way to minimize the immediate code breakage, you didn't eliminate it. If you're going to break code immediately, you might as well just break it all at once and get people to fix their stuff rather than mostly fix it but not quite, especially when you're asking them to change their
code later anyway as part of a migration path.

Regardless, when this came up previously, I believe that the conclusion was that if we were going to switch to classes, we needed to do something like create std.random2 and schedule std.random for deprecation rather than changing the current structs to classes (either that or rename _every_ type in there and schedule them for deprecation individually, but then you have to come up for new names for everything, and it's more of a pain to migrate, since all the names changed rather than just the import). So, I believe that the idea of switching to classes was pretty much rejected previously unless
entirely new types were used so that no code would be broken.

I think that we have two options at this point:

1. Switch the internals so that they're in a separate struct pointed to by the outer struct and check for initialization on every function call to avoid the
problem where init was used.

2. Create a new module to replace std.random and make them final classes in
there, scheduling the old module for deprecation.

Honestly, I'd just go with #1 at this point, because it avoids breaking code, and there's increasing resistance to breaking code. Even Andrei, who was fairly willing to break code for improvements before, is almost paranoid about it now, and Walter was _always_ against it. So, if we have a viable solution that avoids breaking code (especially if any ugliness that comes with it is internal to the implementation), we should probably go with that.

- Jonathan M Davis

TY for your insight. Good points. I'll try to do your "#1": It is simple and non breaking. *IF* we ever do decide to break, and rather it be done after a very thourough discussion of requirements, specifications, migration path, etc...

Reply via email to