Alternatives to nested angle brackets

+1 for these proposals:

  • HashMap<Vec-int, Vec-Rc-Cell-int> or
  • HashMap<Vec:int, Vec:Rc:Cell:int>

Or if just a whitespace is unambiguous then, for some reason, that looks cleanest imho:

  • HashMap<Vec int, Vec Rc Cell int>
1 Like

Keep in mind that nested type parameter lists of more than one item exist.

Can your suggestion remain readable in cases such as this?

HashMap<Foo<int, A, B>, Bar<Vec<bool>, Qux<C, D, E>>>

How would this style

HashMap<Vec:int, Vec:Rc:Cell:int>

handle the first case?

Seems like the proposal's just path compression in a tree, and almost all your nodes have branches. Best you can do for that example is:

HashMap<Foo<int, A, B>, Bar<Vec:bool, Qux<C, D, E>>>

Guys, make sure you take into account #![feature(default_type_params)] when offering your suggestions

In light of RFC 114 syntax: |: a, b, c| expr, you could also use non-symmetric operators (I donā€™t think they look as clean though):

HashMap<Vec<String>, Vec<Rc<Cell<int>>>>
// Similar to `::` and `.` which seems good...
HashMap<Vec:.String, Vec:.Rc:.Cell:.int>
HashMap<Vec:-String, Vec:-Rc:-Cell:-int>
// Also problematic because of `<>`
HashMap<Vec:>String, Vec:>Rc:>Cell:>int>
// Easier with `[]`
HashMap[Vec:>String, Vec:>Rc:>Cell:>int]
// Probably don't want `:_` because of collision with _TypeName
HashMap<Vec:_TypeName, Vec:_Rc:_Cell:_int>

By the way, : probably may not be good even if RFC 135 frees it up some because:

struct Point {x: A<B<C>>, y: A<B<C>> }
// Implies 2 different meanings of `:`
struct Point {x: A:B:C, y: A:B:C }

Defaults:

struct Vec<A, B = C:.D:.E>;

Spaces with <> or () for grouping is the best option in my opinion. It has the benefit of being unambiguous and easy on the eyes as well as including minimal punctuation for default types.

HashMap<Vec<String>, Vec<Rc<Cell<int>>>>

becomes

HashMap <Vec String> <Vec <Rc <Cell int>>>

which remains easy to read while avoiding the level of angle brackets with the lowest signal/noise ratio. Alternatively:

HashMap (Vec String) (Vec (Rc (Cell int)))

This system works very well for non-deeply-nested types, which is important for generics:

Option <Box T>
Result V <Box Show>
Box T
Ref 'a T

Default type params:

pub struct Type<V, A = Vec<String>, B = Default> 
pub struct Type V A = <Vec String> B = Default
1 Like

This seems like it could be made easier to look at with the addition of (optional) commas like so:

pub struct Type V, A = <Vec String>, B = Default

Does that take away from what you were going for?

Not necessarily. Mainly Iā€™m looking to encourage the use of whitespace and remove the most inner pair of angle brackets, which, IMO, introduces the most noise.

bachmā€™s example from before:

HashMap<Foo<int, A, B>, Bar<Vec<bool>, Qux<C, D, E>>>

// Reem's solution becomes (I think...I think it's tricky)
HashMap <Foo<int> <A> <B>> <Bar<Vec bool> <Qux<C> <D> <E>>
// iopq's solution
HashMap<Foo<int, A, B>, Bar<Vec bool, Qux<C, D, E>>>

It becomes

HashMap <Foo int A B> <Bar <Vec bool> <Qux C D E>>

Parens makes this easier to parse:

HashMap (Foo int A B) (Bar (Vec bool) (Qux C D E))

Itā€™s worth noting that types rarely become this complicated without a type synonym or newtype being added along the way to break it up, and if they are this complicated it is usually an inferred type and you donā€™t really have to worry about it.

This seems Lisp-like, i.e. (op args) rather than op(args). It is interesting that this makes multiple-arg types shorter, whilst the other suggestions make single-arg types shorter.

This actually makes single-arg types short also, because the parens are only for grouping, not application. HashMap<String, uint> would be HashMap String uint and Vec<String> would be Vec String.

You can combine ideas by using both shorthands:

// This is contrived
HashMap<Foo<int, A, B, C, D, E>, Bar<Vec<bool>, Qux<F<G<H<I<J>>>>>>>

// If `:` is for singles like `A<B<C>> => A:B:C`
// then with `space` `A<B,C,D,E> => A B C D E`
HashMap<Foo int A B C D E, Bar<Vec:bool, Qux:F:G:H:I:J>>

// Probably the same as:
HashMap<Foo int A B C D E, Bar Vec:bool Qux:F:G:H:I:J>

Also, I think : looks nice in examples so I use it. Not strict about what symbol could be used. Iā€™m definitely not used to A<B,C,D,E> => A B C D E.

[EDIT] Removed :+ (I thought an extra symbol was needed for some reason)

1 Like

One possibility to get rid of the unfortunate angle brackets can be Reverse Polish notation (postfix notation):

HashMap<String, HashMap<String, int>>

becomes

String String int HashMap HashMap

and

HashMap<Vec<String>, Vec<Rc<Cell<int>>>>

becomes

String Vec int Cell Rc Vec HashMap

It works like a stack (think Forth, Factor or Joy). The only downside is that it wouldnā€™t work well with variadic generics.

Donā€™t forget the downside that itā€™s basically impossible to know what is argument of what without knowing the exact arity of everyone, and counting the stack. :slight_smile:

Reverse Polish notation is computer friendly, not human friendly.

And it will not work with HKT too (if Rust got them one day).

This also doesnā€™t work with default type parameters.

Iā€™ve not done my homework on HKT, so Iā€™ll ask you: might not operators between type names be a problem if types were first class citizens? E.g.:

HashMap<Vec-int, Vec-Rc-Cell-int>
// Vec-int

Does this imply that Vec-int is int subtracted from Vec? Then Vec-Rc-Cell-int is int subtracted from Cell which is subtracted from Rc subtracted from Vec?

There isn't a lack of operators if that's a problem though and I've already presented many many and there are still more technically.

No need to be sarcastic here, a normal discussion would be fine...

That wasnā€™t sarcasm. I honestly didnā€™t understand and was asking if what I wrote is what you meant.