Reference semantic ranges and algorithms (and std.random)

monarch_dodra monarchdodra at gmail.com
Thu Sep 20 02:18:27 PDT 2012


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/bvuquzwfykiytdwsqkky@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...


More information about the Digitalmars-d mailing list