I find this a bit too extreme, but only removing nested angle brackets might be a good compromise. The question is then what to do with commas: they could be limited to the first level of nesting, or removed.
That would give us HashMap<Vec String, Vec Rc Cell int>
or HashMap<Vec String Vec Rc Cell int>
Unfortunately that reduces readability in some cases. If the commas stay, the rules are a bit more complex as well.
The problem with commas past the first level of nesting is that HashMap<String, HashMap<String, int>> would then become HashMap<String, HashMap String, int> which in my opinion is a bad middle ground between nothing at all and the current syntax. If we drop commas past the first level of nesting it would be HashMap<String, HashMap String int>. Of course, after two levels of nesting (of type parameter lists containing more than one type), it becomes messy (I wonder how common this actually is).
An alternative is that angle brackets are required wherever there are multiple type parameters. This means HashMap<String, HashMap<String, int>> remains the same but you still get the benefit of being able to say HashMap<Vec String, Vec Rc Cell int>
If any of these collide with other notation, I didn’t know about it but the idea could possibly still work.
HashMap<Vec<String>, Vec<Rc<Cell<int>>>>
// wider braces doesn't help
// replace `<x>` with `<:x:>`
HashMap<:Vec<:String:>, Vec<:Rc<:Cell<:int:>:>:>:>
Would tagging help?
// numbers count depth
// replace `<x>` with `<#:x:#>`
HashMap<1:Vec<2:String:2>, Vec<2:Rc<3:Cell<4:int:4>:3>:2>:1>
// numbers count from left to right
HashMap<1:Vec<2:String:2>, Vec<3:Rc<4:Cell<5:int:5>:4>:3>:1>
Though, even if it works, I have no idea if you’d want this…
On vim there's the g:lisp_rainbow option for coloring parenthesis on lisp code: http://i.imgur.com/0GIHout.png (discourse won't let me embed the image)
Are you suggesting something along these lines?
[Only remove angle brackets for single-type-parameter types]
Option Box
Vec Box
Rc Cell
I really like this suggestion, because parsing does not rely on the arity of the type.
Single type parameter => can optionally(?) use juxtaposition-style
Multiple type parameters => always use angle brackets
Advantages
Rules are easy to understand: syntactic sugar for one simple scenario Vec<int> <==> Vec int
Unambiguous without symbols: compiler doesn't need to know number of type parameters to correctly parse type expression. If you forget one type in the remove-all-angle-brackets example, all other types shift to the "wrong" positions, potentially resulting in very confusing error messages
Disadvantages
Only improves some expressions (but probably simplifies complex scenarios enough to make them acceptable)
I think the same arguments for using infix notation over postfix notation in mathematical expressions applies here. Yes, the latter is unambiguous as a whole (complete, correct expression with full arity information) but working on those expressions is not an ideal experience.
Personally, I would always allow angle brackets, even for single-type-parameter types. Makes transition from other languages easier and doesn't cost anything implementation-wise.
Putting no symbol between the elements makes it read wrong to me: Option Box isn’t an “option-box”, it is an “option of box”. I suggested X/Y for “X of Y”, but maybe some other symbol could be used.
Slash also suggests one being over the other, in the sense that the Option controls/encapsulates the Box (Option/Box), or the Cell controls/encapsulates the int (Cell/int).
Personally, I think I’ve been around Haskell etc. long enough for this to feel OK. There you read f x as “f of x” anyway.
I’m not too keen on the separating character. It seems to separate things (optically) that belong together (logically). The ‘dot form’ (A.B) doesn’t make sense in my mind. Those are not paths, they shouldn’t look like paths.
But what about doing it in the opposite order like in OCaml and F#: int Option. The original example would become:
Well, I don’t know Haskell. Since Haskell is more the Rust way, then probably they might favour the spaces approach then, so long as it is parseable.
(On parsing, does Rust depend on knowing definitely where the type stops? For example after as. Because many of these suggestions may introduce complications on that front.)
I agree. And <String Vec, Vec Rc Cell int>HashMap looks just wrong somehow.
On the topic of "paths". I'm not sure about the 'encapsulates' explanation. The one way in which I can imagine "path"-like syntax making sense is this:
Vec.int <=> From all possible Vec types, get me the Vec for ints.
(or Vec/int or Vec\int ...)
Maybe use a character that looks more like "glue" as opposed to "navigation"?
Vec+int Might cause issues/confusion with generic parameter bounds
Vec-int, Vec-Rc-Cell-int
Vec@int, Vec@Rc@Cell@int (looks super busy, probably not)
Is having two different syntaxes for supplying type parameters really worth it? Sounds like an unnecessary hurdle for newcomers.
HashMap<Vec<String>, Vec<Rc<Cell<int>>>>
// Use -> but confusable with `<>` ends
HashMap<Vec->String, Vec->Rc->Cell->int>
// a little better
HashMap[Vec->String, Vec->Rc->Cell->int]
// also => if available
HashMap[Vec=>String, Vec=>Rc=>Cell=>int]
// still . is nicer
HashMap<Vec.String, Vec.Rc.Cell.int>
// if : is made available (see Note)
HashMap<Vec:String, Vec:Rc:Cell:int>
// can extend : but not compact
HashMap<Vec:-:String, Vec:-:Rc:-:Cell:-:int>
HashMap<Vec:=:String, Vec:=:Rc:=:Cell:=:int>
HashMap<Vec:_:String, Vec:_:Rc:_:Cell:_:int>
// not a fan of ~
HashMap<Vec~String, Vec~Rc~Cell~int>
Use a font style like Hasklig so that << and >>map to a custom style. Then it is possible to type and make it look different. You could also use their other special symbols (-<, -<<, and =<< for example).