-
Notifications
You must be signed in to change notification settings - Fork 432
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Deprecate Rng::sample and Rng::sample_iter #552
Conversation
f7ebf1e
to
8fd4d0d
Compare
…sample and Distribution::sample_iter
Any ideas what the test failure is? Looks like a tool problem.
|
There is one difference IIRC: I agree that these are redundant, though I'm still unsure about removing them. |
Yeah, I've always found that pretty weird. Especially since you usually want to sample from a Fortunately, because of this, you can still pass a reference to a |
Yeah, it's weird. It's because some distributions have no parameters, thus you don't really need an instance: |
Ah, I see. So would we still need that if this PR lands? |
Well, this works: let x: f32 = Standard.sample(&mut rng); But there's no easy way to specify the type within the function call (since neither rng.sample::<f32, _>(Standard) |
You can always do Distribution::<f32>::sample(Standard, rng) But if we think that's too verbose, we could add pub trait DistributionAs {
fn sample_as<T, R: Rng + ?Sized>(&self, rng: &mut R) -> T
where Self: Distribution<T>;
}
impl<D> DistributionAs for D {
fn sample_as<T, R: Rng + ?Sized>(&self, rng: &mut R) -> T
where Self: Distribution<T> {
self.sample(rng)
}
} which makes it possible to write Standard.sample_as::<f32, _>(rng) |
I don't know, now you're just adding another complication to replace the first 😄 Removing these doesn't seem like a usability win or to have much advantage to be honest. Besides, your original argument is that Any other rationales? Otherwise I'm going to close this. |
I don't really see why As things stand right now, I agree that the disambiguation rules for the let x: f32 = Standard.sample(rng);
do_stuff(x); and get compilation errors when changing to do_stuff(Standard.sample(rng)); I don't think it's going to be obvious to that person to switch to do_stuff(rng.sample::<f32, _>(Standard); I.e. I don't think I definitely think that the simplest solution to the problem of disambiguation is to simply rely on the standard rust features of The other thing to remember is that this is mainly a problem for |
I don't understand why you want to deprecate |
I don't think we should remove Maybe I was wrong in removing Technically, yes, we could remove With that in mind, I suppose we could un-deprecate |
This is not really reflected by the documentation. It does not clarify that only
Alternatively, we could move |
|
We also have to consider that let secret_number = rand::thread_rng().gen_range(1, 101); If we want to reduce the API surface, we should probably discuss (maybe even with the docs team) whether something like let mut rng = rand::thread_rng();
let secret_number = rand::Uniform::new(1, 101).sample(&mut rng); is newcomer-friendly enough. |
I don't want to advocate a large and complex API, but in this case I don't think a small amount of redundancy (in the form of convenience functions wrapping other implementations) adds much complexity, and I think it does add a significant amount of convenience (look at the example just posted; even ignoring repeated symbols and local variables, it goes from 3 identifiers to 5: There's a case for teaching users the building blocks of a toolkit and a case for teaching how to accomplish basic tasks; I think good design has to consider trade-offs between the two. |
Arguably
Fair enough, I'm only slightly worried that this is not made clear by the current documentation. |
Oops, missed those changes. Deprecating Like @dhardy I very much see
In this case I don't see the distribution as a source of data, but as a transformation. Not really sure how to put it though. |
Ok, seems I'm the only one that feel these are out of place, and are sufficiently bothered by the redundancy to think that we should remove them. |
In #483 we deprecated
Rng::choose
andRng::choose_mut
in favor ofSliceRandom::choose
andSliceRandom::choose_mut
. The argument (presented here) was that we can think of the the rng object as a source of data. So functions (likegen_range
) which generate data should live on theRng
trait. Whereas when we're operating on other data (likechoose
), we should stick functions on those data objects and take theRng
as a parameter.I.e.
vec.choose(rng)
rather thanrng.choose(vec)
.I think that argument also argues for doing
distribution.sample(rng)
rather thanrng.sample(distribution)
. The namesample
reinforces that the distribution is the source of the data that we're sampling from.I also think it's generally a bad idea to have two functions that do the same thing. Feels like a source of confusion where it's easy to wonder what the difference is or why one should choose one over the other.
Same goes for
sample_iter
.This PR deprecates
Rng::sample
andRng::sample_iter
and changes all internal callers (mostly tests) to useDistribution::sample
instead (there were no internal callers ofRng::sample_iter
).With this change, all functions on
Rng
generate new values, making the trait feel more coherent. The values are either returned (most functions) or written to a passed in buffer (fill
,try_fill
).