Skip to content

Switch <> back to [] #148

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

Closed
wants to merge 11 commits into from
66 changes: 66 additions & 0 deletions active/0000-square-brackets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
- Start Date: 2014-06-30
- RFC PR #: (leave this empty)
- Rust Issue #: (leave this empty)

# Summary

Switching (back) the current type parameter syntax from `<>` to `[]`.

# Motivation

Recently there has been a lot of talks on simplifying the syntax. Starting from removing the sigils `@` and `~` and making lifetimes less syntax heavy (through various proposals). I think changing the current generic syntax to `[]` will make it that much better and clearer (I think `[]` is much easier to read).

1. We would remove the current ambiguities surround the current syntax `<>`. That means, we could be able to have:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What ambiguities? You haven't described any.

The only ambiguity I know of is the one requiring the use of foo::<int>(), and the change to [] does not resolve that. As @chris-morgan already said, your code example here needs to still be foo::[int]() to be unambiguous.


```rust
struct Vec[T] {
// ...
}

fn foo[T]() -> Vec[T] {
// ...
}

fn main() {
let something = foo[int]();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is problematic, colliding with indexing—indexing requires an expression inside the [], specifying generic parameters requires a type or types. Thus I suggest that foo::<int>() is changed to foo::[int]() rather than foo[int](), for now at least.

}
```

2. `[]` composes **much** better than the more cryptic `<>` form. This is a common readability issue when working with any nested types (such as encoders and decoders).

```rust
fn parse['a, T: Encodable[Encoder['a], IoError]](value: T) {
// ...
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By what criteria are you claiming that this "composes much better"? It's literally the the same number of symbols, arranged in the same fashion, with the same basic properties (balanced pairs of some form of bracket), with the only only difference being one uses square brackets and one uses angle brackets.

You seem to be taking it as a given that what you've stated is true, but without providing any evidence whatsoever, I'm unconvinced that this provides a readability benefit.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What precisely do you mean by composes?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nesting them (i.e., T: Encodable[Encoder['a], IoError] vs T: Encodable<Encoder<'a>, IoError>)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@thehydroimpulse Those nest identically.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not the amount of nesting, it's that [] separates each piece a lot better (imo) than <>, resulting in it being easier to read.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@thehydroimpulse Then express it that way—for that is different from what people will understand by the other things you’ve said here. Incidentally, I agree with you completely on that claim.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chris-morgan Already in the latest commit.

```

3. It would bring the ability to have much nicer syntax when dealing with HKTs (there are a few different proposals I have in mind in terms of syntax, but it's mostly inferred.).

```rust
// Possible syntax for HKTs.
pub trait Monad[M[T]] {
// ...
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this bring the ability to have nicer syntax for HKTs? Wouldn't the example be exactly the same when written like this?

pub trait Monad<M<T>> {
    // ...
}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is one syntax proposal for HKTs (following in Scala's footsteps) which is ok (There are better alternatives that focus more on inference). I find [] a lot more composable than <> (i.e., you can nest them without it being cryptic to read).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t see this as a separate point from the second point.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Define "cryptic to read". The nesting properties of [] and <> are identical. You personally may simply have more experience reading nested [] due to being used to Scala. Me, not being a Scala programmer, very rarely encounter nested [] and I find there to be no benefit at all over <>.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, imo, [] is much nicer to read than <> without being exposed too much myself (to the [] syntax).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that this claim should be removed—it has no substance apart from the previous claim, is subjective and is not dealing with a concrete situation (HKT is far future).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, fixed!

```

4. There's precendence for it. Scala's syntax for generics is awesome. It imposes very little effort (I think) when reading and understanding.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After the first sentence, this is pure subjectiveness and not distinct from the earlier points.

What is of note here is this. At the time when Rust changed from [] to <>, there was no known precedent in a C-style language for []-generics: C++ and Java, for example, used <>. Since then, [] has been adopted in at least one fairly popular language, Scala.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will fix.


6. Because it's consistent and has no ambiguities, one can finally use motions like `%` in Vim (and alternatives in other editors.).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have never had an issue using % in Vim. We do not support the use of < or > as an operator inside of a generic type parameter list, which means there is no problem finding the matching < or > when using %.


# Detailed design

This is a very easy change to make.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That’s… not much of a detailed design. How about saying something like “in type parameter lists, replace < with [ and > with ].” and giving examples of T<U, …> and f::<T, …>()?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The


## Downsides
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/##/#/

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed!


* The syntax is used quite a bit. Automation could potentially do some, if not most of the changes (The tricky part is the ambiguities in the current syntax). However, of the changes we've had in the past, I think this syntax change is a whole lot easier to work with than semantic changes, or more complex syntax changes.

* One that I forgot about is the issue with the indexing syntax.

# Alternatives

* Keep it like it currently is and end up with the current syntax forever.

# Unresolved questions

* Why was did Rust originally have `[]` but decided to switch to `<>`? I heard it was related to try and be consistent with C-class languages (C++, Java, etc...), is this correct?