At this stage to me Go is the new C compiler. Quite honestly I spent 10 years or more writing Go code. At times 18 hours a day. And now with AI, I get models to write my Go code. I STILL want it to be Go code. Is that strange? No because I understand the performance and safety of Go. But I rarely write it directly anymore. Probably not at all in 6-9 months. Yet I still occasionally look at the code to see what the output looks like.
So yea hopefully a nice new addition but only my prompts now matter. So need to make sure they pickup on the new API.
Yes. It's that thing — the fact that a language helps you read what a single function does really easily, but in so doing makes the entire codebase quite dense, is not optimal for us, but is good for them.
And the things that we do to manage this, that are symbolic or based on visceral real-world analogy, are good for us and bad for them.
Go is kind of in the middle, in the way that many vaguely Wirth-like and CSP-like languages possibly are, I guess.
As I do more agentic coding, I am starting to feel strongly that I need to set aside a project -- and time -- for strictly hand-coding, using AI (if at all) only to explain things, never to write any code.
First, I like programming and don't want to stop doing it just because a machine can do it too. Second, I'm pretty sure atrophied coding skills will make me worse at directing the machines, thus worse at what seems to be "the job" these days.
>First, I like programming and don't want to stop doing it just because a machine can do it too. Second, I'm pretty sure atrophied coding skills will make me worse at directing the machines, thus worse at what seems to be "the job" these days.
In theory wouldn’t atrophied coding skills not matter if models are sufficiently good at coding? I mean isn’t whats left just design and architecture and testing?
Sure, if LLMs are producing shit code you need to have an understanding of what it should do instead. But I think that day is already gone.
Its hard to think of a skill that you’d need to build with an LLM that requires writing meaningful amounts of code by oneself. From my end all the complexity is business logic and system design.
I still prefer Go over the alternatives because I'm reviewing the code carefully and trying to make it all look as good as if I'd written it very carefully by hand.
The few times I've needed a language without a GC and used Rust and C as target languages, I've reviewed the code less and enjoyed it less.
People are sleeping on Go as a LLM language of choice. Everyone trying to rewrite in rust which is fine and rust is fine. However its compile times are several orders of magnitude greater than Go. You can have LLMs write and compile 20 apps in the time one rust app compiles. I've got a Go personal project that would take hours for rust to compile, I can't imagine making the same velocity in Rust. Though I do see the workplace appeal of bringing back the xkcd "compiling" meme.
However it kind of makes me feel like my professional life was wasted and I don't really know what kind of future I have. One of Go's main points was you can pick it up in a weekend. What career moat do people like us have?
That is fighting the last war on the early days of 5 GLs, and taken care by agentic runtimes.
This isn't futurology, besides a few projects that already came up on Phoronix and around here, with Assembly generation, there are enough SIGPLAN papers on ML driven compilers already.
I think it'll bite some people, but probably is the better default for JSON specifically. Most of the nil-maps-become-null thing is just an extension of Go defaulting to nil for empty arrays and maps, which is usually not what you expect when you're writing a JSON schema like
type ListUsersResponse struct {
Users []User `json:"users"`
}
in Go. It's pretty common have code like
userRows := db.Query(...)
var users []api.User
for _, user := range userRows {
users = append(users, api.User{...})
}
where you don't _really_ intend for the default nil-ness to leak out. (Apologies for any broken syntax, I'm realizing in real time that it's been a while since I've written Go)
I'm working on better SIMD support in Dart and one of the motivations for that work is specializing parser specifications based on schema specifications of that language.
Or more concretely, if we had a parser generator + json parser specification that can generate efficient simdjson style parsers, then we should be able to parse json restricted to a subset of json based on a schema at least as efficiently as simdjson can parse general purpose json.
That way we could have the best of both worlds, a (potentially lazy) statically type-safe model and simdjson performance!
I still think that this is definitely doable, it's just that a lot of infrastructure needs to be built for it.
I remember reading that Meta is doing something similar with binary formats. I find it weird that there's nothing like that for json yet.
I was working on metaparsing earlier this year (specifically over grpc because of its better support for types and better serialization format).
I think most people who use JSON don't want strong typing or to use schemas. If you do, you just use a different serialization format: JSON is designed to be a generic, human-readable way to basically represent any kind of data structure (since it deserializes to JS objects). So everything about JSON is working against you if you do try to enforce type/schemas on it.
A grpc-like protocol with more prescriptive type registries/discovery (over the network) and better interop with http (like grpc-gateway) would be the best approach IMO. Definitely not JSON schema
I generally like `encoding/json/v2` and I'm glad they did it.
I wish they'd left `MarshalIndent` in there though. The equivalent code is super complicated and non-obvious, so it's a pain to migrate, and it's a pain when you're banging out some quick code and want to dump some readable JSON. I suppose these days the LLMs can write the complicated version for us, of course.
I'm just getting the neuron-band back together where Golang is concerned. I have done some in the past for little system tasks, but I am switching away from interpreted languages entirely for web stuff, and initially to Go, as I switch away from hand-coding to at least LLM-supported coding.
So I am a bit out of date on the pain points (I never really felt pain points when I last used it, because the jobs I was doing had very small, quite Go-friendly shapes).
The fact that this took 6 years to land stood out to me.
I'm not entirely surprised, and I'm glad the Go team is carefully considering their interfaces before launching. But 6 years seems like a lot of work to land a JSON API.
I think in the case of JSON, it was “good enough”. People who found this to be a problem would use one of the many 3rd party options.
I kind of like that the standard library doesn’t move fast. It makes it easier to fit the ecosystem into my head (unlike, for example, the JavaScript ecosystem where I find code doesn’t survive 2 years before failing to compile).
But I do agree that slow and steady produces its own problems. Your example of generics is a good one. But think the example of Go modules is another, perhaps even better, example of how sometimes the Go team really should have accelerated their efforts on.
> generics (something that I still haven't used beyond the tutorials) took an age as well
Only about a year from when the Haskell guys lent their expertise in designing it and it landing in a release. That's actually fairly quick given that it was a pretty significant change under the hood and constrained by only two releases annually.
The Go team was always clear that they didn't have the necessary capabilities internally, despite a lot of failed efforts trying anyway, and were relying on the open source community to lend a hand. Unfortunately, open source doesn't mean open willingness to contribute. It turns out that for all the experts on HN, posting "go doesn't have generics lol" was preferable to helping out.
the marshal api has been very annoying. why does marshal return an error? the only reason the marshal can return an error is that the input type is not marshalable. it's certainly a code bug. to me it's like int+str returning an error instead of compiler error
> why does marshal return an error? the only reason the marshal can return an error is that the input type is not marshalable
Not necessarily. Among other things, MarshalJSON methods called during marshaling can return an error for whatever reason the developer sees fit - one possibility that comes to mind is that part of the object tree being marshalled is lazily loaded, and failed to do so.
And, as others have mentioned, there are types like "any" whose marshalability can't be determined at compile time. Those types aren't necessarily at the top level, either; they could appear anywhere in the object tree.
This isn’t a type system problem. It’s a data origin problem. Ie it could be any random JSON blob a user entered. The compiler doesn’t know this ahead of time.
If you wanted type safety with JSON in Go then don’t use the marshal interface. Period.
For unmarshalling, but the original complaint was about marshalling. And for that the type system could help if the marshal API only allowed you to marshal objects of types that supported marshalling.
And even for unmarshalling you could get a compile time error if you try to unmarshall to a type that doesn't support it.
I’d already addressed that when I said the marshal API is an interface that’s meant to be reusable across generalise use cases. it can be used for a variety of data formats and not just JSON.
Go has this concept baked into the language. For example read and write interfaces so you can write data to an endpoint without having to worry about whether it’s a file system object, TCP/UDP socket, gzip achieve, and a plethora of other options too.
It’s a nice convenience feature and makes up considerably for the restrictions that the Go type system places.
And if you need higher performance code, or compile time safety, or have any other constraints that limit your ability to use these generalised interfaces, then you can use different APIs from the Go standard library to achieve the same thing.
You don’t need to call json.Marshal() if you don’t want to. It’s an option, but it’s just one API into the JSON toolbox.
First of all, the json2 Marshall API is json specific.
Secondly, other languages like rust, haskell, scala, etc. Have json APIs that are typesafe and give you compile time errors if you try to use a type that doesn't support (de) serialization . In the case of rust at least, the API is also general and can be used for other data types as well.
Even in go, you could have some more type safety if Marshall took an interface (such as MarshallerTo) rather than `any`. Although then you have the problem of how you implement that interface. So I guess you probably want some kind of "derive" functionality, or macros that can approximate it for a convenient and type-safe API.
P.S. gophers like to talk about how explicit being better than implicit, but using reflection for marshalling is very implicit.
i meant i hoped it's panic as opposed to return an error. if there was an error, how am i supposed to handle it? can i tell the user hey sorry my bad it's likely a code error? crash is better than possible eating the error siliently
It is trivial to ignore an error in Go if you know your use case cannot result in one. But at the end of the day, json.Marshal takes an `any` as a parameter, so it must return an error.
At this stage to me Go is the new C compiler. Quite honestly I spent 10 years or more writing Go code. At times 18 hours a day. And now with AI, I get models to write my Go code. I STILL want it to be Go code. Is that strange? No because I understand the performance and safety of Go. But I rarely write it directly anymore. Probably not at all in 6-9 months. Yet I still occasionally look at the code to see what the output looks like.
So yea hopefully a nice new addition but only my prompts now matter. So need to make sure they pickup on the new API.
This is mostly why I have come back to Go.
I am using local LLMs and Go has a bunch of strengths where smaller contexts are concerned; less complicated flow of control being one of them.
not a lot of overloading complexity, very verbose with a small total language structure too helps.
Yes. It's that thing — the fact that a language helps you read what a single function does really easily, but in so doing makes the entire codebase quite dense, is not optimal for us, but is good for them.
And the things that we do to manage this, that are symbolic or based on visceral real-world analogy, are good for us and bad for them.
Go is kind of in the middle, in the way that many vaguely Wirth-like and CSP-like languages possibly are, I guess.
Truly the argument can be made either way.
If you code in Go, the code is more high level, prettier, simpler.
If you code in Rust, the code is more robust, efficient, faster.
Both are important for LLM, why can't we have a high level, simple language that is also robust, efficient and fast? =)
> Both are important for LLM, why can't we have a high level, simple language that is also robust, efficient and fast? =)
I think many variants from Rust can be stripped to make it simple, especially in type system, which is way more complicated than go.
I dont think we need simpler types for agentic coding though. We need compiler speed. I guess those are probably related to some degree though.
Goose?
As I do more agentic coding, I am starting to feel strongly that I need to set aside a project -- and time -- for strictly hand-coding, using AI (if at all) only to explain things, never to write any code.
First, I like programming and don't want to stop doing it just because a machine can do it too. Second, I'm pretty sure atrophied coding skills will make me worse at directing the machines, thus worse at what seems to be "the job" these days.
>First, I like programming and don't want to stop doing it just because a machine can do it too. Second, I'm pretty sure atrophied coding skills will make me worse at directing the machines, thus worse at what seems to be "the job" these days.
In theory wouldn’t atrophied coding skills not matter if models are sufficiently good at coding? I mean isn’t whats left just design and architecture and testing?
Sure, if LLMs are producing shit code you need to have an understanding of what it should do instead. But I think that day is already gone.
Its hard to think of a skill that you’d need to build with an LLM that requires writing meaningful amounts of code by oneself. From my end all the complexity is business logic and system design.
> In theory wouldn’t atrophied coding skills not matter if models are sufficiently good at coding?
I am begging HN users to understand that some people enjoy writing software themselves
I still prefer Go over the alternatives because I'm reviewing the code carefully and trying to make it all look as good as if I'd written it very carefully by hand.
The few times I've needed a language without a GC and used Rust and C as target languages, I've reviewed the code less and enjoyed it less.
People are sleeping on Go as a LLM language of choice. Everyone trying to rewrite in rust which is fine and rust is fine. However its compile times are several orders of magnitude greater than Go. You can have LLMs write and compile 20 apps in the time one rust app compiles. I've got a Go personal project that would take hours for rust to compile, I can't imagine making the same velocity in Rust. Though I do see the workplace appeal of bringing back the xkcd "compiling" meme.
However it kind of makes me feel like my professional life was wasted and I don't really know what kind of future I have. One of Go's main points was you can pick it up in a weekend. What career moat do people like us have?
My only issue with it is that you can't ship a wasm go app in under 2mb. With rust you can make it under 100kb.
I would rather have LLM directly generate machine code, and best of all, macro Assemblers are blazing fast for decades.
You can't PGO that code, though. Also, I'm not sure it can generate architecture specific optimized (-march, -mtune) code as well.
Considering it just pulls and remixes blocks, this is a very tall order for today's LLMs, and maybe a couple of generations as well.
Calling GCC/clang at the back and emitting ASM is both cheating and has no advantages whatsoever as well.
That is fighting the last war on the early days of 5 GLs, and taken care by agentic runtimes.
This isn't futurology, besides a few projects that already came up on Phoronix and around here, with Assembly generation, there are enough SIGPLAN papers on ML driven compilers already.
> nil slices and maps are marshaled as an empty JSON array or object by default
I feel that is the kind of broken default that will come back and bite a bunch of people, and will eventually need to be fixed in a v3.
I think it'll bite some people, but probably is the better default for JSON specifically. Most of the nil-maps-become-null thing is just an extension of Go defaulting to nil for empty arrays and maps, which is usually not what you expect when you're writing a JSON schema like
in Go. It's pretty common have code like where you don't _really_ intend for the default nil-ness to leak out. (Apologies for any broken syntax, I'm realizing in real time that it's been a while since I've written Go)Can you explain why? This seems like the appropriate expectation to me, given how JSON is constructed and consumed.
I'm working on better SIMD support in Dart and one of the motivations for that work is specializing parser specifications based on schema specifications of that language.
Or more concretely, if we had a parser generator + json parser specification that can generate efficient simdjson style parsers, then we should be able to parse json restricted to a subset of json based on a schema at least as efficiently as simdjson can parse general purpose json.
That way we could have the best of both worlds, a (potentially lazy) statically type-safe model and simdjson performance!
I still think that this is definitely doable, it's just that a lot of infrastructure needs to be built for it.
I remember reading that Meta is doing something similar with binary formats. I find it weird that there's nothing like that for json yet.
I was working on metaparsing earlier this year (specifically over grpc because of its better support for types and better serialization format).
I think most people who use JSON don't want strong typing or to use schemas. If you do, you just use a different serialization format: JSON is designed to be a generic, human-readable way to basically represent any kind of data structure (since it deserializes to JS objects). So everything about JSON is working against you if you do try to enforce type/schemas on it.
A grpc-like protocol with more prescriptive type registries/discovery (over the network) and better interop with http (like grpc-gateway) would be the best approach IMO. Definitely not JSON schema
This approach is called "staged parsing":
https://dl.acm.org/doi/pdf/10.1145/2714064.2660241
https://dl.acm.org/doi/pdf/10.1145/3314221.3314625
I generally like `encoding/json/v2` and I'm glad they did it.
I wish they'd left `MarshalIndent` in there though. The equivalent code is super complicated and non-obvious, so it's a pain to migrate, and it's a pain when you're banging out some quick code and want to dump some readable JSON. I suppose these days the LLMs can write the complicated version for us, of course.
I'm not sure if I'm missing something, but isn't this functionality just moved into jsontext? https://pkg.go.dev/encoding/json/jsontext#Value.Indent
This is how you roll out features, C++. Congrats to Go team and users.
I… think this is good?
I'm just getting the neuron-band back together where Golang is concerned. I have done some in the past for little system tasks, but I am switching away from interpreted languages entirely for web stuff, and initially to Go, as I switch away from hand-coding to at least LLM-supported coding.
So I am a bit out of date on the pain points (I never really felt pain points when I last used it, because the jobs I was doing had very small, quite Go-friendly shapes).
The fact that this took 6 years to land stood out to me.
I'm not entirely surprised, and I'm glad the Go team is carefully considering their interfaces before launching. But 6 years seems like a lot of work to land a JSON API.
Sometimes I wonder about Go's velocity - generics (something that I still haven't used beyond the tutorials) took an age as well
JSON processing, quite markedly, is renown in Go for being slow, so I don't really understand why it took so long
I think in the case of JSON, it was “good enough”. People who found this to be a problem would use one of the many 3rd party options.
I kind of like that the standard library doesn’t move fast. It makes it easier to fit the ecosystem into my head (unlike, for example, the JavaScript ecosystem where I find code doesn’t survive 2 years before failing to compile).
But I do agree that slow and steady produces its own problems. Your example of generics is a good one. But think the example of Go modules is another, perhaps even better, example of how sometimes the Go team really should have accelerated their efforts on.
> generics (something that I still haven't used beyond the tutorials) took an age as well
Only about a year from when the Haskell guys lent their expertise in designing it and it landing in a release. That's actually fairly quick given that it was a pretty significant change under the hood and constrained by only two releases annually.
The Go team was always clear that they didn't have the necessary capabilities internally, despite a lot of failed efforts trying anyway, and were relying on the open source community to lend a hand. Unfortunately, open source doesn't mean open willingness to contribute. It turns out that for all the experts on HN, posting "go doesn't have generics lol" was preferable to helping out.
Haskell guys? Got a reference to this? That’s news to me.
I'm not sure about the haskell association, but search for "Featherweight Go"
> in any, out any
Completely unrelated: can anyone tell me why language authors introduce in/out/inout function parameters (see also C#).
It feels like this is some rather bad workaround for something that breaks assumptions about parameters being passed into the function.
C# `out` is kinda nice because it can be `var`d and used directly.
"in" and "out" are the names of the parameters, not qualifiers.
the marshal api has been very annoying. why does marshal return an error? the only reason the marshal can return an error is that the input type is not marshalable. it's certainly a code bug. to me it's like int+str returning an error instead of compiler error
> why does marshal return an error? the only reason the marshal can return an error is that the input type is not marshalable
Not necessarily. Among other things, MarshalJSON methods called during marshaling can return an error for whatever reason the developer sees fit - one possibility that comes to mind is that part of the object tree being marshalled is lazily loaded, and failed to do so.
And, as others have mentioned, there are types like "any" whose marshalability can't be determined at compile time. Those types aren't necessarily at the top level, either; they could appear anywhere in the object tree.
Types may be mostly marshallabble, have a subset of values that are unmarshallable. E.g. non-finite floats.
I wouldn’t class unmarshallable data as a compile time error.
Plus the point of the marshal interface is that it’s reusable.
It could be if go had a more powerful type system.
This isn’t a type system problem. It’s a data origin problem. Ie it could be any random JSON blob a user entered. The compiler doesn’t know this ahead of time.
If you wanted type safety with JSON in Go then don’t use the marshal interface. Period.
For unmarshalling, but the original complaint was about marshalling. And for that the type system could help if the marshal API only allowed you to marshal objects of types that supported marshalling.
And even for unmarshalling you could get a compile time error if you try to unmarshall to a type that doesn't support it.
I’d already addressed that when I said the marshal API is an interface that’s meant to be reusable across generalise use cases. it can be used for a variety of data formats and not just JSON.
Go has this concept baked into the language. For example read and write interfaces so you can write data to an endpoint without having to worry about whether it’s a file system object, TCP/UDP socket, gzip achieve, and a plethora of other options too.
It’s a nice convenience feature and makes up considerably for the restrictions that the Go type system places.
And if you need higher performance code, or compile time safety, or have any other constraints that limit your ability to use these generalised interfaces, then you can use different APIs from the Go standard library to achieve the same thing.
You don’t need to call json.Marshal() if you don’t want to. It’s an option, but it’s just one API into the JSON toolbox.
First of all, the json2 Marshall API is json specific.
Secondly, other languages like rust, haskell, scala, etc. Have json APIs that are typesafe and give you compile time errors if you try to use a type that doesn't support (de) serialization . In the case of rust at least, the API is also general and can be used for other data types as well.
Even in go, you could have some more type safety if Marshall took an interface (such as MarshallerTo) rather than `any`. Although then you have the problem of how you implement that interface. So I guess you probably want some kind of "derive" functionality, or macros that can approximate it for a convenient and type-safe API.
P.S. gophers like to talk about how explicit being better than implicit, but using reflection for marshalling is very implicit.
Then write yourself a MustMarshal wrapper that panics.
The GP is talking about compile time errors. You’re discussing exceptions, which is still a runtime check
A generic Must[T] you mean ;)
AFAIK it's mostly because of floats. JSON doesn't support `NaN` and `infinity` for example.
Because they are common types, makes sense to precautionary have the error return.
JSON is a runtime input of unknown type but in your example those are known types so the compiler can catch them.
i meant i hoped it's panic as opposed to return an error. if there was an error, how am i supposed to handle it? can i tell the user hey sorry my bad it's likely a code error? crash is better than possible eating the error siliently
do not know at compile time that input is not marshalable, every language marshal must return error for this reason, not only golang
It is trivial to ignore an error in Go if you know your use case cannot result in one. But at the end of the day, json.Marshal takes an `any` as a parameter, so it must return an error.