Hacker News new | ask | show | jobs
by shabbyrobe 453 days ago
IME this is a longstanding pain point with Go. There's an attempt to propose an encoding/json/v2 package [1] being kicked around at the moment [2], spawned from a discussion [3].

This at least seems to improve the situation of marshalling to/from an interface directly slightly by providing the ability to pass custom Unmarshalers for a specific type (via json.WithUnmarshalers and json.UnmarshalFunc) to the Unmarshal functions, but it appears to still have the inefficient double-decode problem. Or I just haven't found a decent way around it yet.

Looks like they're intentionally punting on a first class solution until (if) the language gets some sort of sum type, but I still think the second-class solution could do a bit more to make this extremely common use-case more convenient. Pretty much every serious production Go app I've worked on in the last 10 years or so has had some horrible coping strategy for the "map a field-discriminated object to/from implementations of an interface" gap, often involving some sort of double-unmarshal.

Quote from the proposal [1]:

> First-class support for union types: It is common for the type of a particular JSON value to be dynamically changed based on context. This is difficult to support in Go as the equivalent of a dynamic value is a Go interface. When unmarshaling, there is no way in Go reflection to enumerate the set of possible Go types that can be stored in a Go interface in order to choose the right type to automatically unmarshal a dynamic JSON value. Support for such use cases is deferred until better Go language support exists.

  [1]: https://pkg.go.dev/github.com/go-json-experiment/json
  [2]: https://github.com/golang/go/issues/71497
  [3]: https://github.com/golang/go/discussions/63397
4 comments

> IME this is a longstanding pain point with Go.

Understatement of the year. But it’s really not limited to encoding but generally lack of sum types is excruciating after having tasted them (in Rust, in my case). They click instantly as an abstraction and they solve countless real-world logic bugs. Not to mention their ergonomics in seemingly unrelated things like eliminating null and error handling with result types. Just sprinkle some pattern matching on top and you’re in paradise.

Last time I checked, the json/v2 package fixed the double decode problem by passing the decoder into the unmarshaling callback.
That's not what I found in my own experiments, I still had to unmarshal once inside the callback to get the `type` field out, then again once I knew what the type was. Do you have an example handy?
> until (if) the language gets some sort of sum type

Is there any discussion with the Go team about this actually happening?

I don't know about recently, but people were asking about them from the first announcement in 2009; and got the answer that they were "under consideration"

To be fair, it's a significant advantage of go that they have been strict about keeping it's feature set small.

Edited to add:

There is this 2023 proposal from Ian Lance Taylor (on the go team) https://github.com/golang/go/issues/57644 But it makes all sum types include nil, which seems suboptimal

Well, he advocated for and eventually got generics, so it could happen here too.
The weird JSON handling was the main reason I stopped using Go for side projects long ago.