Hacker News new | past | comments | ask | show | jobs | submit
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.

Except it’s not JSON specific. The article literally talks about how their function design was chosen for similarity with the v1 API. And I’d wager that the go team are planning on reusing that function signature for other encoders too. Or at least expecting it to be reused by the wider ecosystem.

If you want type safety then pass a struct to the marshal. Which is actually the idiomatic way to do it anyway. The type safety would be in that.

I’m not going to disagree with you that Gos type system is a rather blunt tool nor that Gos expressiveness is extremely rigid compared to many other popular languages. All of that is completely true. But I don’t agree that the v2 JSON API is a good example of that problem.

I'm confused by this - you're saying that a "type system" would address this, even though there are two (incompatible) type systems at play here - Go's and JSON's

Compile time cannot check for runtime values (not even in Rust) - is your position that fields can only be populated at compile time?

In go, the Marshall and unmarshal functions take `any` for the source or destination. If you use a value of a type that doesn't support marshaling or unmarshalling (say because it contains an unserializable type like a channel, function pointer, etc) then you get a runtime error. Whereas in rust, Haskell, scala, etc. you can get a compile time error if you try to use a type that doesn't support serialization.
You don’t get runtime errors for marshalling pointers to JSON. I actually do this all the time. What you get is the address of the pointer marshalled.

I think the same is true for channels too but I’m not near a computer to check.

I’ve abused the v1 API heavily* and haven’t found anything it couldn’t marshal. Though I’m yet to apply the same “fuzzing” (for lack of a better term) to v2.

* the v1 JSON marshaller underpins some core functionality in my alternative $SHELL. So I’ve thrown a lot of exotic type structures (by Gos standards at least) at it.