3 ms·
Readability has multiple dimensions, of which "difficulty" is only one. The parent post is not talking about golang being confusing, but rather being verbose an
by deleuze 6y ago
Readability has multiple dimensions, of which "difficulty" is only one. The parent post is not talking about golang being confusing, but rather being verbose and low density, which makes it easier for bugs to hide when reviewing.
- jahaja 6y agoYeah, it does indeed. But compared to what? Reviewing high density code can surely hide bugs as well when reviewing? Either way, it's also easier to debug low density code so there are more things to consider.
- tsimionescu 6y agoThe biggest problem when reading Go code is that, like C, it tends to contain a lot of details that have to do with the language implementation, instead of the problem domain. Errors are bubbled up manually. Loops often use array indices, instead of expressing the desired operations. You often need to use pointers instead of values just to avoid the cost of constantly copying structs. And every time you encounter one of these things, you get to spend some time thinking about why this or that was chosen,or if you're missing some detail. Java is much more readable, because it has far fewer of these concerns. Every time you see a catch block, you know it is there for a reason. If someone is making a copy of an object, you know they wanted to make sure the original isn't changed. With Streams, if I want to write an operation which filters a list, I can use filter(), not 'create a new array, go through the original, every time you find an element matching the condition, write it at some index in the new array, increment that index'.
- jahaja 6y agoI think we'll have to agree to disagree on this one. That standard for loops are less readable just doesn't make any sense to me. Neither do the preference of exceptions which is pretty much the first things most experienced people turn off and ban in C++.
- tsimionescu 6y ago> most experienced people turn off and ban in C++ Sure, most experienced people dislike exceptions, except for Bjarne Stroustrup, Herb Sutter, everyone else who designs the 3 popular C++ compilers, everyone who designs some of the the most popular C++ libraries (Boost, newer Qt), Java, .NET, Swift, JavaScript, OCaml, Python, and a few others. And if you think that int j; for i := range someArray { if hasSomeProperty(someArray[i]) { filtered[j] = someArray[i] j++ } } is as readable as filtered = someArray.Filter(x => hasSomeProperty(x)) then yes, we have to agree to disagree.
- jahaja 6y agoLanguage design and lib design are different things than building, maintaining, and shipping a game involving hundreds of programmers. To use Boost is somewhat of an internal joke within the game industry. You don't need to use indexes like that in Go. You would append to the new array. The thing is that in practice such loops usually goes beyond just filtering on a boolean basis, so you combine whatever you want to do with that array in a single full iteration.
- tsimionescu 6y agoSure, games (and probably real-time systems in general) are one domain where exceptions are not a good control-flow mechanism. But there is much, much more to the software industry than real-time software, and extremely large systems built by huge teams of programmers do successfully use exceptions as a core error-handling mechanism, sometimes in C++, much more often in managed-memory languages. You're right about the indexes, I should have used append() there. Related to loops though, the more you need to do in a single loop, the less clear the code becomes in the traditional loop style. Note that stream-style constructs don't iterate more than once either (not that doing M things per iteration vs doing M iterations is necessarily a clear performance win, depending on the size of the array etc). For example, I would say that the readability difference is even more pronounced between these 2: for i := range players { isLocal := false for _,localPlayer := range localPlayerIds { if localPlayer.firstName == players[i].firstName && localPlayer.lastName == players[i].lastName { isLocal = true break } } if !isLocal { break } allLocalMoney += players[i].money numLocalPlayers++ } avg = allLocalMoney / numLocalPlayers vs (C#-ish) avg = allPlayers.Where(p -> localPlayerIds.Any(lp -> lp.firstName == p.firstName && lp.lastName == p.lastName)) .Average(p -> p.money) Add a little bit of grouping and things will get even worse for the first example. And sure, you could extract the checking into a separate function, but I wanted to come up with something that takes a few operations. On the other hand, I fully agree that sometimes you just need to do various actions (i.e. anything with side-effects) for each element in a list, and there there is nothing better for readability than plain loops. I just like the option of more explicit transformations when that's what I'm doing.