3 ms·
> Go is an extreme parody of C mindset adapted to kindergarten level of risk There's nothing of substance actually said here so it's hard to refute or support
by jchw 3y ago
> Go is an extreme parody of C mindset adapted to kindergarten level of risk
There's nothing of substance actually said here so it's hard to refute or support it very much.
> so that Google could tackle its internal dev culture issues.
Only one sentence in and this sounds like a vague reference to that thing Rob Pike said one single time 10 years ago or whatever, but I don't really care why Go was supposedly created or what it was for over 10 years ago, I care about what it is today. I think you can do better than this.
> There is only a single high-level language with GC today that exposes appropriate low-level primitives and it is C#. In C#, you can declare C-style struct and pass it directly to C code across interop without any marshalling whatsoever. You cannot do this in the way everyone uses Go.
I dunno what you mean exactly, you certainly can declare a C-style struct in Go. You can declare it using C, or you can pass a Go-defined struct into a C function, it follows roughly the same alignment rules, I do this all the time when coding against Win32 APIs. It's in fact better than it used to be because there is now a way to pin memory so that it is safe to pass Go pointers directly to C code, using `runtime.Pinner`, but certainly there was no limitation to using structs the C way before. There aren't many guarantees about memory layout, but there wasn't in C either.
> I'm also amused by how "AOT" is one of the biggest demonstration of "X Y issue" in practice where developers have little understanding of how each platform can have drastically different ways of achieving "lean fast to start applications". What's unfortunate in this is they make Go's AOT as a selling point, where it's just its deployment model, at which it's not even the best nowadays
OK... but for context... I was literally only trying to demonstrate that Go is a relatively low-level programming language in terms of how close you are to machine code, putting it in a similar camp to languages like C and Rust, rather than something like Python. It is not an argument in favor of a given way of packaging or executing software. You can of course make programming language implementations with different tradeoffs, but I don't think it's accidental that scripting languages, compiled bytecode languages and compiled machine code languages tend to have different attitudes, I'm of a mind that they are mostly the way they are because of the characteristics that would lead someone to reach for one versus the others.
> you will be much better served by forgoing Go in favour of Rust or C#
I don't think there's any reason to disagree that Rust is a great choice of programming language. Rust has numerous amazing advantages versus most other programming languages, offering an excellent set of guarantees. However, it does so by paying a lot of language cost to get there. I'm not saying, for example, that this means that Go is good at some use cases and Rust is good at others, I'm saying that Go and Rust have some overlap at what they are good at and they have some areas where one will have nicer tradeoffs than the other. Rust handily has the advantage for writing concurrent code, which is ironic given Go's name and how much its concurrency model was emphasized early on. But Rust is a very, very complex programming language; sometimes what it goes through great, great pains is something that you absolutely, 100% want to have, at any cost, and sometimes it's just not. When dealing with embarassingly parallel programs like network services with nothing-shared architectures, Go shines; it's pretty good at these. When every CPU cycle and byte counts, Rust wins against Go every single time, because it offers greater control and vastly more powerful compile-time metaprogramming. (Go offers essentially none, since even generics mostly compile down to interfaces.) Does the complexity always matter? Well, maybe not, but you'll notice in compile times.
> please do not tell me about how good Go's standard library is - it isn't, I know how a good one looks like
This is a shallow dismissal that shouldn't really convince anyone since it doesn't even attempt to paint the picture so I will reply with a retort of the packages in the Go standard library that I think are pretty great and I would like to have in other programming languages.
- Literally all of `crypto`, including `crypto/tls`. To be completely fair, I wouldn't argue they are perfect (`tls.Config` is a little weird) but they are very clean and reasonable implementations of cryptographic routines including some nice optimized assembler code across many architectures for plenty of them. The actual interfaces are pretty good and help to avoid some basic pitfalls. It is relatively easy to e.g. create and sign an X509 certificate, versus OpenSSL.
- `regexp` - Neither the fastest nor the most fully-featured regular expression engine. On the other hand, though, it gives you re2 behavior - the same featureset, and the same runtime behavior, specifically that it scales linear with time with regards to input size. That makes it a rather good library to have as a default choice, since it is significantly less likely to wind up as a surprise footgun that way, versus say PCRE.
- `image/png`, `image/jpeg`, etc. They're neither the best nor the worst PNG or JPEG encoders/decoders. But they're PNG and JPEG decoders, in the standard library, and the speed they perform at it seems acceptable, and they're memory safe. For doing any serious amount of image processing, I'd rather shell out to libvips, but there are a lot of use cases where it's tremendously nice that there's basic codecs like this in the standard library. Same for the compression algorithms in `compress` and the archive implementations in `archive`.
- The `go` package. Like most of the other packages, there's nothing terribly astonishing about this package. However, it does give you enough Go compiler guts to go ahead and parse and work with Go code. This is very useful because inevitably with large codebases you are going to want the ability to write accurate static analysis tools, make code that can do large tree-wide refactors, and other such tasks. For C++, you probably have little choice other than to use Clang's AST. For Rust, the closest I'm aware of is the `syn` crate, but I might be out of date here. These options generally require a great deal of effort to do even rather simple things, making it rather hard to get to the point of break-even for them, and I think that's not great. Some of this is definitely due to language complexity, but that is indeed a cost you have to pay multiple times. So it better be worth it!
- neonsunset 3y agoThe regexp being "not the fastest" might be an understatement: https://github.com/BurntSushi/rebar?tab=readme-ov-file#summary-of-search-time-benchmarks https://github.com/BurntSushi/rebar?tab=readme-ov-file#summa... Nothing that Go has is uniquely better or even competitive. Both C# and Java have way more extensive and optimized standard libraries. What Java has working against it is the lack of structs, monomorhpized generics and SIMD API to ensure all standard library bits stay as performant as they are in .NET, but nonetheless the areas that have no use of those are optimized to a comparable degree. And you can be sure they offer sufficiently good support on all major platforms: Linux, macOS, Windows and, what is a major Java/Kotlin's advantage, Android. Something that Go treats with pretending like the only platforms that exist is UNIX/POSIX. There are other things, like Go's blessed way of slicing strings being problematic because it does nothing to prevent you from tearing code points so you have a lot of code that might be silently corrupting text data. And for interpreting AST, this exists in both Java and C# with different strategies. C# approach is a bit more complex but extremely powerful with build-time source generators that have full access to AST generate new code, fill out existing partial members or intercept calls, and does not require any external scripting. A good example of that is generating gRPC clients and servers code from .proto file by simply executing `dotnet add package Grpc.Tools` and adding .proto reference to .csproj. Go may seem like a good language after coming from scripting nightmares, it may even seem like a good systems programming language that is easy to use. I promise, it is not. In order to call intrinsics, in C#, I just call them because that's what it offers, in Go, I have to write asm helpers manually. If you look at what modern C#, Kotlin and, again, Rust provide in their respective areas, you will soon realize that Go does not have anything to offer that is unique or better, except perhaps the culture of minimalism, which other ecosystems are also advocating for in at least last 5 years. p.s.: I forgot to mention the reason interop was brought up - in C# it can be zero cost or nearly zero cost because it is something that was considered at the language and platform inception. It is one of the reasons C# will never use green threads and eventually will move over upgraded runtime handled task system (mind you, current day state machine implmentation works well, but can be improved). The cost of FFI in Go, unless you use Cgo, is dramatic. This is something that is not acceptable for a language positioning itself as a systems programming one.
- 3y ago