top | item 9325212

(no title)

frowaway001 | 11 years ago

  1) You basically have both () and [], wasting one of the scarcest
     resources in syntax (brackets).

     Some people might argue that getting a value from an array is totally 
     different from getting the same value from a function or a map, and
     deserves special syntax. If we look at languages which did away with 
     this made-up difference we see that pretty much no person is missing 
     it.

     Even if you disagree with this reasoning and consider arrays to be 
     something very special, deserving its own syntax – guess what? Rust 
     developers introduced the traits Index and IndexMut, so all the great 
     ideas about some type being special and knowing what happens when you 
     read [] went straight out of the window.

     If we dig deeper and try to understand why they did something so 
     astoundingly mis-designed, we basically hit an interesting issue:

       They overloaded their [] operator to do two completely different 
       things, depending on whether there is an & in front.
       Thing A: return the value at the given index
       Thing B: return an address pointing into the indexed data

     This of course doesn't translate well into the () syntax, given that 
     Rust devs are proud that they don't support overloading.
     (I guess []'s overloading doesn't count for some reason ... and doing 
     the little dance with traits doesn't count either.)

     Taking a step back, if it would have been considered acceptable to 
     pick two different names for doing two different things, the whole
     issue wouldn't exist, e. g.:

       Thing A: values(1)
       Thing B: values.at(1)

  2) Yes, we agree. Having different sets of rules for a) language 
     designers and b) language users has shown consistent results:
     It never worked out.

  3) Yes. I have nothing against terse syntax, it should just be 
     consistent. (I favor not abbreviating things though, because it's
     difficult to be both terse and consistent, considering that things 
     can be abbreviated in more than one way.)

  4) Yes, I know what they do. I just thought it was a nice example of two
     related constructs having different casing style and abbreviation 
     style.

  6/7) See 1). The sad thing is that [] can do whatever the developer 
     wants. All guarantees went out of the window with Index/IndexMut.

  9) What I meant was that not every macro wants to define some weird 
     language DSL. There are plenty of things which only exist as macros
     because the language lacks varargs. (vec! for instance.)
     If the language ever adds varargs, vec! can't just stop being a macro, 
     because that would break source compatibility: They would need to keep 
     the macro version of vec and introduce a new non-macro version of vec,
     and hope people migrate from vec! to the "new" vec.

     It's a design which makes the common 90% suffer for the benefit of the 
     most complex 10%. Sometimes it is worth it, but in this case I think it is
     not.

 10) I had the combination of picking a symbol which is both hard to read and
     hard to parse in mind.

     Every language that chose <> has weird cruft and therefore it shouldn't 
     haven been too surprising that Rust is also suffering issues, but in 
     Rust's case the problems have been shokingly severe. There are loooong 
     discussions about it if you want to read them. (So it's not just me.)

 11) Sorry, I meant structs.

 12) URLs are an option. There are also alternatives. I think the essence is
     that it's possible to deal with these issues without entering Java's
     tld.mycompany.mycontinent.mygroup.myproject.mydogsmiddlename madness.
I hope this cleared some things up. :-)

discuss

order

dbaupp|11 years ago

Re 1, 6 & 7, as we've discussed before: languages that do away with the () vs. [] distinction don't have the same restrictions as Rust, e.g. Scala doesn't have explicit addresses for objects, meaning `&x[i]` in Rust (get the address of the `i`th element) has no first-class equivalent in it. It isn't possible to implement this by just overloading `()` without introducing dramatic new language features.

On this note,

  They overloaded their [] operator to do two completely different things, 
  depending on whether there is an & in front.
is entirely wrong: `x[i]` is literally equivalent to `x.index(i)` or `x.index_mut(i)` depending on mutability, so the `&x[i]` form is the same as `&x.index(i)`, and `&` is a no-op. There's no special magic other than a slightly subtle desugaring.

No Rust dev is proud that there's no overloading... that sentence doesn't even make sense, as every operator can be overloaded, including the () call syntax.

Now I agree that there's often room for improvement in designs, but hammering on a relatively minor syntactic point (especially one that follows many, many other languages) for months seemingly without understanding the current design and the reasons for it is quite tiresome.

Ygg2|11 years ago

1) Sure, pair symbols are a bit rare. But they aren't as rare as you set them to be. Symbols like / | or \ can act as substitute for bracket like syntax.

    > If we look at languages which did away with 
      this made-up difference we see that pretty much no  
      person is missing  it.
Eh, not sure what languages are these and how you think they relate to Rust. Keep in mind Rust goal almost from the start was something that is familiar to a people that worked with Gecko engine. With that in mind, I can't think of a similar family language that did this.

You know what happens when you call Index/IndexMut. To say it isn't so, is outright fallacious.

Also Rust takes the probably best road of allowing limited operator overload, which IMO so far proved better than allowing no operator overload or allowing custom operator overload - which is subject to weird rules and/or leads to heavily overloaded code.

Also [] always does same thing. For given Index value it returns value found at that index. & is not an address. AFAIK address are never allowed unless you drop into unsafe. It's a borrow reference to Output value. Index returns immutable version, while IndexMut takes mutable version and returns mutable output value.

2) No we don't. I think having user and built-in defined types is perfectly ok. In any non-Lispy langugage there are special literals, which can't be handled by user defined ways, unless you allow for some funky grammar.

4) Related yes, same no. In same way i32 and u32 are related but not the same. As I said in previous question having some subset of language be unable to modify is IMHO a good thing.

6/7) They can't. You can tell by mutability of array which one will be called and which one will be returned. You CAN'T EVER change type of mutability of result.

9) Whenever I see macros/operator overload/syntax being given too much freedom, it always ends up with incomprehensible DSL-s that you can't disentangle from regular code.

10) Subjective opinion. And it could be trivially solved with better type aliases.

12) I don't think it's a big problem and a fix to Cargo that adds a special unique identifier should be trivial to implement, although not necessarily forward/backward compatible.

Ygg2|11 years ago

What multiple Rust proponents? Where? What people consider [] superior to <>? How many?

Your text have an air of superiority but your arguments leave a lot to be desired or to be more frank [Citation needed].

Ygg2|11 years ago

    > Nobody claims that <> isn't "familiar". People are claiming
    > that <> isn't very good, and they are correct with that.
    > Those languages which made <> "familiar" picked <> because
    > they added templates/generics/... after the fact and tried
    > to bolt them onto an existing language without breaking all
    > existing code.   
What people are claiming this? Is there some argument

     <Type, <Type<Type>>> 
is much better than

     [Type, [Type[Type]]]
in monospaced fonts? I agree I prefer it, but I know it comes down to subjectivity and familiarity. Not the most compelling reasons.

  >      impl Index<usize> for MyFunnyArray {
               type Output = u32;
               fn index<'a>(&self, _index: usize) -> &u32 {
                   &self.arr[random::<usize>() % 3]
               }
           }

           impl IndexMut<usize> for MyFunnyArray {
               fn index_mut<'a>(&'a mut self, _index: usize) -> &'a mut u32 {
                   self.arr[random::<usize>() % 3] = random();
                   &mut self.arr[random::<usize>() % 3]
               }
           }
Sure, you can write that, but you can also write:

      fn getIndex(&self, index: usize) {
           self.arr[random::<usize>()%self.arr.length]
      }
Rust won't protect you from stupid mistakes. The only question is whether it will allow you to override index syntax.

      myArray[3]
      myArray.getIndex(3) 
I actually prefer this small token of power, as opposed to having to write:

     date1.compareTo(date2) < 0
     date1.equalsTo(date2)
     date1.index(at)
That gets tedious fast.

     > As you might see, they are called i32 and u32, not i32 and UnsignedInt4Bytes.
Correct, because they are both built in literals. String isn't a built in literal. You can't have:

      u32::new()



     >Not sure what you suddenly try so say with "you can't
     >modify this", "you can't mutate that".

     >As shown in 1) [] can do whatever the developer implements.
     >Sure, the usual language rules apply, I think nobody 
     >disagreed on that.
No matter how many time you call Index on your array it stays the same. What kind of trippy Index you want to implement is still there, only with uglier syntax.

      > Yes, that's exactly the concern I'm raising here, because that's what Rust is encouraging.
It's not. All macro invocations are fenced in by macro calls. All syntax extensions are followed by extension calls. Could macros/syntax extensions do horrible things? Sure. So can regular code.