Yes, let's move away from the standard syntax used to specify generics, which is used in virtually every widely-used programming language of the past 15-20 years I can think of, and instead redefine the most common syntax used for array indexing (oh, and make array indexing look like a function call).
This fixating on relatively inconsequential issues, while totally missing the forest for the trees, is a dangerous attribute I've found in a small subset of programmers.
I would say it's in a small subset of people who think like computer scientists rather than like software engineers. Computer scientists think about better syntax for computer languages. (I don't think that this is actually a better syntax, especially given the already-established conventions. But thinking about array indexing as a function call is, in my view, an interesting take.)
Software engineers, on the other hand, worry about how to efficiently write non-trivial programs. For that problem, this change doesn't move the needle whatsoever.
Most non-junior programmers think more like software engineers than like computer scientists. And even many who think like computer scientists can see the forest, not just the trees.
This article completely loses me at the end where it suggests that using square brackets for assignment and collection access is an unnecessary/dangerous convenience.
It might not be the author's preference, but the argument made about it getting abandoned as the language ages anyway is demonstrably false with the most popular languages in the world (Python, JavaScript, Java, C, C++), and in languages with operator overloading it could never be true no matter how far the language evolved.
I think the tone of this post is insufferable, and the arguments have little merit presented this way.
The alternative suggested is also non ideal, since array declaration/indexing is semantically different than a function call, not all languages have method calls, not all languages have postfix method calls, I can think of a few ways to break that syntax (what if the type has a call operator and indexing operator?), and it is hypocritical.
The problem with <> is that they are used elsewhere. If you use [] by sacrificing arrays, () will cause problems because they're used elsewhere.
The lowest friction solution would be to introduce a new two character bracket. How about <: or :>? (: :)? I don't know but writing about it in that tone won't get anyone to do something different.
From a computer science standpoint it is obvious that generic declarations are just functions that accept type parameters and return a declaration according to the type parameters. So the only reasonable suggestion would be to use the same syntax for both type parameters and normal parameters and optionally add something that distinguishes between the two (like your !).
Meanwhile picking either <> or [] feels like that decision is based on personal preference. Obviously, you can justify choosing the most popular syntax because of familiarity but then [] was never an option to begin with.
I tried (lightly) to convince people to switch from <> to [] well before Rust 1.0, but even when many (not sure if it was most) agreed that square brackets were superior, there was no will to make such a radical syntactic change, for two main reasons:
1. Angle brackets are also not uniformly superior. Technically, I think it’s fair to consider them uniformly superior, but social aspects are important too; there was reasonable concern that Rust had exhausted its weirdness budget. (Of the most common languages people may be familiar with when coming to Rust, I think Scala is the only one that uses square brackets for generics; the likes of C++, C and Java are much more common and use angle brackets.)
2. For Rust specifically, it wouldn’t actually have been just a syntactic change—if you want to change array indexing to use function call syntax, you’ve got to sort out more technical challenges there, things that would be approaching trivial in full-GC languages but which are actually rather difficult in Rust. Specifically, function calls are rvalues only (`… = f()`), but array indexing can be an lvalue also (`a[i] = …`). So you need to more or less unify the Index and Fn trait families, auto-ref/-deref might cause trouble, and placement might make an appearance in it too for best results (and that’s something that still isn’t resolved). Now I’m inclined to believe that these changes would have been a really good thing and resulted in a more coherent and incidentally slightly more flexible language (and not in a dangerous way), but it would now be even more difficult to achieve (though not impossible; the edition system could be used for the syntax side of things, and Fn/Index implementations are still mutually exclusive, because you can’t implement Fn manually on stable, so you could blanket-impl Fn traits for types implementing Index traits without breaking backwards-compatibility).
I’m simplifying the story a bit. Others may fill out more history and technical detail if they desire. I’m going to bed.
Can't say I agree with most of the last paragraph. I much prefer to have my function calls easily differentiated from my array indexing / hashmap keying.
Though I do agree that array/object literals using [] and {} are probably not necessary.
My personal anecdatal arguement against "Hard to parse for humans" is... I actually really like generics residing within <>. It's actually harder for me to read generics denoted otherwise (D uses parentheses)
As for the last paragraph, I'd implore language implementors to continue using < > lest we get that awfully ambigous nonsense.
Scala uses brackets for generics and parens for array accesses. It works nicely, I think.
The primary argument against < and > as generic brackets is that the ambiguity can make for some confusing error messages. It also prevents you from pre-matching your braces before the parsing phase (a technique that enhances error recovery).
I managed to get them back in there with a space in between.
Input sanitizing and escaping is one aspect of the HN code I've never dug too much into. There are a lot of corner cases that don't work, but it's never been a high enough priority to fix them.
If this had been published in, like, 1982, maybe the author would've had a point. But by now we're all so used to <> for generics, especially when scanning code rapidly, that I can't imagine the meager benefits of switching would outweigh the cognitive cost.
I don’t think in 1982 they had issues with it - most problems that this article purports angle brackets to have are problems we have recently invented.
As for HN not being able to handle having them as characters in titles, one would hope that would be a “simple” matter of escaping the characters at time of submission and parsing them at time of rendering.
Try using verilog where <= is both the non-blocklocking assignment operator and the less-than-or-equal operator. At first it might sound/look weird, but you can get used to it easily.
It’s all about the context; I think it would actually be more confusing to go with the author’s suggestion.
Furthermore, I can’t think of a place in code where the generic and comparison use cases of < or > would be ambiguous or even adjacent.
> Furthermore, I can’t think of a place in code where the generic and comparison use cases of < or > would be ambiguous or even adjacent.
Most languages that use angle brackets for generics (e.g. C++, Rust, Swift) also allow you to explicitly instantiate generics, which leads to a syntactic ambiguity, e.g. is
a < b > (c)
a function call or two comparisons? The usual way to disambiguate in these cases is to check whether b is plausibly a type and treat it as a generic, but this is ugly on a few different levels.
That’s the most ridiculous article I read this week. At this point the fact I can’t input < and > with my virtual Azerty on physical Japanese keyboard could make in it to make it more substantial.
IMO haskells way of defining type parameters with forall is the best option. It doesn't fit with C-style function definitions though.
It's kind of a stupid thing to rant over. Whether a language uses <> or [] is the least interesting thing about it, but that doesn't stop everyone from bikeshedding.
Personally, as a C++ adventurist I find the instantiating syntax a bit confusing, but quite tolerable.
I wonder whether anyone attempted to use "|T|" as their template syntax...Ruby uses it in iterators (I think) and if I'm not mistaken Rust uses it with closures.
Wouldn't be a bit cleaner to have something like the following?
|T| something (T foo) -> T {
// do something in here
return foo;
}
> Ruby uses it in iterators (I think) and if I'm not mistaken Rust uses it with closures.
Ruby and Rust both use them for closures (I assume Rust took them from Ruby). Nearly all of Ruby's iteration constructs are based on closures, so it makes sense that you understood them as for iterators.
I agree with some parts of the problem (those < > chars already have other uses), but not others (guillemets are even shorter and they read just fine). I don't care for the proposed solution: it's just robbing Peter to pay Paul.
Now people are proposing digraphs and trigraphs as alternatives. Have we learned nothing from C?
I'm annoyed that (even in 2020) every programming language restricts its syntax to ASCII characters (1967) which can be typed on an IBM Model F keyboard (1981). Unicode has dozens of unique styles of brackets. Everybody's using an OS that supports Unicode, and an editor/IDE that autocompletes most of their source code anyway. We're even using programming languages which allow Unicode identifiers, so ASCII-only viewers have been in trouble for 25 years already. People are even using emoji in commit messages. That ship has sailed. Unicode is safe to use.
It could be List⟦Int⟧ or List⟬Int⟭ or List「Int」 or dozens of others. They're big, they're clear, they're easy to parse (one char, no other uses). All you need to do is pick one and make it part of the language, and update a few editor modes to support it in some templates.
It's not going to happen, but my favorite solution to the parsing ambiguities would be to parse < as a comparison if there is whitespace around it, and as a template bracket otherwise. Kind of how "puts -1" in Ruby prints -1, and "puts - 1" tries to subtract 1 from the result of puts.
Unfortunately, scala caused parsing problems by allowing alphabetic function names as operators, grabbing implicit arguments from all over the place, and leaving out () in function calls (I think they reversed the latter decision)
[+] [-] hn_throwaway_99|6 years ago|reply
This fixating on relatively inconsequential issues, while totally missing the forest for the trees, is a dangerous attribute I've found in a small subset of programmers.
[+] [-] AnimalMuppet|6 years ago|reply
Software engineers, on the other hand, worry about how to efficiently write non-trivial programs. For that problem, this change doesn't move the needle whatsoever.
Most non-junior programmers think more like software engineers than like computer scientists. And even many who think like computer scientists can see the forest, not just the trees.
[+] [-] ledauphin|6 years ago|reply
It might not be the author's preference, but the argument made about it getting abandoned as the language ages anyway is demonstrably false with the most popular languages in the world (Python, JavaScript, Java, C, C++), and in languages with operator overloading it could never be true no matter how far the language evolved.
[+] [-] aloknnikhil|6 years ago|reply
> someList(0) /* instead of */ someList[0]
This is worse. How do you disambiguate that from a function call? It's now just abusing ()
[+] [-] unlinked_dll|6 years ago|reply
The alternative suggested is also non ideal, since array declaration/indexing is semantically different than a function call, not all languages have method calls, not all languages have postfix method calls, I can think of a few ways to break that syntax (what if the type has a call operator and indexing operator?), and it is hypocritical.
The problem with <> is that they are used elsewhere. If you use [] by sacrificing arrays, () will cause problems because they're used elsewhere.
The lowest friction solution would be to introduce a new two character bracket. How about <: or :>? (: :)? I don't know but writing about it in that tone won't get anyone to do something different.
[+] [-] WalterBright|6 years ago|reply
D uses !() for generics. It looks odd at first, but soon becomes completely natural:
Since ! is not a conventional binary operator, this:1. parses without lookahead and ambiguity issues
2. stands out in the code as being a template instantiation
Well over a decade of experience with it (and D code typically makes heavy use of templates) with no issues shows that it works.
[+] [-] imtringued|6 years ago|reply
Meanwhile picking either <> or [] feels like that decision is based on personal preference. Obviously, you can justify choosing the most popular syntax because of familiarity but then [] was never an option to begin with.
[+] [-] chrismorgan|6 years ago|reply
1. Angle brackets are also not uniformly superior. Technically, I think it’s fair to consider them uniformly superior, but social aspects are important too; there was reasonable concern that Rust had exhausted its weirdness budget. (Of the most common languages people may be familiar with when coming to Rust, I think Scala is the only one that uses square brackets for generics; the likes of C++, C and Java are much more common and use angle brackets.)
2. For Rust specifically, it wouldn’t actually have been just a syntactic change—if you want to change array indexing to use function call syntax, you’ve got to sort out more technical challenges there, things that would be approaching trivial in full-GC languages but which are actually rather difficult in Rust. Specifically, function calls are rvalues only (`… = f()`), but array indexing can be an lvalue also (`a[i] = …`). So you need to more or less unify the Index and Fn trait families, auto-ref/-deref might cause trouble, and placement might make an appearance in it too for best results (and that’s something that still isn’t resolved). Now I’m inclined to believe that these changes would have been a really good thing and resulted in a more coherent and incidentally slightly more flexible language (and not in a dangerous way), but it would now be even more difficult to achieve (though not impossible; the edition system could be used for the syntax side of things, and Fn/Index implementations are still mutually exclusive, because you can’t implement Fn manually on stable, so you could blanket-impl Fn traits for types implementing Index traits without breaking backwards-compatibility).
I’m simplifying the story a bit. Others may fill out more history and technical detail if they desire. I’m going to bed.
[+] [-] steveklabnik|6 years ago|reply
[+] [-] fastball|6 years ago|reply
Though I do agree that array/object literals using [] and {} are probably not necessary.
[+] [-] F-0X|6 years ago|reply
As for the last paragraph, I'd implore language implementors to continue using < > lest we get that awfully ambigous nonsense.
[+] [-] seanmcdirmid|6 years ago|reply
The primary argument against < and > as generic brackets is that the ambiguity can make for some confusing error messages. It also prevents you from pre-matching your braces before the parsing phase (a technique that enhances error recovery).
[+] [-] karlicoss|6 years ago|reply
[+] [-] Animats|6 years ago|reply
Let's see if this will work.
<
Nah.
[+] [-] dang|6 years ago|reply
Input sanitizing and escaping is one aspect of the HN code I've never dug too much into. There are a lot of corner cases that don't work, but it's never been a high enough priority to fix them.
[+] [-] Analemma_|6 years ago|reply
[+] [-] troughway|6 years ago|reply
As for HN not being able to handle having them as characters in titles, one would hope that would be a “simple” matter of escaping the characters at time of submission and parsing them at time of rendering.
[+] [-] jackyinger|6 years ago|reply
It’s all about the context; I think it would actually be more confusing to go with the author’s suggestion.
Furthermore, I can’t think of a place in code where the generic and comparison use cases of < or > would be ambiguous or even adjacent.
[+] [-] cwzwarich|6 years ago|reply
Most languages that use angle brackets for generics (e.g. C++, Rust, Swift) also allow you to explicitly instantiate generics, which leads to a syntactic ambiguity, e.g. is
a function call or two comparisons? The usual way to disambiguate in these cases is to check whether b is plausibly a type and treat it as a generic, but this is ugly on a few different levels.[+] [-] u801e|6 years ago|reply
[+] [-] mkchoi212|6 years ago|reply
[+] [-] praptak|6 years ago|reply
[+] [-] tasogare|6 years ago|reply
[+] [-] leshow|6 years ago|reply
It's kind of a stupid thing to rant over. Whether a language uses <> or [] is the least interesting thing about it, but that doesn't stop everyone from bikeshedding.
[+] [-] stefanos82|6 years ago|reply
I wonder whether anyone attempted to use "|T|" as their template syntax...Ruby uses it in iterators (I think) and if I'm not mistaken Rust uses it with closures.
Wouldn't be a bit cleaner to have something like the following?
Personally I prefer it.[+] [-] Gaelan|6 years ago|reply
Ruby and Rust both use them for closures (I assume Rust took them from Ruby). Nearly all of Ruby's iteration constructs are based on closures, so it makes sense that you understood them as for iterators.
[+] [-] ken|6 years ago|reply
Now people are proposing digraphs and trigraphs as alternatives. Have we learned nothing from C?
I'm annoyed that (even in 2020) every programming language restricts its syntax to ASCII characters (1967) which can be typed on an IBM Model F keyboard (1981). Unicode has dozens of unique styles of brackets. Everybody's using an OS that supports Unicode, and an editor/IDE that autocompletes most of their source code anyway. We're even using programming languages which allow Unicode identifiers, so ASCII-only viewers have been in trouble for 25 years already. People are even using emoji in commit messages. That ship has sailed. Unicode is safe to use.
It could be List⟦Int⟧ or List⟬Int⟭ or List「Int」 or dozens of others. They're big, they're clear, they're easy to parse (one char, no other uses). All you need to do is pick one and make it part of the language, and update a few editor modes to support it in some templates.
[+] [-] ledauphin|6 years ago|reply
imagine being a beginner programmer and not even being able to find the character you need to make your program work without learning about Unicode...
[+] [-] gurkendoktor|6 years ago|reply
[+] [-] quink|6 years ago|reply
I'd loooove to hear the author's thoughts on the above then extending weird parentheses syntax crap to function calls, MUMPS style too:
Maybe I'm warning of a slippery slope here, but this is a direction that I'm not sure you'll want to go down.Also a fun fact, MUMPS has very dynamic typing and thus very much no type annotations.
[+] [-] Someone|6 years ago|reply
- https://docs.scala-lang.org/overviews/collections/arrays.htm...
- https://docs.scala-lang.org/tour/generic-classes.html
Unfortunately, scala caused parsing problems by allowing alphabetic function names as operators, grabbing implicit arguments from all over the place, and leaving out () in function calls (I think they reversed the latter decision)
[+] [-] kalekold|6 years ago|reply
[+] [-] j88439h84|6 years ago|reply