upvote
Fair criticism, it's my main gripe with Go as well - it's not strict enough when it comes to e.g. nil, enums, and type safety. Annotations are supported but they're just strings. Projects require additional tooling / linters to check for things like unchecked errors and many more "gotchas" that I think could (should?) be part of the compiler or standard tools. Trivial example, Go's compiler will error when you have an unused variable, but won't if you reuse and overwrite an error variable a dozen times and only handle it once.

But on the other hand, I suppose it makes it a bit more pragmatic - less checks makes for a faster compiler, and fast compilation was/is very high up in the language's requirements and motivation. If you want / need more strictness in your language, there's Rust, Java, C#, etc.

reply
i never run into these issues in >100k loc of productionized llm generated elixir (nil safety, type issues). i wonder, is there something architecturally in go that makes this a particular problem?
reply
How about Swift and Erlang? Why are these always left out of comparisons?
reply
Haven’t you heard? Swift is the first-ever corporate programming language, and we on HN can’t take its community efforts seriously to the extent of considering its potential value in general-purpose software.

/s

reply
Good point.

It's sometimes challenging to get a Rust program to compile... but if you do, it's probably going to work.

reply
The authors make some good points: compile time and test speed matter, platforms matter. But they really dodge the whole “guardrails matter” thing. And guardrails are going to win long term.

As for readability, the fact that AI-written Go closely resembles human-written Go is not necessarily a point in Go’s favour.

reply
> "guardrails matter" This thing has been solved in the 70s. Basically, have a powerful type system, and a compiler that beats you into submission when you try to stray from the straight and narrow. Languages like (S/OCa)ML, Haskell and Rust will bring this to you. As a consequence, you fight the compiler, and once it submits, you have a good chance it's going to work. The compiler is also the ultimate refactoring tool here. Change the concept? Just change the type in the code and follow through by fixing the error messages the compiler spits out.
reply
> As for readability, the fact that AI-written Go closely resembles human-written Go is not necessarily a point in Go’s favour.

There's just not many ways of writing Go. It's a very dull language. It was designed to be dull and easily understandable.

reply
but there are a whole lot of ways you can mess up a dull language, like shitty variable names, bad code organization, etc.

if an llm has learned dumb things from dumb users it could disproportionately cause provlems versus other languages, just by being "in a sloppy mood" when writing go.

reply
Not panicking doesn’t mean working as intended, the biggest issue with LLM generated code is that it will do something that’s subtly wrong not that it will crash. It anything LLMs are too careful with Golang code and litter useless nil checks everywhere e.g. for function calls with pointer receivers. That whole fear of panics is totally overblown.
reply
And by that point a program written in go has been deployed and making money for months. These are two wildly different languages, people should stop comparing them as if they're targetting the same niche. Checks and protections that rust has aren't necessary in most cases, but increase development and maintenance time.
reply
Do you have some evidence of that? I use both daily and my experience has been the opposite, if anything. Once I was as proficient at Rust as I was at Go, the "increased development and maintenance time" disappeared completely.
reply
I've found that keeping very healthy test coverage solves issues like this. LLMs are very good at validating their own implementations with TDD.
reply
i think they validate less with TDD; if you tell them a bare bones spec to validate they tend to write just that. if you write the test after, then their testing is causally conditioned on what was written. if you are going to write tests, it seems like teat first is way better than test last.
reply
With unit tests you gotta be careful though, oftentimes LLMs skip implementations with mockups that just say "not implemented yet" or similar and then the unit tests become pointless because they start to only test internal structures for being set / not default values.

For me it helped a lot to try to make containerized end-to-end tests and a custom TestMain for this, where I am using podman to run the integration tests. This way the end-to-end tests are forced to be on network level, and you can test protocol and API quirks much easier with LLMs.

Also, never forget to write a bootstrapping docs/ folder so that you don't have to re-explain these things all the time.

reply
With test, which LLM are good at writing, you can reduce this tremendously. Even Rust won't protect you in this case, remember the cloudflare outage. At the end it is not the language, it is you intrinsic ability to architecture well your software from there any LLM can do the work.
reply
Very good point, thanks for confirming that! What is the reasoning behind the Go design choices you described?
reply
i am using nilaway from fb it's great for those
reply
It's from uber, I believe.
reply
So your problem is the default/zero values of properties?

In Go the convention is kind of to have a constructor pattern with a NewStruct(...) *Struct method that initializes all properties.

Also can't you build your own validator for that with the reflect package in the Add() method of your UI graph to prevent this sorta thing?

reply
> the convention is kind of to have a constructor pattern with a NewStruct(...) *Struct method that initializes all properties

But that doesn't stop you from declaring a var s Struct, and never initializing it, or making a NewStruct {}.

> can't you build your own validator for that with the reflect package in the Add() method of your UI graph

Besides the fact that that would almost certainly significantly hurt performance, how would you be able to differentiate between unitialized data and data that was intentionally set to the zero value?

reply
> But that doesn't stop you from declaring a var s Struct, and never initializing it, or making a NewStruct {}.

Static analysis tools can catch this, no?

reply
The Go std library, as well as practically all go library code, is full of things that don't fully initialize all properties and things that nil-pointer-panic if you hold them wrong, so no, no matter what you do you have to deal with this wart of Go.

The go type-system is simply incapable of enforcing nil-safety without being no longer able to compile the go stdlib nor most code in the wild, so it's a quite valid criticism of the go type-system and language, and your comment doesn't hit on a valid solution.

reply
You're painting a picture where people writing Go are constantly drowning in nil pointer panics. This is not reality. You hit them occasionally and they're trivial to understand and fix.
reply
nil pointer panics aren't nearly as bad as values getting zero initialized, then used in places that assume they were initialized, and getting subtle bugs because the state is inconsistent.
reply
deleted
reply