September 05, 2002 Re: proposal regarding intent | ||||
---|---|---|---|---|
| ||||
Posted in reply to Mac Reiter | "Mac Reiter" <Mac_member@pathlink.com> wrote in message news:al7veu$ls1$1@digitaldaemon.com... > In article <al75nt$26bl$1@digitaldaemon.com>, Sean L. Palmer says... > >byte b=8, c=9; > >byte x = b + c; // stays in byte or larger in intermediate form > >int y = b + c; // intermediates done in int since result is int, and int is > >more precise than byte > > byte d=150, e=230, f=2; > byte x=(b+c)/f; // hosed. Your rule requires the intermediates to be bytes, > // since nothing but bytes are used in this calculation. > // Bytes are simply not big enough to do math with bytes. I confessed I thought of some of this as I was typing. What I tried to say at least by the end was that it shouldn't require any *more* precision than the smallest involved type. And in fact I think the above has that in a comment: "// stays in byte **or larger** in intermediate form" If you're worried about it, you can have the result or one of the inputs or any temporary part of it cast explicitly to a larger type. Or use "precise" goal directives around it. You have the exact same problem with integers and nobody seems to complain about that. I've personally hit the integer problem a bunch especially when doing fixed point arithmetic. What if all your byte values are only using maybe 7 of the bits? Then there's no need for a larger type for the intermediates. > And as for speed, it doesn't matter how much faster you can make it if the answer is wrong. NOPs run really fast, and they give you an answer that is no > more wrong than the above code would... If code would be illegal to generate in the SIMD unit due to precision issues even though what you're trying to do is force the compiler to do the calculation in the SIMD unit, it can be pretty frustrating. So making the rules too restrictive can cause problems too. The answer would only be wrong since you decided to use bytes (a very imprecise type) to hold your values. If you're worried about precision, use a more precise type, or add a cast, it's not that hard. Sean > I *do* like your ideas about standardizing ways to tell the compiler about your > intent. I would prefer that certain of these "hints" actually be required -- > boundschecked should require boundschecking, rather than just suggesting that > "it might be a nice idea to check those arrays, if the compiler isn't too busy...". I generally hate hints -- if I took the time to tell the compiler to > do something, it should either do it or throw an error if it is physically impossible to do it (attempts to inline recursive functions, for example). > > I agree that it is sometimes preferable to avoid the intermediate casts for > performance reasons. I just think that the vast majority of code expects correct answers first, with optimization second. One thing I can think of to > give the programmer the ability to turn off conversions would be something that > looked like a cast, along the lines of: (I'm sure the exact syntax would have to > be different) > > byte x = expr(byte)(b+c) / f; > > or, if you get really explicit: > > byte x = expr(byte)( expr(byte)(b+c) / f); > > although the outer expr() is not really needed, since the result immediately > goes into a byte. > > expr() is kinda like cast(), except that cast() would evaluate the expression in > whatever types it wanted and then cast it afterwards. expr() would tell the > compiler what type to use for evaluating the expression, and that the programmer > knows the cost and consequences and wants it to behave that way. > > That way, when the average programmer writes out a math formula, it generates > the right answer (as long as the machine is physically capable of doing so -- I > do *not* advocate implicitly doing 'long' math with software emulated bignum > routines for the intermediate expressions). But when the advanced programmer > really needs to tweak an expression down and either knows that the result will > fit or explicitly wants the wrapping/truncation behavior, he/she can use expr() > to force the behavior. > > expr() is probably a really bad name. It keeps bringing up lisp and scheme > memories... Unfortunately, it is the best thing I could think of on spur of the > moment. > > Mac > > |
September 05, 2002 Re: proposal regarding intent | ||||
---|---|---|---|---|
| ||||
Posted in reply to Sean L. Palmer | In article <al77jb$2anj$1@digitaldaemon.com>, Sean L. Palmer says... >Which leads me to comment about the contracts currently in D. I don't see how it's semantically much different to write: > >in >{ > assert(A); >} >out >{ > assert(B); >} >body >{ > C; >} > >than it is to write: > >{ > assert(A); > C; > assert(B); >} The following discussion is going to talk about classes, because it talks about inheritance and you don't directly inherit functions... It applies to functions because you can override member functions in derived classes. In theory, by separating the contracts out, the compiler can provide contract inheritance. I don't think this has been done yet, but it is a benefit over the inline asserts. Design By Contract was originally created as part of Eiffel, where contract inheritance does work. When you inherit from a class in Eiffel, you don't just get its interfaces and members, you pick up all of its contractual obligations. This allows any user of a class to also use subclasses, knowing that they are required by the compiler to fulfill all contractual requirements of the original class. Contract inheritance is not as painful as I originally thought it would be. The actual implementation is not especially efficient, and it frequently gets turned off in Release builds, but it does at least work. The idea is that when you derive from a class, you have to accept ANY input that would have been legal to the base class. However, if your new class is more capable, it may be able to accept inputs that used to be invalid. The new class writes a more lenient contract. The compiler combines the contracts by putting OR between the inheritance levels. That means that the derived class will accept anything that was legal to the base OR anything that is legal to itself. When you derive from a class, you also promise not to surprise any customers of that base class. That means that any results must satisfy the original base class contract. If your new class is more precise, or more complete, it may be able to promise more than the base class, so it would have a new out contract. The compiler combines the contracts by putting AND between the inheritance levels. That means that the results will satisfy all of the base class requirements AND all of the derived class requirements. Presumably, D will eventually support contract inheritance, and you really need to know which bits are contract and which bits are just asserts. I agree that I would prefer having the out contract after the body, but the placement is part of the "contract" idea. In a contract, you don't care as much about how the work gets done (the body) as you do about what you have to supply (the in) and what the contractor guarantees to give you (the out). The contract is the user's view of the function. You get the return type, the parameter list, and then the description of what you have to do and what the function will do for you. I also would prefer to simply have expressions in the contracts. The presence of the assert() just seems to clutter the code, and nothing but asserts should be in the contracts anyway, normally. I guess it might be necessary sometimes to run a small piece of code to generate a test result, and then have an assert on that result. I dunno. I haven't really used contracts enough to have a firm grasp on what is really necessary. Mac |
September 07, 2002 Re: proposal regarding intent | ||||
---|---|---|---|---|
| ||||
Posted in reply to Sean L. Palmer | "Sean L. Palmer" <seanpalmer@earthlink.net> wrote in message news:al77jb$2anj$1@digitaldaemon.com... > Which leads me to comment about the contracts currently in D. I don't see how it's semantically much different to write: > > in > { > assert(A); > } > out > { > assert(B); > } > body > { > C; > } > > than it is to write: > > { > assert(A); > C; > assert(B); > } Good question. The differences are: 1) Aesthetic - the in and out sections clearly mark what are preconditions and what are postconditions. 2) Syntactic sugar - the out(result) syntax is a handy way to check the function return value. 3) in and out bodies can contain arbitrary statements, loops, etc., not just expressions. 4) Inheritance - the in and out clauses of virtual functions are inherited. No way to do that with assert's. |
September 07, 2002 Re: proposal regarding intent | ||||
---|---|---|---|---|
| ||||
Posted in reply to Mac Reiter | "Mac Reiter" <Mac_member@pathlink.com> wrote in message news:al82ip$rf2$1@digitaldaemon.com... > I also would prefer to simply have expressions in the contracts. The presence > of the assert() just seems to clutter the code, and nothing but asserts should > be in the contracts anyway, normally. I guess it might be necessary sometimes > to run a small piece of code to generate a test result, and then have an assert > on that result. I dunno. I haven't really used contracts enough to have a firm > grasp on what is really necessary. Sometimes contracts are not easilly expressible as functions. Consider a sort() function - the out contract should test that the array really is sorted. That requires a loop, which is not possible with just an assert expression. |
Copyright © 1999-2021 by the D Language Foundation