> I'm sure, but my reading of the winds is that people will find other idiosyncracies of Go to latch onto and complain about. It seems to me the next object of hatred is the lack of sum types.
This is a needlessly dismissive perspective. Put another way, now that one of golang's most prominent deficiencies has been addressed, people will switch their focus to other areas of frustration.
> Sometimes I think the core irritation with Go is its simplicity.
The core irritation (well, my core irritation) around Go is that this simplicity just kicks the cans of complexity and verbosity downstream onto its users, and—in the worst cases—hides the fact that this complexity exists, so encourages writing simple, obvious, and subtly incorrect solutions. Subtly incorrect is by far the worst kind of incorrect, as it results in code that works for all the obvious test cases but breaks at 2AM in production. Go doesn't feel simple to me. It feels half-assed.
Let's take a recent example I had to work with: truncating a string to the first 63 characters. Easy, right?
s = s[:63]
Simple, obvious, and subtly incorrect. Why? UTF-8. Strings in go are just a microscopic wrapper around immutable byte arrays. I could almost consider this okay if there was some type in the language that wrapped a byte array and an encoding to provide a real string type. But not only isn't one included, a reliable one can't even exist. The range keyword iterates over bytes for byte arrays and over UTF-8 code points for strings and there's no way to express anything different. String straddles this uncomfortable middle where it's both a byte array and a UTF-8 string and which one it is depends implicitly on which operators you use on it. Strings are "just" a byte array, except they're supposed to contain UTF-8, but not only is there no way to enforce that they do, the language makes it trivial to accidentally corrupt any UTF-8 string you do have (e.g., through indexing). If you're writing a function and somebody passes you a string you have no way to know or enforce that it's intended to bytes or ASCII or UTF-8 or any encoding whatsoever without iterating through to check. There is no type that actually encodes an enforced requirement of a valid UTF-8 string, nor can one even be written as a library.
So the way you're "supposed" to truncate a string is:
s = string([]rune(s)[:63])
At least I think that's the magical incantation. Do you do this everywhere? Do your coworkers? Yeah, I didn't think so.
Compare this to Rust and Ruby, which take two diametrically opposite (but both IMO reasonable) approaches. In Rust, strings are UTF-8. You cannot construct a String that does not contain a valid UTF-8 byte sequence (at least not without unsafe). You cannot perform safe operations on a string that result in an invalid byte sequence. If you are handed a string, you know that it contains valid UTF-8 bytes. You can iterate over either the bytes or the characters, but you have to explicitly decide which. In Ruby, strings are byte array / encoding pairs. It could be UTF-8, UTF-16, Shift-JIS, EBCDIC, or any other encoding you want it to be. When you're handed a string you can generally assume it's been parsed and validated under that character set. Indexing, iteration, and other operations are all encoding-aware. If you want the bytes, you can choose to access them.
The string equivalent in golang is, to put it bluntly, half-assed. It's mostly just a byte array but some keywords assume it's UTF-8. If you're given a string you generally assume it's UTF-8 but there's no reasonable expectation that it's valid since the language makes it ridiculously easy to violate the encoding rules. Go strings aren't even a decent building block for an encoding-aware type since range can't be made to work on other encodings. For a language written by none other than Rob Pike himself, I simply cannot fathom how golang arrived at this design.
Keep in mind this is just one of a myriad places where the language punts complexity to the user but, at best, doesn't give them the tools to actually reliably handle it and, at worst, pretends like the complexity doesn't even exist so it's not even obvious something needed special care in the first place. Simple, obvious, and subtly incorrect.
None of your proposals truncate a Unicode string to the first 63 characters. Go and Rust are at least honest about this. Ruby lies about what you're indexing and makes you pay O(n) to do it. (And no, that's not the way you're "supposed to" truncate to a codepoint offset in Go, but it's also true that there's no function to do it in the standard library - in part because that's rarely what you really want to do and it would get abused to do such.)
> When you're handed a string you can generally assume it's been parsed and validated under that character set
But it might not have been. It's a weakass guarantee, the same Go gives you, except in Go it's UTF-8-or-garbage, vs. Ruby's could-be-anything-and-could-still-be-garbage.
> this simplicity just kicks the cans of complexity and verbosity downstream onto its users
I still think that append() is the arch-example of that. I can't think of another high-level PL that doesn't have an atomic add-item-to-collection operation (either in the language or in stdlib) without requiring the user to coordinate all the moving parts.
This is a needlessly dismissive perspective. Put another way, now that one of golang's most prominent deficiencies has been addressed, people will switch their focus to other areas of frustration.
> Sometimes I think the core irritation with Go is its simplicity.
The core irritation (well, my core irritation) around Go is that this simplicity just kicks the cans of complexity and verbosity downstream onto its users, and—in the worst cases—hides the fact that this complexity exists, so encourages writing simple, obvious, and subtly incorrect solutions. Subtly incorrect is by far the worst kind of incorrect, as it results in code that works for all the obvious test cases but breaks at 2AM in production. Go doesn't feel simple to me. It feels half-assed.
Let's take a recent example I had to work with: truncating a string to the first 63 characters. Easy, right?
Simple, obvious, and subtly incorrect. Why? UTF-8. Strings in go are just a microscopic wrapper around immutable byte arrays. I could almost consider this okay if there was some type in the language that wrapped a byte array and an encoding to provide a real string type. But not only isn't one included, a reliable one can't even exist. The range keyword iterates over bytes for byte arrays and over UTF-8 code points for strings and there's no way to express anything different. String straddles this uncomfortable middle where it's both a byte array and a UTF-8 string and which one it is depends implicitly on which operators you use on it. Strings are "just" a byte array, except they're supposed to contain UTF-8, but not only is there no way to enforce that they do, the language makes it trivial to accidentally corrupt any UTF-8 string you do have (e.g., through indexing). If you're writing a function and somebody passes you a string you have no way to know or enforce that it's intended to bytes or ASCII or UTF-8 or any encoding whatsoever without iterating through to check. There is no type that actually encodes an enforced requirement of a valid UTF-8 string, nor can one even be written as a library.So the way you're "supposed" to truncate a string is:
At least I think that's the magical incantation. Do you do this everywhere? Do your coworkers? Yeah, I didn't think so.Compare this to Rust and Ruby, which take two diametrically opposite (but both IMO reasonable) approaches. In Rust, strings are UTF-8. You cannot construct a String that does not contain a valid UTF-8 byte sequence (at least not without unsafe). You cannot perform safe operations on a string that result in an invalid byte sequence. If you are handed a string, you know that it contains valid UTF-8 bytes. You can iterate over either the bytes or the characters, but you have to explicitly decide which. In Ruby, strings are byte array / encoding pairs. It could be UTF-8, UTF-16, Shift-JIS, EBCDIC, or any other encoding you want it to be. When you're handed a string you can generally assume it's been parsed and validated under that character set. Indexing, iteration, and other operations are all encoding-aware. If you want the bytes, you can choose to access them.
The string equivalent in golang is, to put it bluntly, half-assed. It's mostly just a byte array but some keywords assume it's UTF-8. If you're given a string you generally assume it's UTF-8 but there's no reasonable expectation that it's valid since the language makes it ridiculously easy to violate the encoding rules. Go strings aren't even a decent building block for an encoding-aware type since range can't be made to work on other encodings. For a language written by none other than Rob Pike himself, I simply cannot fathom how golang arrived at this design.
Keep in mind this is just one of a myriad places where the language punts complexity to the user but, at best, doesn't give them the tools to actually reliably handle it and, at worst, pretends like the complexity doesn't even exist so it's not even obvious something needed special care in the first place. Simple, obvious, and subtly incorrect.