Reference semantic ranges and algorithms (and std.random)
monarch_dodra
monarchdodra at gmail.com
Thu Sep 20 03:23:12 PDT 2012
On Thursday, 20 September 2012 at 09:58:41 UTC, Johannes Pfau
wrote:
> Am Thu, 20 Sep 2012 08:51:28 +0200
> schrieb "monarch_dodra" <monarchdodra at gmail.com>:
>
>> > 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:
>
> How would the internal state be allocated? malloc + refcounting
> or GC?
>
> I'm not really happy about that as I'd like to avoid allocation
> (especially GC) whenever possible. Using a PRNG only locally in
> a
> function seems like a valid use case to me and it currently
> doesn't
> require allocation. As the similar problem with
> std.digest/std.algorithm.copy has showed value type ranges seem
> not
> very well supported in phobos.
>
> I wonder why we don't pass all struct/value type ranges by ref?
> Is
> there a reason this wouldn't work?
>
> Or we could leave the PRNGs as is and provide reference
> wrappers. This
> would allow to place the PRNG on the stack and still make it
> work with
> all range functions. But it's also cumbersome and error prone.
>
> Best solution is probably to have wrappers which allocate by
> default,
> but also allow to pass a reference to a stack allocated value.
> Then
> make the wrappers default, so the default's safe and easy to
> use.
>
> Pseudo-code:
>
> struct RNG_Impl
> {
> uint front();
> void popFront();
> bool empty();
> }
>
> struct RNG
> {
> RNG_Impl* impl;
>
> this(ref RNG_Impl impl)
> impl = cast(RNG_Impl*) impl;
>
> void initialize()
> {
> assert(!impl); //Either initialize or constructor
> impl = new RNG_Impl();
> }
> }
>
> RNG_Impl impl;
> RNG(impl).take(5); //No allocation (but must not leak
> references...)
>
> Regarding the initialization check: I'd avoid the check in
> release
> mode. Not initializing a struct is a developer mistake and
> should be
> found in debug mode. I think it's unlikely that error handling
> code can
> handle such a situation anyway. But you could check how
> std.typecons.RefCounted handles this, as it also need explicit
> initialization.
That is a good point, I'd also make the "default" heap allocated,
but give a way to access a stack allocated payload.
Regarding the "developer mistake", the problem is that currently:
"Misnstdrand a;"
Will create a valid and seeded PRNG that is ready for use, so we
can't break that.
Arguably though, the argument holds for Mersenne twister, which
needs a function call to be (default) seeded.
HOWEVER I find that:
auto a = MersenneTwister(); //Not seeded and invalid
auto b = MersenneTwister(5); //Seeded and valid
Is a confusing, especially since MersenneTwister provides
"seed()" to default seed.
I'd rather have:
auto a = MersenneTwister(); //Not *yet* seeded: It will be done
on the fly...
auto b = MersenneTwister(5); //Seeded and valid
But AGAIN, on the other hand, if you change back again to your
proposed stack allocated MersenneTwisterImpl:
auto a = MersenneTwister(); //Not Seeded, but not assertable
auto b = MersenneTwister(5); //Seeded and valid
It really feels like there is no perfect solution.
********
The truth is that I would have rather ALL prngs not have ANY
constructors, and that they ALL required an explicit seed: This
would be uniform, and not have any surprises.
OR
That creating a prng would seed it with the default seed if
nothing is specified, meaning that a prng is ALWAYS valid.
It feels like the current behavior is a bastardly hybrid...
More information about the Digitalmars-d
mailing list