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

Abstracting type limits (numeric and not only). #2252

Open
wants to merge 3 commits into
base: master
from

Conversation

Projects
None yet
10 participants
@vityafx

vityafx commented Dec 21, 2017

This RFC proposes to add a unified way of introducing type limits, for both numeric and non-numeric types (custom).

Rendered

@vityafx vityafx changed the title from Create 0000-add-limits-trait.md to Abstracting type limits (numeric and not only). Dec 21, 2017

@Centril

This comment has been minimized.

Show comment
Hide comment
@Centril

Centril Dec 21, 2017

Contributor

Added a rendered link for you =)

Contributor

Centril commented Dec 21, 2017

Added a rendered link for you =)

@Centril

I'm sympathetic to the core idea behind this RFC. Here are some hopefully helpful comments =)

Show outdated Hide outdated text/0000-add-limits-trait.md Outdated
Show outdated Hide outdated text/0000-add-limits-trait.md Outdated
Show outdated Hide outdated text/0000-add-limits-trait.md Outdated
Show outdated Hide outdated text/0000-add-limits-trait.md Outdated
Show outdated Hide outdated text/0000-add-limits-trait.md Outdated
Show outdated Hide outdated text/0000-add-limits-trait.md Outdated
Show outdated Hide outdated text/0000-add-limits-trait.md Outdated
Show outdated Hide outdated text/0000-add-limits-trait.md Outdated
Show outdated Hide outdated text/0000-add-limits-trait.md Outdated
Show outdated Hide outdated text/0000-add-limits-trait.md Outdated

@Centril Centril added the T-libs label Dec 21, 2017

@Centril

This comment has been minimized.

Show comment
Hide comment
@Centril

Centril Dec 21, 2017

Contributor

@Centril Thank you very much for your comments here. Should I answer here and when we come to a compromise reflect the result in the rfc itself or should I answer right there in the rfc file?

On-going discussion should mainly happen in comments on the PR itself rather than the file you're proposing we merge. To the extent possible, prefer line comments and replying to them as it is the closest we can get to threaded discussion, on GitHub, which tends to be more readable as it allows us to discuss a single issue at a given place rather than mixing all issues together. Whenever you feel that you've changes to make that elaborate, change design decisions, adds alternatives, and so on, just add additional commits.

Contributor

Centril commented Dec 21, 2017

@Centril Thank you very much for your comments here. Should I answer here and when we come to a compromise reflect the result in the rfc itself or should I answer right there in the rfc file?

On-going discussion should mainly happen in comments on the PR itself rather than the file you're proposing we merge. To the extent possible, prefer line comments and replying to them as it is the closest we can get to threaded discussion, on GitHub, which tends to be more readable as it allows us to discuss a single issue at a given place rather than mixing all issues together. Whenever you feel that you've changes to make that elaborate, change design decisions, adds alternatives, and so on, just add additional commits.

@mark-i-m

This comment has been minimized.

Show comment
Hide comment
@mark-i-m

mark-i-m Dec 21, 2017

Contributor

Can associated constants be used instead?

EDIT I mean this: https://doc.rust-lang.org/1.15.0/book/associated-constants.html

Contributor

mark-i-m commented Dec 21, 2017

Can associated constants be used instead?

EDIT I mean this: https://doc.rust-lang.org/1.15.0/book/associated-constants.html

@mark-i-m

This comment has been minimized.

Show comment
Hide comment
@mark-i-m

mark-i-m Dec 21, 2017

Contributor

Also, this RFC seems to be using the older format, rather than the newer format.

Contributor

mark-i-m commented Dec 21, 2017

Also, this RFC seems to be using the older format, rather than the newer format.

@Centril

This comment has been minimized.

Show comment
Hide comment
@Centril

Centril Dec 21, 2017

Contributor

@mark-i-m Please continue discussion here regarding associated constants.

Contributor

Centril commented Dec 21, 2017

@mark-i-m Please continue discussion here regarding associated constants.

@vityafx

This comment has been minimized.

Show comment
Hide comment
@vityafx

vityafx Dec 22, 2017

@mark-i-m Heh, did not think the format could change somehow. Should I rewrite it using new template?

vityafx commented Dec 22, 2017

@mark-i-m Heh, did not think the format could change somehow. Should I rewrite it using new template?

@mark-i-m

This comment has been minimized.

Show comment
Hide comment
@mark-i-m

mark-i-m Dec 22, 2017

Contributor

@vityafx TBH, I don't know. I guess it would be nice if it's not too much trouble.

Contributor

mark-i-m commented Dec 22, 2017

@vityafx TBH, I don't know. I guess it would be nice if it's not too much trouble.

@scottmcm

This comment has been minimized.

Show comment
Hide comment
@scottmcm

scottmcm Dec 23, 2017

Member

Can you clarify what the expected semantics of Limits are? Is it ∀x:T, T::MIN <= x && x <= T::MAX? If so, should it be Limits<T> : PartialOrd<T>?

Member

scottmcm commented Dec 23, 2017

Can you clarify what the expected semantics of Limits are? Is it ∀x:T, T::MIN <= x && x <= T::MAX? If so, should it be Limits<T> : PartialOrd<T>?

@vityafx

This comment has been minimized.

Show comment
Hide comment
@vityafx

vityafx Dec 23, 2017

@scottmcm Awesome question. But it needs a small decomposition:

  1. Are all of possible implementors of Limit trait must have different values for MIN and MAX ?
  2. If so, should the type be comparable?
  3. If so, should it be comparable via PartialOrd trait or maybe via custom user methods?

Now it is difficult for me to answer these questions. The possible drawbacks in my opinion are:

  1. It is possible to have such types which may have equal limits (min and max). It is possible because I think it is very rough to say the opposite.
  2. Requiring the type to be comparable is something extra we ask a user to do. I'd let the user decide what he wants to do. If it were impossible to use Limits without ordering, then I'd say we have to require ordering, but since it is not, I doubt we should.
  3. Even then, it is a good practice to use PartialOrd and Ord and anything else. But what if the user
    decides to write his own ordering trait and methods - we will require a useless trait implementation, which probably, will be impossible to write (if the user ordering methods need some extra parameters).

What do others think?

vityafx commented Dec 23, 2017

@scottmcm Awesome question. But it needs a small decomposition:

  1. Are all of possible implementors of Limit trait must have different values for MIN and MAX ?
  2. If so, should the type be comparable?
  3. If so, should it be comparable via PartialOrd trait or maybe via custom user methods?

Now it is difficult for me to answer these questions. The possible drawbacks in my opinion are:

  1. It is possible to have such types which may have equal limits (min and max). It is possible because I think it is very rough to say the opposite.
  2. Requiring the type to be comparable is something extra we ask a user to do. I'd let the user decide what he wants to do. If it were impossible to use Limits without ordering, then I'd say we have to require ordering, but since it is not, I doubt we should.
  3. Even then, it is a good practice to use PartialOrd and Ord and anything else. But what if the user
    decides to write his own ordering trait and methods - we will require a useless trait implementation, which probably, will be impossible to write (if the user ordering methods need some extra parameters).

What do others think?

@xfix

This comment has been minimized.

Show comment
Hide comment
@xfix

xfix Dec 23, 2017

Contributor

This feature exists in Haskell as Bounded type class with minBound and maxBound functions. The documentation also says it's intentionally not Ord with a following explanation.

Ord is not a superclass of Bounded since types that are not totally ordered may also have upper and lower bounds.

Contributor

xfix commented Dec 23, 2017

This feature exists in Haskell as Bounded type class with minBound and maxBound functions. The documentation also says it's intentionally not Ord with a following explanation.

Ord is not a superclass of Bounded since types that are not totally ordered may also have upper and lower bounds.

@Centril

This comment has been minimized.

Show comment
Hide comment
@Centril

Centril Dec 23, 2017

Contributor

∀T : PartialOrd<T> + Limits<T>. ∀x : T. T::MIN ≤ x ∧ x ≤ T::MAX does not hold since x may be unrelated to either T::MIN or T::MAX. Therefore, Limits<T> : PartialOrd<T> would have to be Limits : Ord, but then you would loose expressive power as @xfix shows.


Proof by counterexample:

fn main() {
    fn proof_partial_ord<T: PartialOrd>() {}
    proof_partial_ord::<f64>();

    assert_eq!(std::f64::MIN <= std::f64::NAN, false);
    assert_eq!(std::f64::NAN <= std::f64::MAX, false);
}
Contributor

Centril commented Dec 23, 2017

∀T : PartialOrd<T> + Limits<T>. ∀x : T. T::MIN ≤ x ∧ x ≤ T::MAX does not hold since x may be unrelated to either T::MIN or T::MAX. Therefore, Limits<T> : PartialOrd<T> would have to be Limits : Ord, but then you would loose expressive power as @xfix shows.


Proof by counterexample:

fn main() {
    fn proof_partial_ord<T: PartialOrd>() {}
    proof_partial_ord::<f64>();

    assert_eq!(std::f64::MIN <= std::f64::NAN, false);
    assert_eq!(std::f64::NAN <= std::f64::MAX, false);
}
@xfix

This comment has been minimized.

Show comment
Hide comment
@xfix

xfix Dec 24, 2017

Contributor

Haskell doesn't define Bounded for floating numbers either. It defines it for what looks like:

  • Machine-size integers
  • Enumerations (like boolean or Ordering)
  • SIMD vector types
  • Raw pointers
  • Tuples of bounded types
  • Monoids (not to be confused with monads) and semigroups of bounded types
Contributor

xfix commented Dec 24, 2017

Haskell doesn't define Bounded for floating numbers either. It defines it for what looks like:

  • Machine-size integers
  • Enumerations (like boolean or Ordering)
  • SIMD vector types
  • Raw pointers
  • Tuples of bounded types
  • Monoids (not to be confused with monads) and semigroups of bounded types
@vityafx

This comment has been minimized.

Show comment
Hide comment
@vityafx

vityafx Dec 26, 2017

So, we end the ordering question with not requiring it, am I right?

vityafx commented Dec 26, 2017

So, we end the ordering question with not requiring it, am I right?

@mark-i-m

This comment has been minimized.

Show comment
Hide comment
@mark-i-m

mark-i-m Dec 26, 2017

Contributor

Or by getting everyone to stop using floating point...

Contributor

mark-i-m commented Dec 26, 2017

Or by getting everyone to stop using floating point...

@Centril

This comment has been minimized.

Show comment
Hide comment
@Centril

Centril Dec 26, 2017

Contributor

@mark-i-m In any case PartialOrd is not enough if you add such invariants.

Contributor

Centril commented Dec 26, 2017

@mark-i-m In any case PartialOrd is not enough if you add such invariants.

@scottmcm

This comment has been minimized.

Show comment
Hide comment
@scottmcm

scottmcm Dec 26, 2017

Member

So, we end the ordering question with not requiring it, am I right?

I agree about not requiring Ord, but FWIW, wiki's definition of bounds is only over a partially-ordered set, so I still like PartialOrd.

Regardless whether there's a trait bound or not, the "what's the expected behaviour?" question still needs to be answered. Perhaps floating point just can't implement a general Bounded trait because of NAN, the same what that prevents it from implementing Ord, so it'll just have a f32::MIN that's not part of Bounded trait the same way it has a f32::min() that's not part of the Ord trait.

Edit: for clarity after an IRC discussion, I'm not proposing adding these properties to PartialOrd, but instead something like

trait Bounded<T = Self> : PartialOrd<T> {
    /// ∀x : Self, Self::MIN <= x
    const MIN: T;
    /// ∀x : Self, Self::MAX >= x
    const MAX: T;
}

Looking at other languages, C++ provides std::numeric_limits

Reminder just how many things are on numeric_limits: http://en.cppreference.com/w/cpp/types/numeric_limits#Member_constants. It argues more for separate Integer and Float traits, as it has specific things like is_signed and max_exponent.

Member

scottmcm commented Dec 26, 2017

So, we end the ordering question with not requiring it, am I right?

I agree about not requiring Ord, but FWIW, wiki's definition of bounds is only over a partially-ordered set, so I still like PartialOrd.

Regardless whether there's a trait bound or not, the "what's the expected behaviour?" question still needs to be answered. Perhaps floating point just can't implement a general Bounded trait because of NAN, the same what that prevents it from implementing Ord, so it'll just have a f32::MIN that's not part of Bounded trait the same way it has a f32::min() that's not part of the Ord trait.

Edit: for clarity after an IRC discussion, I'm not proposing adding these properties to PartialOrd, but instead something like

trait Bounded<T = Self> : PartialOrd<T> {
    /// ∀x : Self, Self::MIN <= x
    const MIN: T;
    /// ∀x : Self, Self::MAX >= x
    const MAX: T;
}

Looking at other languages, C++ provides std::numeric_limits

Reminder just how many things are on numeric_limits: http://en.cppreference.com/w/cpp/types/numeric_limits#Member_constants. It argues more for separate Integer and Float traits, as it has specific things like is_signed and max_exponent.

@vityafx

This comment has been minimized.

Show comment
Hide comment
@vityafx

vityafx Jan 20, 2018

Okay, we stopped on requiring PartialOrd. I'll add this. But there is also a question which was not answered: should we used associated constants or trait methods? If methods, should they have a Self context? I can't exactly say what is better. For example:

  1. Using trait methods are more subtle than just associated constants. Associated constants are constants, but min() can produce values based on some context (not only self but anything from the global namespace).
  2. Constants can not be initialized via anything that is not known at compile time. So, you can not have associated constant which is a result of some function. This is a restriction. By accepting the associated constants we must fully agree that this restriction does not harm the usability.

vityafx commented Jan 20, 2018

Okay, we stopped on requiring PartialOrd. I'll add this. But there is also a question which was not answered: should we used associated constants or trait methods? If methods, should they have a Self context? I can't exactly say what is better. For example:

  1. Using trait methods are more subtle than just associated constants. Associated constants are constants, but min() can produce values based on some context (not only self but anything from the global namespace).
  2. Constants can not be initialized via anything that is not known at compile time. So, you can not have associated constant which is a result of some function. This is a restriction. By accepting the associated constants we must fully agree that this restriction does not harm the usability.
@vityafx

This comment has been minimized.

Show comment
Hide comment
@vityafx

vityafx Jan 20, 2018

@scottmcm

Perhaps floating point just can't implement a general Bounded trait because of NAN, the same what that prevents it from implementing Ord, so it'll just have a f32::MIN that's not part of Bounded trait the same way it has a f32::min() that's not part of the Ord trait.

I'd do the same. I guess we can do nothing better than that.

vityafx commented Jan 20, 2018

@scottmcm

Perhaps floating point just can't implement a general Bounded trait because of NAN, the same what that prevents it from implementing Ord, so it'll just have a f32::MIN that's not part of Bounded trait the same way it has a f32::min() that's not part of the Ord trait.

I'd do the same. I guess we can do nothing better than that.

vityafx added some commits Jan 20, 2018

Merge pull request #1 from vityafx/add-limits-trait-v2
Update 0000-add-limits-trait.md
@burdges

This comment has been minimized.

Show comment
Hide comment
@burdges

burdges Jan 20, 2018

I'd kinda imagine this'll go like const type parameters, meaning the deeper theory needs to be explored first, like ticki did with const-dependent types, to demonstrate that some limited feature set is both useful and has agreement in the wider programming language community.

In that vein, I'd suggest someone read up on, or even survey, related features like refinement types in languages designed for formal verification of production software, like F* (paper) and ProVerif. I donno if all their theory makes sense in a language with mutability, etc. but maybe enough does.

As for floats, I do not buy @Centril argument that Bounded : PartialOrd requires Bounded : Ord. Yes, if T: Bounded<f64> means T::MIN <= x && x < T::MAX, then std::f64::NAN is not in T, which sounds fine formally but likely creates implementation headaches. In fact, f64 is already both PartialOrd and naively bounded by std::f64::INFINITY and std::f64::NEG_INFINITY, except for std::f64::NAN. It manages this by relaxing the reflexivity requirement for a mathematical partial order, which creates issues mathematically but that's floats. If you similarly define T: Bounded<f64> to mean !(x < T::MIN) && !(x >= T::MAX) then everything works for std::f64::NAN, you do not require Ord, and std::f64::NAN is always present. You break std::f64::NEG_INFINITY with <, but adding more corner cases can fix it I guess.

Anyways, I think examining F*, ProVerif, etc. will provide a far better answer to the floats question than Haskell, like maybe nobody even included floats when doing formal verification, or maybe you need refinement types than can clearly express all the allowed special float values.

burdges commented Jan 20, 2018

I'd kinda imagine this'll go like const type parameters, meaning the deeper theory needs to be explored first, like ticki did with const-dependent types, to demonstrate that some limited feature set is both useful and has agreement in the wider programming language community.

In that vein, I'd suggest someone read up on, or even survey, related features like refinement types in languages designed for formal verification of production software, like F* (paper) and ProVerif. I donno if all their theory makes sense in a language with mutability, etc. but maybe enough does.

As for floats, I do not buy @Centril argument that Bounded : PartialOrd requires Bounded : Ord. Yes, if T: Bounded<f64> means T::MIN <= x && x < T::MAX, then std::f64::NAN is not in T, which sounds fine formally but likely creates implementation headaches. In fact, f64 is already both PartialOrd and naively bounded by std::f64::INFINITY and std::f64::NEG_INFINITY, except for std::f64::NAN. It manages this by relaxing the reflexivity requirement for a mathematical partial order, which creates issues mathematically but that's floats. If you similarly define T: Bounded<f64> to mean !(x < T::MIN) && !(x >= T::MAX) then everything works for std::f64::NAN, you do not require Ord, and std::f64::NAN is always present. You break std::f64::NEG_INFINITY with <, but adding more corner cases can fix it I guess.

Anyways, I think examining F*, ProVerif, etc. will provide a far better answer to the floats question than Haskell, like maybe nobody even included floats when doing formal verification, or maybe you need refinement types than can clearly express all the allowed special float values.

@brendanzab

This comment has been minimized.

Show comment
Hide comment
@brendanzab

brendanzab Jan 21, 2018

Member

Yup. If there's one thing I learned in my explorations on numeric libraries, it's that Haskell really messed up on the Haskell 98 numeric type classes. So take what they do with a big grain of salt!

Member

brendanzab commented Jan 21, 2018

Yup. If there's one thing I learned in my explorations on numeric libraries, it's that Haskell really messed up on the Haskell 98 numeric type classes. So take what they do with a big grain of salt!

@scottmcm

This comment has been minimized.

Show comment
Hide comment
@scottmcm

scottmcm Jan 22, 2018

Member

If you similarly define T: Bounded<f64> to mean !(x < T::MIN) && !(x >= T::MAX) then everything works for std::f64::NAN

Unfortunately, that loses uniqueness—that expression is also true if you set both MIN and MAX to NAN.

Member

scottmcm commented Jan 22, 2018

If you similarly define T: Bounded<f64> to mean !(x < T::MIN) && !(x >= T::MAX) then everything works for std::f64::NAN

Unfortunately, that loses uniqueness—that expression is also true if you set both MIN and MAX to NAN.

@mark-i-m

This comment has been minimized.

Show comment
Hide comment
@mark-i-m

mark-i-m Jan 22, 2018

Contributor

@scottmcm This is technically not true since NaN != NaN...

Contributor

mark-i-m commented Jan 22, 2018

@scottmcm This is technically not true since NaN != NaN...

@scottmcm

This comment has been minimized.

Show comment
Hide comment
@scottmcm

scottmcm Jan 22, 2018

Member

@mark-i-m But the definition didn't mention anything about != 😁

Member

scottmcm commented Jan 22, 2018

@mark-i-m But the definition didn't mention anything about != 😁

@mark-i-m

This comment has been minimized.

Show comment
Hide comment
@mark-i-m

mark-i-m Jan 22, 2018

Contributor

@scottmcm what I mean is that you can have MAX = NaN and MIN = NaN and still have uniqueness because MAX != MIN. This strikes me as absolutely ridiculous, but it is technically true :P

Contributor

mark-i-m commented Jan 22, 2018

@scottmcm what I mean is that you can have MAX = NaN and MIN = NaN and still have uniqueness because MAX != MIN. This strikes me as absolutely ridiculous, but it is technically true :P

@scottmcm

This comment has been minimized.

Show comment
Hide comment
@scottmcm

scottmcm Jan 22, 2018

Member

@mark-i-m Ah, sorry I misunderstood. I meant non-uniqueness in the sense that NAN and -INF are both valid values to satisfy the limits on MIN under that definition, so I consider it insufficient.

Member

scottmcm commented Jan 22, 2018

@mark-i-m Ah, sorry I misunderstood. I meant non-uniqueness in the sense that NAN and -INF are both valid values to satisfy the limits on MIN under that definition, so I consider it insufficient.

@burdges burdges referenced this pull request Jan 25, 2018

Closed

Guard Clause Flow Typing #2221

@fstirlitz

This comment has been minimized.

Show comment
Hide comment
@fstirlitz

fstirlitz Jan 28, 2018

Yes, minimal and maximal elements aren't unique in a general partial order, but floating-point types are just one example of that. (Ignoring here that floating-point values aren't technically partially ordered because ¬(NaN ≤ NaN).) The floating-point types simply have no greatest and smallest value, but both NaN and +∞ are maximal values, and both NaN and −∞ are minimal values.

How about creating newtypes which would be able to contain any floating-point value except NaN? I am preparing an RFC proposing a feature like that (I'm not sure when I'll be able to submit it, though). For such a type, implementing a type limit trait would be straightforward.

I'd also suggest splitting limits into two traits. Some orders may have one limit, but not the other. Consider the prefix order; in Rust terms, an ordering for &'_ [T] where a <= b means b.starts_with(a). This partial order has a smallest element &[] (and even a greatest-lower-bound operation, i.e. the longest common prefix), but no greatest element.

fstirlitz commented Jan 28, 2018

Yes, minimal and maximal elements aren't unique in a general partial order, but floating-point types are just one example of that. (Ignoring here that floating-point values aren't technically partially ordered because ¬(NaN ≤ NaN).) The floating-point types simply have no greatest and smallest value, but both NaN and +∞ are maximal values, and both NaN and −∞ are minimal values.

How about creating newtypes which would be able to contain any floating-point value except NaN? I am preparing an RFC proposing a feature like that (I'm not sure when I'll be able to submit it, though). For such a type, implementing a type limit trait would be straightforward.

I'd also suggest splitting limits into two traits. Some orders may have one limit, but not the other. Consider the prefix order; in Rust terms, an ordering for &'_ [T] where a <= b means b.starts_with(a). This partial order has a smallest element &[] (and even a greatest-lower-bound operation, i.e. the longest common prefix), but no greatest element.

@vityafx

This comment has been minimized.

Show comment
Hide comment
@vityafx

vityafx Feb 12, 2018

A question for those who knows the rfc process better than me: is it really bad if it will be a breaking change? I mean, if we move those implementation to a trait and remove them from the types implementations, we will require the user code to add use Limits; line as we are using it.

Another way would be to deprecate using type's static methods.

vityafx commented Feb 12, 2018

A question for those who knows the rfc process better than me: is it really bad if it will be a breaking change? I mean, if we move those implementation to a trait and remove them from the types implementations, we will require the user code to add use Limits; line as we are using it.

Another way would be to deprecate using type's static methods.

@vityafx

This comment has been minimized.

Show comment
Hide comment
@vityafx

vityafx Feb 12, 2018

One more question: I'd agree with using associated constants if it were able to use methods/functions for their initialization. Now it is impossible and even const fn is unstable, so I'd suggest to use trait methods then.

vityafx commented Feb 12, 2018

One more question: I'd agree with using associated constants if it were able to use methods/functions for their initialization. Now it is impossible and even const fn is unstable, so I'd suggest to use trait methods then.

@Centril

This comment has been minimized.

Show comment
Hide comment
@Centril

Centril Feb 12, 2018

Contributor

@vityafx

is it really bad if it will be a breaking change?

Yes, but it depends on how extensive the breakage is, and that would have to be measured with a crater run... If the breakage isn't too extensive, then it can be done with an epoch.

I mean, if we move those implementation to a trait and remove them from the types implementations, we will require the user code to add use Limits; line as we are using it.

Not necessarily. If Limits is in the prelude, then you will not have to use Limits; (unless a user has done use Limits; for a trait from some other crate that does not do the same thing, but that is probably unlikely). However, there is a higher bar for adding things to the prelude.

Another way would be to deprecate using type's static methods.

That is probably a good idea.

Contributor

Centril commented Feb 12, 2018

@vityafx

is it really bad if it will be a breaking change?

Yes, but it depends on how extensive the breakage is, and that would have to be measured with a crater run... If the breakage isn't too extensive, then it can be done with an epoch.

I mean, if we move those implementation to a trait and remove them from the types implementations, we will require the user code to add use Limits; line as we are using it.

Not necessarily. If Limits is in the prelude, then you will not have to use Limits; (unless a user has done use Limits; for a trait from some other crate that does not do the same thing, but that is probably unlikely). However, there is a higher bar for adding things to the prelude.

Another way would be to deprecate using type's static methods.

That is probably a good idea.

@vityafx

This comment has been minimized.

Show comment
Hide comment
@vityafx

vityafx Feb 12, 2018

@Centril so what if we add it to a prelude and deprecate using of static methods? Sounds like the best option at this moment to me.

vityafx commented Feb 12, 2018

@Centril so what if we add it to a prelude and deprecate using of static methods? Sounds like the best option at this moment to me.

@Centril

This comment has been minimized.

Show comment
Hide comment
@Centril

Centril Feb 12, 2018

Contributor

@vityafx

One more question: I'd agree with using associated constants if it were able to use methods/functions for their initialization. Now it is impossible and even const fn is unstable, so I'd suggest to use trait methods then.

I agree. You can get back the constness of the methods in Limits with const impl as discussed in RFC #2237 or with some other const polymorphism / effect system proposal. So I think that is the more flexible proposal.

@Centril so what if we add it to a prelude and deprecate using of static methods?

So I'd recommend taking a look at the other things that are in the prelude. In particular, the documentation states:

It's kept as small as possible, and is focused on things, particularly traits, which are used in almost every single Rust program.

Does Limits fall under that category? I'm skeptical.

Contributor

Centril commented Feb 12, 2018

@vityafx

One more question: I'd agree with using associated constants if it were able to use methods/functions for their initialization. Now it is impossible and even const fn is unstable, so I'd suggest to use trait methods then.

I agree. You can get back the constness of the methods in Limits with const impl as discussed in RFC #2237 or with some other const polymorphism / effect system proposal. So I think that is the more flexible proposal.

@Centril so what if we add it to a prelude and deprecate using of static methods?

So I'd recommend taking a look at the other things that are in the prelude. In particular, the documentation states:

It's kept as small as possible, and is focused on things, particularly traits, which are used in almost every single Rust program.

Does Limits fall under that category? I'm skeptical.

@vityafx

This comment has been minimized.

Show comment
Hide comment
@vityafx

vityafx Feb 12, 2018

@Centril

Does Limits fall under that category? I'm skeptical.

Well, I agree.

I agree. You can get back the constness of the methods in Limits with const impl as discussed in RFC #2237 or with some other const polymorphism / effect system proposal. So I think that is the more flexible proposal.

Should I wait for the #2237 being merged before continuing?

vityafx commented Feb 12, 2018

@Centril

Does Limits fall under that category? I'm skeptical.

Well, I agree.

I agree. You can get back the constness of the methods in Limits with const impl as discussed in RFC #2237 or with some other const polymorphism / effect system proposal. So I think that is the more flexible proposal.

Should I wait for the #2237 being merged before continuing?

@scottmcm

This comment has been minimized.

Show comment
Hide comment
@scottmcm

scottmcm Feb 12, 2018

Member

MIRI is coming soon, so I think sticking to consts is better unless there's a persuasive situation in which a limit really can only be computed at runtime and the bound is important.

I mean, if we move those implementation to a trait and remove them from the types implementations, we will require the user code to add use Limits; line as we are using it.

There's no requirement to remove them from the types' implementations just because the trait exists. They shouldn't do different things, because that would be confusing, but the inherent method will take priority in resolution, so there's no need for a breaking change discussion here.

And there's no reason to remove a method from, say, f32 just because it has the same name as a trait method. There's still f32::min as an inherent method (on a type that's not Ord) even though there's also Ord::min as a trait method. The f32 has its definition for the cases that make it not Ord.

Member

scottmcm commented Feb 12, 2018

MIRI is coming soon, so I think sticking to consts is better unless there's a persuasive situation in which a limit really can only be computed at runtime and the bound is important.

I mean, if we move those implementation to a trait and remove them from the types implementations, we will require the user code to add use Limits; line as we are using it.

There's no requirement to remove them from the types' implementations just because the trait exists. They shouldn't do different things, because that would be confusing, but the inherent method will take priority in resolution, so there's no need for a breaking change discussion here.

And there's no reason to remove a method from, say, f32 just because it has the same name as a trait method. There's still f32::min as an inherent method (on a type that's not Ord) even though there's also Ord::min as a trait method. The f32 has its definition for the cases that make it not Ord.

@vityafx

This comment has been minimized.

Show comment
Hide comment
@vityafx

vityafx Feb 12, 2018

@scottmcm what is "MIRI"? :)

vityafx commented Feb 12, 2018

@scottmcm what is "MIRI"? :)

@Centril

This comment has been minimized.

Show comment
Hide comment
@Centril

Centril Feb 12, 2018

Contributor

@vityafx

Should I wait for the #2237 being merged before continuing?

I don't think that is necessary, and #2237 will probably not be merged in its current form, I'll have to rework it.. Whether or not Limits should use associated constants depends on whether you think there's any scenario where you'd want both the bound and a runtime computed bound. If there is, we should go with methods, because they can be made opt-in const after the fact.

@scottmcm what is "MIRI"? :)

https://github.com/solson/miri

Contributor

Centril commented Feb 12, 2018

@vityafx

Should I wait for the #2237 being merged before continuing?

I don't think that is necessary, and #2237 will probably not be merged in its current form, I'll have to rework it.. Whether or not Limits should use associated constants depends on whether you think there's any scenario where you'd want both the bound and a runtime computed bound. If there is, we should go with methods, because they can be made opt-in const after the fact.

@scottmcm what is "MIRI"? :)

https://github.com/solson/miri

@vityafx

This comment has been minimized.

Show comment
Hide comment
@vityafx

vityafx Feb 12, 2018

@Centril

Whether or not Limits should use associated constants depends on whether you think there's any scenario where you'd want both the bound and a runtime computed bound. If there is, we should go with methods, because they can be made opt-in const after the fact.

Totally agree with this and I told this a month ago here. So, I can't guarantee that someone would want the value to be evaluated at runtime. So I'd use the most generic solution which can be tuned to be computed both at compile time and runtime.

https://github.com/solson/miri

Could you tell how this can help? Don't quite understand..

vityafx commented Feb 12, 2018

@Centril

Whether or not Limits should use associated constants depends on whether you think there's any scenario where you'd want both the bound and a runtime computed bound. If there is, we should go with methods, because they can be made opt-in const after the fact.

Totally agree with this and I told this a month ago here. So, I can't guarantee that someone would want the value to be evaluated at runtime. So I'd use the most generic solution which can be tuned to be computed both at compile time and runtime.

https://github.com/solson/miri

Could you tell how this can help? Don't quite understand..

@oli-obk

This comment has been minimized.

Show comment
Hide comment
@oli-obk

oli-obk Feb 12, 2018

Contributor

So I'd use the most generic solution which can be tuned to be computed both at compile time and runtime.

Can you give an example with runtime limits, so we have something concrete to discuss?

Could you tell how this can help? Don't quite understand..

I think all that was meant by the miri-comment is that miri allows you to compute many things that were previously out of scope of const eval and needed to be done at runtime. It doesn't help for inherently runtime things.

Contributor

oli-obk commented Feb 12, 2018

So I'd use the most generic solution which can be tuned to be computed both at compile time and runtime.

Can you give an example with runtime limits, so we have something concrete to discuss?

Could you tell how this can help? Don't quite understand..

I think all that was meant by the miri-comment is that miri allows you to compute many things that were previously out of scope of const eval and needed to be done at runtime. It doesn't help for inherently runtime things.

@vityafx

This comment has been minimized.

Show comment
Hide comment
@vityafx

vityafx Feb 12, 2018

@oli-obk

Can you give an example with runtime limits, so we have something concrete to discuss?

The thing is I can't be responsible for each user. I am not a god. Even if I don't provide an example, I can't guarantee there is no case for that. Could you?

vityafx commented Feb 12, 2018

@oli-obk

Can you give an example with runtime limits, so we have something concrete to discuss?

The thing is I can't be responsible for each user. I am not a god. Even if I don't provide an example, I can't guarantee there is no case for that. Could you?

@eddyb

This comment has been minimized.

Show comment
Hide comment
@eddyb

eddyb Feb 12, 2018

Member

I think the value of putting limits in types diminishes greatly when they're not const. That is, you should use your own abstraction if you have such usecases, and not limit (heh) everyone else with it.

Member

eddyb commented Feb 12, 2018

I think the value of putting limits in types diminishes greatly when they're not const. That is, you should use your own abstraction if you have such usecases, and not limit (heh) everyone else with it.

@mark-i-m

This comment has been minimized.

Show comment
Hide comment
@mark-i-m

mark-i-m Feb 12, 2018

Contributor

what is "MIRI"? :)

It's the new const evaluation system in rustc. It's been under construction for a while, but it's now making it into nightly: rust-lang/rust#46882

Contributor

mark-i-m commented Feb 12, 2018

what is "MIRI"? :)

It's the new const evaluation system in rustc. It's been under construction for a while, but it's now making it into nightly: rust-lang/rust#46882

Why should we *not* do this?
# Rationale and alternatives
[alternatives]: #alternatives

This comment has been minimized.

@oli-obk

oli-obk Feb 12, 2018

Contributor

This RFC could live entirely in a crate, could it not? Or is there a use case where libstd could make use of it?

@oli-obk

oli-obk Feb 12, 2018

Contributor

This RFC could live entirely in a crate, could it not? Or is there a use case where libstd could make use of it?

This comment has been minimized.

@vityafx

vityafx Feb 12, 2018

I wanted to get rid of u64::max_value() methods and for other types, because it will duplicate the functionality. Getting rid of these types and making limits be a separate crate is a breaking change. Also, it is not clear for me, if it is a separate crate, who owns it.

@vityafx

vityafx Feb 12, 2018

I wanted to get rid of u64::max_value() methods and for other types, because it will duplicate the functionality. Getting rid of these types and making limits be a separate crate is a breaking change. Also, it is not clear for me, if it is a separate crate, who owns it.

This comment has been minimized.

@vityafx

vityafx Feb 12, 2018

Also, I have not finished the proposal yet, because there are questions still.

@vityafx

vityafx Feb 12, 2018

Also, I have not finished the proposal yet, because there are questions still.

This comment has been minimized.

@mark-i-m

mark-i-m Feb 12, 2018

Contributor

@vityafx We don't have to settle all questions now. Most RFCs do have an "unresolved questions" section for questions that should be decided before stabilization but after we have some experience using a feature.

@mark-i-m

mark-i-m Feb 12, 2018

Contributor

@vityafx We don't have to settle all questions now. Most RFCs do have an "unresolved questions" section for questions that should be decided before stabilization but after we have some experience using a feature.

This comment has been minimized.

@vityafx

vityafx Feb 13, 2018

So I may go with my own vision and just put all the unresolved questions in the appropriate section of the rfc?

@vityafx

vityafx Feb 13, 2018

So I may go with my own vision and just put all the unresolved questions in the appropriate section of the rfc?

This comment has been minimized.

@mark-i-m

mark-i-m Feb 13, 2018

Contributor

Well, generally we want some kind of consensus about what we want to try, and part of that is reaching consensus about what questions we think can be resolved later... A large part of that is the OP's (your) original vision, but most RFCs do take a few edits and changes along the way :)

@mark-i-m

mark-i-m Feb 13, 2018

Contributor

Well, generally we want some kind of consensus about what we want to try, and part of that is reaching consensus about what questions we think can be resolved later... A large part of that is the OP's (your) original vision, but most RFCs do take a few edits and changes along the way :)

@Centril

This comment has been minimized.

Show comment
Hide comment
@Centril

Centril Feb 12, 2018

Contributor

@eddyb I have to say that it is hard for me to imagine a practical scenario with non-const Limits... but with #2237 or a similar proposal we have our cake and eat it too, so no one gets limited?

Contributor

Centril commented Feb 12, 2018

@eddyb I have to say that it is hard for me to imagine a practical scenario with non-const Limits... but with #2237 or a similar proposal we have our cake and eat it too, so no one gets limited?

@scottmcm

This comment has been minimized.

Show comment
Hide comment
@scottmcm

scottmcm Feb 12, 2018

Member

we have our cake and eat it too

There is of course a small ergonomic cost to having both, since the const uses are now impl const Limits and where T: Const Limits and T::max_value() (instead of T::MAX).

Member

scottmcm commented Feb 12, 2018

we have our cake and eat it too

There is of course a small ergonomic cost to having both, since the const uses are now impl const Limits and where T: Const Limits and T::max_value() (instead of T::MAX).

@Centril

This comment has been minimized.

Show comment
Hide comment
@Centril

Centril Feb 12, 2018

Contributor

There is of course a small ergonomic cost to having both

Sure; On scale I think I can personally live with that small cost =)

Contributor

Centril commented Feb 12, 2018

There is of course a small ergonomic cost to having both

Sure; On scale I think I can personally live with that small cost =)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment