Hacker News
points by lolpanda 3 days ago | 0 comments
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
[–] duskwuff 3 days ago
> 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.

[–] sgsjchs 3 days ago
Types may be mostly marshallabble, have a subset of values that are unmarshallable. E.g. non-finite floats.
[–] bicarbonato 3 days ago
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.

[–] hnlmorg 3 days ago
I wouldn’t class unmarshallable data as a compile time error.

Plus the point of the marshal interface is that it’s reusable.

[–] thayne 3 days ago
It could be if go had a more powerful type system.
[–] hnlmorg 3 days ago
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.

[–] thayne 3 days ago
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.

[–] hnlmorg 3 days ago
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.

[–] thayne 3 days ago
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.

[–] hnlmorg 3 days ago
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.

[–] awesome_dude 3 days ago
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?

[–] thayne 3 days ago
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.
[–] hnlmorg 3 days ago
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.

[–] 0x696C6961 3 days ago
Then write yourself a MustMarshal wrapper that panics.
[–] jeremyloy_wt 3 days ago
A generic Must[T] you mean ;)
[–] hnlmorg 3 days ago
The GP is talking about compile time errors. You’re discussing exceptions, which is still a runtime check
[–] 0x696C6961 3 days ago
Their complaint is about the signature. The comment about the compile time checking was a misguided justification for their position.
[–] leetrout 3 days ago
JSON is a runtime input of unknown type but in your example those are known types so the compiler can catch them.
[–] lolpanda 3 days ago
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
[–] iaaan 3 days ago
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.
[–] NoDodgeQuestion 3 days ago
do not know at compile time that input is not marshalable, every language marshal must return error for this reason, not only golang