On Fri, Jun 20, 2014 at 2:07 PM, Gábor Lehel <glaebho...@gmail.com> wrote:
> > > > On Thu, Jun 19, 2014 at 9:05 AM, Jerry Morrison <jhm...@gmail.com> wrote: > >> Nice analysis! >> >> Over what scope should programmers pick between Gábor's 3 categories? >> >> The "wraparound is desired" category should only occur in narrow parts of >> code, like computing a hash. That suits a wraparound-operator better than a >> wraparound-type, and certainly better than a compiler switch. And it >> doesn't make sense for a type like 'int' that doesn't have a fixed size. >> > > I thought hash algorithms were precisely the kind of case where you might > opt for types which were clearly defined as wrapping. Why do you think > using different operators instead would be preferred? > Considering a hashing or CRC example, the code reads a bunch of non-wraparound values, mashes them together using wraparound arithmetic, then uses the result in a way that does not mean to wrap around at the integer size. It's workable to convert inputs to wraparound types and use wraparound accumulators, then convert the result to a non-wraparound type. But using wraparound operators seems simpler, more visible, and less error-prone. E.g. it'd be a mistake if the hash function returned a wraparound type, which gets assigned with type inference, and so downstream operations wrap around. > >> >> The "wraparound is undesired but performance is critical" category occurs >> in the most performance critical bits of code [I'm doubting that all parts >> of all Rust programs are performance critical], and programmers need to >> make the trade-off over that limited scope. Maybe via operators or types, >> but not by a compiler switch over whole compilation units. >> >> That leaves "wraparound is undesired and performance is not critical" >> category for everything else. The choice between checked vs. unbounded >> sounds like typing. >> >> BigUint is weird: It can underflow but not overflow. When you use its >> value in a more bounded way you'll need to bounds-check it then, whether it >> can go negative or not. Wouldn't it be easier to discard it than squeeze it >> into the wraparound or checked models? >> > > Making the unbounded integer types implement the Checking/Wrapping traits > is more for completeness than anything else, I'm not sure whether it has > practical value. > > A BigUint/Natural type is not as important as BigInt/Integer, but it can > be nice to have. Haskell only has Integer in the Prelude, but an external > package provides Natural, and there've been proposals to mainline it. It's > useful for function inputs where only nonnegative values make sense. You > could write asserts manually, but you might as well factor them out. And > types are documentation. > > The Haskell implementation of Natural is just a newtype over Integer with > added checks, and the same thing might make sense for Rust. > I see. Good points. > > On Wed, Jun 18, 2014 at 11:21 AM, Brian Anderson <bander...@mozilla.com> > wrote: > >> >> On 06/18/2014 10:08 AM, Gábor Lehel wrote: >> >>> >>> # Checked math >>> >>> For (2), the standard library offers checked math in the `CheckedAdd`, >>> `CheckedMul` etc. traits, as well as integer types of unbounded size: >>> `BigInt` and `BigUint`. This is good, but it's not enough. The acid test >>> has to be whether for non-performance-critical code, people are actually >>> *using* checked math. If they're not, then we've failed. >>> >>> `CheckedAdd` and co. are important to have for flexibility, but they're >>> far too unwieldy for general use. People aren't going to write >>> `.checked_add(2).unwrap()` when they can write `+ 2`. A more adequate >>> design might be something like this: >>> >>> * Have traits for all the arithmetic operations for both checking on >>> overflow and for wrapping around on overflow, e.g. `CheckedAdd` (as now), >>> `WrappingAdd`, `CheckedSub`, `WrappingSub`, and so on. >>> >>> * Offer convenience methods for the Checked traits which perform >>> `unwrap()` automatically. >>> >>> * Have separate sets of integer types which check for overflow and >>> which wrap around on overflow. Whatever they're called: `CheckedU8`, >>> `checked::u8`, `uc8`, ... >>> >>> * Both sets of types implement all of the Checked* and Wrapping* >>> traits. You can use explicit calls to get either behavior with either types. >>> >>> * The checked types use the Checked traits to implement the operator >>> overloads (`Add`, Mul`, etc.), while the wrapping types use the Wrapping >>> traits to implement them. In other words, the difference between the types >>> is (probably only) in the behavior of the operators. >>> >>> * `BigInt` also implements all of the Wrapping and Checked traits: >>> because overflow never happens, it can claim to do anything if it "does >>> happen". `BigUint` implements all of them except for the Wrapping traits >>> which may underflow, such as `WrappingSub`, because it has nowhere to wrap >>> around to. >>> >>> Another option would be to have just one set of types but two sets of >>> operators, like Swift does. I think that would work as well, or even >>> better, but we've been avoiding having any operators which aren't familiar >>> from C. >>> >> >> The general flavor of this proposal w/r/t checked arithmetic sounds >> pretty reasonable to me, and we can probably make progress on this now. I >> particularly think that having checked types that use operator overloading >> is important for ergonomics. >> _______________________________________________ >> Rust-dev mailing list >> Rust-dev@mozilla.org >> https://mail.mozilla.org/listinfo/rust-dev >> > > > > -- > Jerry > >> > -- Jerry
_______________________________________________ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev