Re: [rust-dev] sub-grammar for range pattern constants?
On 4/29/2013 2:00 PM, Graydon Hoare wrote: Yes. Formalizing and completing the definition of the constant expression subgrammar would be extremely valuable. It's one of the major high-risk areas remaining the the design space. -Graydon VHDL is a very different language from Rust (it's a hardware design language, not a normal programming language), but it has the nicest constant system I've worked with. The rule is basically that constant expressions can contain: Literals (i.e. 5, 0x20) Expressions that depend on constants (i.e. 0x5 + 10 * const_k) Any pure function whose arguments are constant. (i.e. 5 + f_pure(5, const_k) ) It's this last rule that is truly beautiful. You can use the same functions in both constant initialization and run-time code, with only the requirement that they are pure. Pure functions are ones whose output depends only on their arguments (and constants). Allowing functions to initialize constants avoids the whole annoyance in C/C++ where you have to use macros to make things compile-time const. It also allows a surprising amount of compile-time optimization. I don't know how realistic this is for constants in a language like rust - but it would be very elegant if it could work. Erik ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
[rust-dev] Proposal: Do block with multiple closures (was: Update on I/O progress)
In thinking about how to handle I/O errors, I came up with a system that needed a do that could take multiple closures (and pass them to multiple trailing arguments). In thinking about it, this seems incredibly useful (for non-I/O things), and consistent with how Rust currently works. I propose having do (or some related keyword) take an argument that tells it how many closures it should consume, and pass to that many trailing arguments. I'm thinking having do take a list of labels, which are repeated at the blocks, for readability. So if I don't like the if statement, I could write: do(:if, :else) if_fun(my_condition) :if { // if code } :else { // else code } Python has a really useful else block on for loops: do (:for, :else) for_else(~[1, 2,3]) :for |x| { if something(x) {break}; } :else{ // Run if for loop didn't break } Lisp has the cond statement, which is like a multi-if: do(:0, :1, :2) cond(~[true, false, false]):0 { // if zeroth element is true } :1 { // if first is true } :2 { // if second is true } This new do is basically the same as a regular do, except: Do passes more than one block to the trailing arguments of its function. Do wraps the return value in an optionX, so we can detect a break; (which returns None). The I/O proposal I had is below, if anyone is interested. It has the advantage of very clean, top-to-bottom code flow in the case where errors are being handled. It has the disadvantage of adding one level of parentheses for each I/O call. I based this on the observation - in my code - that I/O errors are both expected and handled, and generally not handled in a uniform way. You simply can't avoid I/O errors - even if you check that a file exists, it may not by the time you call open(). The same goes for network sockets, except things are even more unpredictable. Each I/O function would take two blocks - error and success. The error block can return either 1. break - which stops processing, but lets the task live; 2. io::fail, which kills the task; or 3. a good I/O handle, which is passed to the success block. do(:err, :succ) io::open(my-file):err |e| { if some-condition(e) { do(:err, :succ) io::open(some-other-file) :err |_| {io::fail} :succ |x|{x} } } :succ |f| { do(:err, :succ) io::write(f) :err {io::fail} :succ |f| { f.close(); } // close probably doesn't need to handle errors } On both error and success, code flows very nicely top-to-bottom (skipping error blocks on success). It's easy to see what the argument is to each success block - it's either the return value from the err block, or the result of the I/O operation. That said, the deep nesting makes it pretty ugly. Thanks, Erik ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Division and modulo for signed numbers
Comments inline. I'm glad to see integer division getting this level of attention. Erik On 4/25/2013 11:12 AM, Matthieu Monrocq wrote: Of course, having an infix syntax would make things easier: 5 % 3 vs 5 rem 3 vs 5.rem(3), in increasing order of typed keys (and visual noise for the latter ?). I like this a lot. Making "div" and "mod" operators instead of methods makes them proper, first-class citizens. It also makes them easy to use in math expressions, which is a big plus. It also makes them opt-in for developers expecting C-like behavior. This looks pretty good to me: let a = vec[idx mod len]; //safe if len 0 let n = (k + 5) div 10; // precedence rules require parentheses, just like "/" I really don't like the [div/mod]_[floor/ceil] methods proposed on IRC. It makes the ones I care about - [div/mod]_floor - a second class citizen. Adding "mod" and "div" as operators doesn't feel that bad to me - these are important and useful, and should exist in a way that C developers can find them. Making them operators makes them highly discoverable. On Thu, Apr 25, 2013 at 6:25 PM, Graydon Hoare gray...@mozilla.com wrote: There are other questions to answer in this thread. We had a complex set of conversations yesterday on IRC concerning exposure of multiple named methods for the "other variants" -- ceiling, floor and truncating division, in particular. We may need to expose all 3, There are at least 6 different meanings of integer "divide". The IEEE-754 spec has 5 rounding modes, each of which maps directly to an integer div/mod rule: Truncate (round to zero: C-style, "%", "/" (integers only for "/")) Floor (round to -inf: The proposal for "mod" and "div") Ceil (round to +inf) Round to nearest, ties to even (default floating point rounding mode) (modulo is in the range [ -|d|/2 .. |d|/2]) Round to nearest, ties away from zero (grade-school rounding) (not IEEE): Euclidean division (% is always positive - see [1]) Plus there's floating point division, which is fundamentally different from integer division. Floating point division sets q = D/d so that D = q*d ("modulo" part of the division equation is zero). It's very different -- but all the integer modes still make sense for floats, since floats can represent integers. I don't think it's a good idea to expose six (or seven) division operators (though maybe they should all be in a library somewhere). Truncate and floor are - by far - the most common integer modes, and cover the normal programming use cases. and it might be the case that calling any of them 'quot' is just misleading; it's not clear to me yet whether there's a consistent method _name_ to assign '/' to (floating point divide seems to do the opposite of integer divide on chips that have both). I don't think it is possible to come up with one short word that means both "rounded floating-point division" and "truncating integer division". Maybe just call it "div_sign_op" and have it call "quot" for integers and "float_div" for floats? But I don't think it's wise to map % to 'mod' if we're exposing both 'mod' and 'rem'. That's a separate issue and one with (I think) a simpler answer for us. Agreed. "%" and "/" should satisfy the division rule [D = d*(D/d) + (D%d)], which requires "%" to map to rem if "/" truncates. Being "close-to-the-metal" seems to be very important. In the case of division, it's not as clear-cut as "what does Intel's 'idiv' implement". "idiv" is only used for division when the denominator is not a compile-time constant. When "idiv" is used, T-division (C-style) is closer to the metal. (Note: no hardware divide on ARM, so no difference there.) But when the denominator is known at compile-time, it's transformed into a multiply (or a bitwise-and for power-of-two denominator). In both the "multiply" and "bitwise-and" algorithms, F-division is closer to the metal (fewer instructions needed, on average). In any case where the denominator is a constant, F-division is the same speed or faster (and that's a big percent of divides in a lot of code - especially performance-conscious code). [1]: https://biblio.ugent.be/input/download?func=downloadFilerecordOId=314490fileOId=452146 or
Re: [rust-dev] Division and modulo for signed numbers
On 4/23/2013 9:02 AM, Patrick Walton wrote: On 4/23/13 7:48 AM, sw...@earthling.net wrote: Performance should be about the same when using F-division: * Performance will go up for division by constant powers of two. * Performance will stay the same for division by compile-time constants, since these are transformed by the compiler into multiplies. (I actually think performance will go up slightly in this case, but it's been a while since I looked at the algorithm.) * Performance on ARM will stay the same for divides by variables (not known at compile-time), since ARM does not have a hardware divider. * Performance on x86/x64 for divides by variables will go down slightly, since Intel's idiv instruction implements F-division. So one already very slow operation (x86 idiv) gets slightly slower, one fast operation (divide by power-of-two) gets quite a bit faster. It probably nets out near zero. I worry quite a bit about that last one. Rust language semantics strive to mirror the CPU instructions as closely as possible. This is why, for example, we were forced to make `` respect NaN, unlike Haskell--if we didn't, we couldn't use the hardware floating point comparison instructions. I'm also nervous about C interoperability. Including F-division as a library function sounds fine to me though--macros or dot notation may be able to sweeten the syntax. Patrick ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev Patrick, I would agree with you if it weren't for the cases where F-division performs *better*. Division by a constant power-of-two is pretty common, and is much faster in F-division. I don't think correctness should be sacrificed for a slight performance improvement in one case on one supported architecture. I've had real bugs in my C code caused by incorrectly casting (or forgetting to cast) to unsigned before a % operation. T-division causes *real* bugs. I'm willing to spend some time implementing F-division, if I can get some buy-in that the patch would be accepted. Erik ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Division and modulo for signed numbers
Thanks Graydon for the detailed reply to a newbie suggestion. It looks like I'm a little too late, this ship has already sailed. You're right that it's a topic reasonable people can disagree on. Adding Lint warnings seems like a poor workaround, but maybe the reduced confusion from C developers will outweigh the bugs caused. Or maybe all right-thinking people will agree to use the F-division function calls, and / and % will be unknown to Rust developers :-) I just looked over the Numeric traits bikeshed and pull 5990, and have a few comments. ( https://github.com/mozilla/rust/wiki/Bikeshed-Numeric-Traits and https://github.com/mozilla/rust/pull/5990 ) The floating point quot operator is now misnamed. It returns D/d, which is a floating point divide, not a quotient. (src/libcore/num/f32.rs line 323) I would suggest having floating point quot and rem return proper quotient and remainder (i.e. D.quot(d) has the same value for int and float, if the input/result is representable). Then the floating-point / operator needs to be mapped to a something other than quot (maybe div_float?). The % operator can be removed on floating point numbers. When a floating-point remainder is needed, you would have to call rem() explicitly. The numeric traits do not appear to require a % operator (which is probably a good thing, because it is hard to define for complex numbers). Finally some code review comments (possible already fixed, I didn't have time to check the tip - sorry) modulo replaced with rem inappropriately in a comment src/libstd/base64.rs line 118 Divide by zero error string became Quotient of zero src/libstd/num/rational.rs line 54 src/librustc/middle/trans/base.rs line 788, 790 src/test/compile-fail/eval-enum.rs line 2,3 Regards, Erik On 4/23/2013 11:46 AM, Graydon Hoare wrote: On 23/04/2013 8:53 AM, Diggory Hardy wrote: I suspect (please correct me if I'm wrong) that if it wasn't for C and x86 compatibility then most people would fall into two categories: don't know/don't care, and prefer F-division. It's one of those little things like tau vs. pi which would have been less confusing if we'd started off on the other foot to start with. And IP addresses would have been 64 or 128bit from the start, there would only be one endianness, and so forth ... yes, sure. But we don't cast off the weight of the past so easily, and I do not think this is something it's wise to fiddle with. A very widely-applied design choice in rust is that the basic types are machine-sized and the arithmetic operations on them are (as close as reasonable to) machine operations. The machine operation is remainder. Moreover, it's not just C and x86. This same choice is taken (more or less) by PowerPC, LLVM, C++, C#, Java, D, Go, JS, Scala, Ocaml, etc. etc. The path we've taken here is to admit _two_ operation-pairs, div/mod and quot/rem. This path is taken by several languages and specs (Scheme, Common Lisp, Ruby, Smalltalk, SML, Prolog, Haskell, Ada, and ISO 10967 and IEEE 754). The work for this landed last week: https://github.com/mozilla/rust/pull/5990 https://github.com/mozilla/rust/pull/6013 https://github.com/mozilla/rust/issues/4565 https://github.com/mozilla/rust/issues/4917 (Phew! This must really be the week for integer division!) The only remaining thing to struggle with is which of the two operation-pairs to assign the / and % symbols to, in the operator-overloading sense. For this, we've gone with quot/rem, as in the other languages above, and in keeping with the design preference above. You have to call .mod() or .div() to get the other operators. It does mean there's a bit of a footgun surrounding the symbols / and % specifically, on signed integer types. I would not be at all opposed to adding a lint flag to calls-to-/-and-%-on-signed-integer-types, such that you could trap them all. As with many things here, the design space includes a variety of (sensible) preferences. -Graydon (Please correct me if I've swapped which of the two meanings we've actually assigned; this is one of those issues like double-negation and off-by-one indexing where I get things backwards no matter how much energy I spend on making sure I get it right.) ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev