A new Go API for JSON
Posted by olexsmir 1 day ago
Comments
Comment by modulovalue 1 day ago
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.
Comment by lightandlight 1 day ago
Comment by weitendorf 1 day ago
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
Comment by tcfhgj 1 day ago
I have been using serde without issues, and never resorted to `serde::Value`[0], the untyped option, so far.
I have yet to experience anything working against me.
[0]: https://docs.rs/serde_json/latest/serde_json/enum.Value.html
Comment by thayne 1 day ago
Not necessarily. It's biggest advantage is probably that it has very good interoperability. Basically every language has a readily accessible library for json, and even if you don't, it isn't that hard to write a parser and/or generator for it. And being human readable isn't something to sneeze at either.
Comment by ahtihn 1 day ago
We use JSON because it's standard, human-readable, mostly self-described and works natively with Javascript.
You can read JSON object without having to know the schema even if one exists.
Protobuf is unusablr garbage without a lot of supporting tooling.
Comment by tclancy 1 day ago
Comment by gwbas1c 1 day ago
Pretty much anywhere I've come across JSON (and xml before that,) everyone expected strong typing. The fact that JSON (and xml) can represent different data than they anticipated "did not compute" in their heads.
In many cases, they were treating JSON (and xml) as dumb serializers and not even paying attention to the output and corner cases.
Comment by dankobgd 1 day ago
Comment by CamouflagedKiwi 1 day ago
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.
Comment by athorax 1 day ago
v := jsontext.Value(b)
v.Indent()
fmt.Println(string(v))Comment by irq-1 1 day ago
func WithIndent(indent string) Options
https://pkg.go.dev/encoding/json/jsontext#WithIndentComment by arccy 1 day ago
json.Marshal(v, jsontext.WithIndent(" "))
?Comment by lifeisloving 1 day ago
Not an insult to you, its just how I feel things are going, myself inculded at times.
Edit: why do yall downvote this lol, you know its true.
Comment by txomon 1 day ago
There is no introspection and speed is superb. I would definitely recommend jsontext as a parser for anything non-straightforward.
Comment by dofm 1 day ago
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).
Comment by kvemkon 1 day ago
https://news.ycombinator.com/item?id=44893833
Still searching the true solution:
https://github.com/golang/go/issues/27179#issuecomment-53634...
A proposal of the missing datatype for this operation mode has been just accepted:
Comment by cbarrick 1 day ago
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.
Comment by awesome_dude 1 day ago
JSON processing, quite markedly, is renown in Go for being slow, so I don't really understand why it took so long
Comment by Philip-J-Fry 1 day ago
Within our company I saw people in other teams writing their own optimised JSON libraries for whatever hellish reason. I always reiterated the point that as soon as that app made a single IO request all those nanosecond savings are invisible. Yes, it affects your total CPU time for processing a request but compute was never ever a limitation for us and it's likely not a limitation for most people. And if the trade off is your app needs scale slightly larger versus writing custom JSON code or pulling in a third party dependency, then I'm accepting the larger scaling.
The biggest benefit of encoding/json/v2 isn't the speed, it's the improved API.
Comment by hnlmorg 1 day ago
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.
Comment by awesome_dude 1 day ago
Dependency management is a freaking nightmare, nobody has managed to create something that is bulletproof, not cargo, npm, apt, yum, nix, homebrew, nothing.
The key issue is that you're managing 3rd party software that may or may not adhere to the prescribed lifecycle/versioning
The Linux variants do it by each having dedicated teams watching upstream sources, taking what they believe to be whatever they feel is right, building for their nominated target(s), then testing, before deploying (on top of that, all of these steps vary in importance and rigor depending on numerous variables)
For programming libraries (which is what depmod, goose, vgo, and modules are managing) it's just as bad, if not harder, because their very nature is to provide a base upon which other developers can turn into whatever their imagination.
Add to that, modules was (initially) written by people who were dealing with a slightly different ecosystem; Google rather famously has a monorepo, making their lives a little easier when it comes to managing dependencies.
So, with those things in mind, dependency management is always going to be a hard problem that is opinionated (eg. I have a firm opinion that there should be an easier way to "pin" a dependency, other than vendoring - note that this isn't an invitation to every junior Go developer to come and claim go.sum does that. It doesn't. go.sum is a log of what dependencies were used for the last build by whomever wrote to it)
Comment by hnlmorg 1 day ago
But difficulty aside, my point was Go waited until there were multiple established 3rd party package management tools for Go already in widespread use before even releasing their own experimental implementation.
Their inaction caused far more disruption to the ecosystem because people got fed up of waiting, implemented their own, integrated it into their workflow and then had to reintegrate a whole new system all over again a year or two later.
Your point about Googles needs is the one often cited for Gos inaction. But I think that actually makes the situation worse. Because if the Go team cannot empathise with use cases outside of one bespoke enterprise, then it really shouldn’t be released as a general purpose language available for all to use.
And I say this as someone who generally likes the language.
Comment by Mawr 1 day ago
Especially since "Go team cannot empathise with use cases outside of one bespoke enterprise" — it shows self-awareness of their shorcomings. I think it's wise to let the target audience explore the design space themselves a bit first. Considerably lowers the risk of cementing the wrong design.
I do somewhat remember the drama around this at the time, but I my impression was that of a petulent community of various dependency management projects with too much ego attached to them to see the bigger, long-term picture.
Comment by hnlmorg 19 hours ago
Thanks for sharing.
Comment by kbolino 1 day ago
Put this in your go.mod:
replace example.com/package-to-pin => example.com/package-to-pin v1.2.3
Then go.sum will do its actual job (along with GOSUMDB) and ensure you only get exactly the revision of the code that you want.Comment by cxr 1 day ago
You left out the part that explains why.
Comment by nonethewiser 1 day ago
Comment by throwaway894345 1 day ago
Comment by pjmlp 1 day ago
Wordpress powered by PHP is also the most widely deployed CMS.
That argument is not as great as you think it is.
Comment by throwaway894345 1 day ago
> That argument is not as great as you think it is.
But at least it is an argument in contrast to the comment I was replying to.
Comment by win311fwg 1 day ago
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.
Comment by pjmlp 1 day ago
https://homepages.inf.ed.ac.uk/wadler/topics/gj.html
Before Featherweight Go, there was Featherweight Java.
So maybe a language designed in 2012 (v 1.0 release year), should learn from history instead of hand-waving it away as needless fluff.
> In retrospect, we were biased too much by experience with C++ without concepts and Java generics. We would have been well-served to spend more time with CLU and C++ concepts earlier.
https://github.com/golang/proposal/blob/master/design/go2dra...
And not all on the team were convinced even after Featherweight Go, here Rob Pike,
> I think the transition to getting polymorphism into the language through generics is has still got some years to work through there's still things that don't quite work right
Comment by win311fwg 1 day ago
If you go back and watch the original Go announcement it was made abundantly clear that they believed Go did need generics but that they didn't have the expertise to do it. Yes it is true that the book was already written on it, but that doesn't mean anyone on the Go team was familiar with it or had the time to become familiar with it. Programming languages do not require gods to design them. Imperfect humans can too.
The experts on HN who know all the ins and outs of generics could have contributed but, you know, shitposting "go doesn't have generics lol" was a better use of their time. There are so many hours in the day.
Comment by pjmlp 1 day ago
Go culture is not welcoming to experts contributions, Rust, D, C#, Swift and others, heck even Java, are more open arms to language designers.
Comment by win311fwg 1 day ago
You'd think one of the HN experts would have shown up to offer that expertise, but I guess there was a lot of "go doesn't have generics lol" needing to get out.
Comment by pjmlp 1 day ago
The Haskell muscle had proven their skills already with Featherweight Java, in 2006.
Comment by win311fwg 1 day ago
But obviously "go doesn't have generics lol" comments on HN were even more important. Just think of what would have happened if that wasn't repeated multiple times daily for years and that effort was put into getting Go generics into place much sooner. Count your lucky stars or something.
Comment by pjmlp 1 day ago
Comment by win311fwg 1 day ago
Comment by pjmlp 1 day ago
Comment by pwdisswordfishq 1 day ago
Comment by kbolino 1 day ago
Comment by enneff 1 day ago
Comment by 0x696C6961 1 day ago
Comment by enneff 1 day ago
Comment by asim 1 day ago
So yea hopefully a nice new addition but only my prompts now matter. So need to make sure they pickup on the new API.
Comment by dofm 1 day ago
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.
Comment by calvinmorrison 1 day ago
Comment by dofm 1 day ago
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.
Comment by egnehots 1 day ago
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? =)
Comment by Vaushite 1 day ago
Go is about as far as you can "go" to the efficiency end without becoming Rust. It's a low-overhead language (like C) but with a GC that ensures memory safety. If you want to manage memory yourself and still prove safety it gets very complicated, as we see in Rust.
Comment by satvikpendem 1 day ago
Comment by jimbokun 1 day ago
Expressive and type safety of a functional language, without the odd semantics of Haskell, where laziness can make performance difficult to predict and purity can make simple things like IO difficult to understand.
Does it still lack easy to use concurrency primitives?
Comment by konart 1 day ago
I' just starting OCaml, so can't really compare, but it seems a good progress there.
Comment by andriy_koval 1 day ago
I think many variants from Rust can be stripped to make it simple, especially in type system, which is way more complicated than go.
Comment by nonethewiser 1 day ago
Comment by andriy_koval 1 day ago
LLM can generate very cryptic rust code.
Comment by TheChaplain 1 day ago
Comment by rTX5CMRXIfFG 1 day ago
Comment by jimbob45 1 day ago
Comment by coldtea 1 day ago
Comment by procaryote 1 day ago
Comment by alexfortin 1 day ago
It's really great fun to build stuff in Crystal, I don't have anything prod-grade yet to say how it scores there but the community seems to be happy about that too.
Comment by ksec 1 day ago
Comment by alexfortin 1 day ago
Comment by jimbob45 1 day ago
Comment by girvo 1 day ago
Comment by metaltyphoon 1 day ago
Comment by mfru 1 day ago
Comment by dankobgd 1 day ago
Comment by BigTTYGothGF 1 day ago
I hope you're not still doing this, it's nothing to brag about.
Comment by vikramkr 1 day ago
Comment by jimbokun 1 day ago
Comment by vikramkr 20 hours ago
Comment by TheDong 1 day ago
Unfortunately, the world makes it hard to keep that cycle, and so instead I mostly do 17 hours awake / 7 asleep, which makes me feel constantly tired (though less tired than 16/8 at least).
Comment by sellmesoap 11 hours ago
Comment by jacobgold 1 day ago
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.
Comment by hoppp 1 day ago
Comment by pjmlp 1 day ago
JIT and AOT compilers, REPL development, bleeding edge GC implementations, Java, Scala, Kotlin, Clojure, C#, F# to chose from, and modern IDEs inspired by Smalltalk/Lisp Machines experience, batteries included backed and frontend development frameworks.
Comment by biztos 1 day ago
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.
Comment by nonethewiser 1 day ago
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.
Comment by cyphar 1 day ago
No, because good understanding of design comes from programming experience. Atrophied skills mean you lose touch with the actual nuts and bolts of what you are building.
This is also the worst kind of lock-in you can imagine, by outsourcing all of your understanding of a codebase and your skills you are now completely at the mercy of LLM providers. Even if you switch to open weight models you're at the mercy of graphics cards producers and scalpers.
> 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.
Maybe your standards are too low. By my standard, they still produce shit code and require lots of iterations to get reasonable designs (I often end up just rewriting the bits that are obviously wrong rather than trying to figure out how to "prompt engineer" the right output).
They do often produce passable code but my experience is the longer you spend reading the code the more things you find wrong with it, almost like an uncanny valley.
At least that's my experience with Opus and Fable. Maybe it's too old-fashioned at this point to complain about code quality, but I find it very strange how many people have started proclaiming that their code quality is good.
Comment by nonethewiser 1 day ago
“Programming” is a bit loaded. Do you mean thinking through and typing everything out yourself?
What design skills specifically can you only develop from continually programming in this way? I fully grant that you need to know how to code yourself to build things with models but what specific ability to use models atrophies alongside your no AI coding abilities?
Personally I’ve found the opposite to be true. As writing out code myself has shrank to nearly 0, I now do way more designing. There is a much shorter feedback loop on design now. You get a lot better at design when thats basically all you are doing, and seeing feedback on your design in minutes rather than weeks or even months.
Comment by skydhash 1 day ago
Weeks and month for to wait for a feedback in developing software? Are you the sloth in zootopia?
The longest cycle for feedback for me has only been hours. And that was about redesigning the core architecture of an app. Otherwise, it’s like 10 minutes cycle if it’s edit-compile-run, 5 minutes if it’s REPL (lisp, bash, python,…).
Comment by Mawr 1 day ago
As I write code myself I continously take into consideration stuff like following the consistency of the entire design, whether what I'm doing is efficient, etc. LLMs are terrible at this and I don't see how you could develop such skills by prompting only.
For example, I would never produce designs that require degenerate db access patterns (linear scans, etc.). Everything I write myself always takes into account using db & network efficiently from the ground-up. Every query is an index lookup, every api request is rate limited, everything is batched and cached, so on and so forth.
LLMs meanwhile don't care about any of this. I just had Claude happily try to fetch youtube page html because the api didn't expose something. Great idea, I'd love to be banned by google for scraping.
Designing with performance in mind is black magic too, I need to notice myself that stuff that I think has no reason to be slow (pretty much nothing does) is slow and hammering the llm to finally come up with a non-stupid design that's not ridiculously slow and wasteful. Yeah, why don't we pull 2.3mb of html to update a list, sounds good.
The best part is, LLMs are very obedient. If you tell them to make you a design that's inherently wasteful of resources and slow they will happily oblige, and since you have no programming experience to know better, you won't even know how bad what you're asking for is.
Comment by swatcoder 1 day ago
You don't expect everyone to stop wanting to do those just because they can be automated more or less effectively, right?
Comment by nonethewiser 1 day ago
Please reread my comment and the one im replying to. That’s not what I’m addressing.
Comment by xgulfie 1 day ago
I am begging HN users to understand that some people enjoy writing software themselves
Comment by nonethewiser 1 day ago
Comment by nextaccountic 1 day ago
Comment by queenkjuul 1 day ago
Comment by lkirkwood 1 day ago
Comment by nilamo 1 day ago
Comment by queenkjuul 1 day ago
Agents make it too easy to gloss over too much. They're great at work where i care about productivity but I've zero interest at home where the point is learning and having fun
Comment by c0wb0yc0d3r 1 day ago
Why? Because I don’t give time to wait for code to compile on my time and I don’t have time for people to learn a new language for review at work.
Life is good! Would recommend that split.
Comment by osigurdson 1 day ago
In what sense? Go has a GC and binary sizes are relatively pretty huge. Its closer to Java and C# than C.
Comment by da02 1 day ago
Comment by citizenpaul 1 day ago
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?
Comment by simlevesque 1 day ago
Comment by xarope 1 day ago
Comment by groundzeros2015 1 day ago
Comment by citizenpaul 21 hours ago
Comment by groundzeros2015 12 hours ago
Binary bloat is not just about size; it impacts startup time and icache. It’s basically the speed of light launch time.
So you’re telling me rust is not good for UNIX style tools.
Comment by pjmlp 1 day ago
Comment by dgb23 1 day ago
Letting it generate machine code directly would be the opposite of that.
Comment by pjmlp 1 day ago
As for proven wrong,
"Frame was created over the past month largely via Claude Code. The Assembly code in all its glory can be found on GitHub."
Comment by bayindirh 1 day ago
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.
Comment by pjmlp 1 day ago
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.
Comment by bayindirh 1 day ago
However, starting with LLMs directly generating machine code and moving to ML directed optimization passes, you're moving the whole stadium with a crawler-transporter, not only the goalposts.
Helping compiler in a well defined way, and working on IR or driving it repeatedly to optimize the output is one thing, generating machine code from a training set is other. Let's not mix them.
Comment by pjmlp 1 day ago
"Compiler 2.0: Building the Next Generation of Compilers with Machine Learning"
https://eecs.berkeley.edu/research/colloquium/compiler-2-0-b...
There are other researchers if you interest yourself on the matter.
Then the whole iPaaS agentic coding environments like Workato and Boomi, that even if they might generate 3GL as intermediate step, you don't get to see it.
Comment by bayindirh 1 day ago
https://github.com/zwang4/awesome-machine-learning-in-compil...
Using small, purpose built ML "things" to make things better makes sense. We use them everywhere. I never argued against it, as well.
Comment by kibwen 1 day ago
Comment by pjmlp 1 day ago
Enterprise applications are already being vibe coded in iPaaS platforms, and as mentioned in other comments with LLMs in the picture, that is what compiler research is switching to.
Comment by kibwen 1 day ago
Comment by pjmlp 1 day ago
In case you answer there don't fit the same purpose, there are tiny baggers that replace shovels altogether.
I think those arguing for structured language generation, never had that much experience with 4GLs or declarative programming on the previous AI Summer.
So like the Assembly developers required those -S compiler switches, or nowadays Compiler Explorer snippets, there is a tendency to argue for 3GL code generation, when low code/no code tools with AI runtimes are already being adopted at enterprise level.
What in the past took a bunch of developers using structured programming languages to write their frontend, and the myriad of microservices, is slowly being taken over by MCP tools, SaaS AI orchestration prompts and UI generation tooling, most of which doesn't even expose whatever code gets generated.
Comment by politician 1 day ago
Comment by euroderf 1 day ago
Comment by sneak 1 day ago
Comment by troupo 1 day ago
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.
Comment by hxtk 1 day ago
If the allocation is owned by the calling function which uses it internally and then doesn’t return it, it can be a stack variable. Or the same allocation can be reused many times in a loop. Or an array of allocations can be made all at once with foreknowledge of the total number.
Comment by throwaway894345 1 day ago
Comment by ahmedtd 1 day ago
Comment by jayd16 1 day ago
But you might be conflating that with in/out for Contra/Co-variance when specifying a generic.
Comment by metaltyphoon 1 day ago
They are one way to implement multiple return values. For multiple returns, tuples can be used. In fact, async methods can’t declare out params
Comment by duskwuff 1 day ago
Comment by CharlieDigital 1 day ago
Comment by troupo 1 day ago
Comment by metaltyphoon 1 day ago
if (int.TryParse(str, out var value) && value == 5)
{
// value is available
}
// value also availableComment by rednafi 1 day ago
This pragmatic attitude towards software engineering is exactly why I still reach for Go for pretty much everything these days. I still read and review code.
So working with a language that doesn't throw a splash of sigil soup on my face is quite nice while reviewing clanker output. Also, being able to do all this without needing 5 crates is nice. Oh the fast build time helps too.
Comment by thiht 1 day ago
I love it too, it’s a great way to keep the stdlib moving without actually breaking anything. Simple and efficient design.
Comment by wiseowise 1 day ago
Comment by gosen 1 day ago
Comment by hoppp 1 day ago
I'm happy it's getting fixed!
Comment by lolpanda 1 day ago
Comment by duskwuff 1 day ago
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.
Comment by sgsjchs 1 day ago
Comment by bicarbonato 1 day ago
Because they are common types, makes sense to precautionary have the error return.
Comment by hnlmorg 1 day ago
Plus the point of the marshal interface is that it’s reusable.
Comment by thayne 1 day ago
Comment by hnlmorg 1 day ago
If you wanted type safety with JSON in Go then don’t use the marshal interface. Period.
Comment by thayne 1 day ago
And even for unmarshalling you could get a compile time error if you try to unmarshall to a type that doesn't support it.
Comment by hnlmorg 1 day ago
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.
Comment by thayne 1 day ago
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.
Comment by hnlmorg 1 day ago
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.
Comment by thayne 22 hours ago
The function is in the `encoding/json/v2` package. It might have a similar design, and maybe they will use the same function signature for other encoders, but it the functions are very much json specific.
> If you want type safety then pass a struct to the marshal.
But if you use a struct that has a field that can't be marshalled, you don't get a compile error, you get a runtime SemanticError. That's the problem.
Comment by hnlmorg 20 hours ago
But the point you keep missing is that the function signature needs to be encoder agnostic for it to work as an interface. That’s a hard requirement.
And since interfaces are a convenience to allow common abstractions to be reused at runtime, you cannot depend on things like generics (ie compile time abstractions) to generalise the data types for disparate serialisation formats.
I know this limitation well because I’ve built a shell with a typed pipeline and support for different serialisation formats. So make heavy use interfaces to achieve this. And I’ve spent a lot of time investigating alternative approaches. Each time, I’ve found interfaces to be a massive advantage despite any tradeoffs I end up making do to generalising a “class” (to borrow a term from OOP).
I’d honestly love to have a more sophisticated type system in Go. But that’s not a limitation in this specific instance.
> But if you use a struct that has a field that can't be marshalled, you don't get a compile error, you get a runtime SemanticError. That's the problem.
My point here was: if you know you’re creating a struct to pass to the mashaller at compile time, then you’re already going to use JSONAble fields. So it’s not an issue.
And if you don’t know what you’re passing to the mashaller a compile time, then you’re dependent on runtime checks regardless of the expressiveness of Gos type system.
I don’t disagree that Gos type system is inflexible. But in respect to this specific use case, your argument is a storm in a teacup. The type system isn’t a limiting factor here.
Comment by thayne 9 hours ago
It's a function, not a method. So it can't be part of an interface.
I suppose you could make an argument for passing it as a function argument to a higher ordered function. But that is kind of an unusual way to use such a function. And isn't a common pattern in go.
> My point here was: if you know you’re creating a struct to pass to the mashaller at compile time, then you’re already going to use JSONAble fields. So it’s not an issue.
Maybe. Maybe not. For a simple struct where all the fields are primitive types sure. But if you have a complicated hierarchy, you might miss that some strict in the hierarchy has a field that can't be marshaled.
Say you are marshaling a struct that contains a struct from a third party library, and it works fine. Then you update to a new version of that library, and that added a new field for a type that doesn't work, say a map[A]string where A is a struct. Your program still compiles fine, but then you get an error at runtime when you try to serialize something that worked before. Hopefully you have tests that catch it.
Comment by hnlmorg 8 hours ago
The mashaller here is a function, yes. But having a consistent signature means you can interchange those marshallers. It’s a trick I use heavily in murex to enable typed pipelines.
And I do this via a pipe interface. So it’s not uncommon to reuse function signatures for interface types (fewer runtime abstractions) while retaining some level of dynamism.
> Say you are marshaling a struct that contains a struct from a third party library, and it works fine. Then you update to a new version of that library, and that added a new field for a type that doesn't work, say a map[A]string where A is a struct.
The first mistake there is to blindly forward an internal API struct that you don’t control, and depend on that business logic.
Which comes back to my point about if you want type safety then you’d create a struct to be marshelled rather than passing data you don’t control.
I do take your point that the compiler in smarter languages would catch this. But you’d equally be told not to do this sort of thing on those languages too. So it feels like a moot point.
Comment by awesome_dude 1 day ago
Compile time cannot check for runtime values (not even in Rust) - is your position that fields can only be populated at compile time?
Comment by thayne 1 day ago
Comment by hnlmorg 1 day ago
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.
Comment by thayne 22 hours ago
https://pkg.go.dev/encoding/json/v2#Marshal says otherwise:
> The Go map key must encode as a JSON string, otherwise this results in a SemanticError
> All other Go types (e.g., complex numbers, channels, and functions) have no default representation and result in a SemanticError.
v1 has the same constraints.
Comment by hnlmorg 20 hours ago
The doc you linked to literally proves me right.
Comment by thayne 9 hours ago
Comment by hnlmorg 9 hours ago
Comment by 0x696C6961 1 day ago
Comment by jeremyloy_wt 1 day ago
Comment by hnlmorg 1 day ago
Comment by 0x696C6961 1 day ago
Comment by leetrout 1 day ago
Comment by lolpanda 1 day ago
Comment by iaaan 1 day ago
Comment by NoDodgeQuestion 1 day ago
Comment by brunoborges 1 day ago
Clearly it isn't, otherwise every language would have sorted it out by... a long time ago.
Comment by thiht 1 day ago
Comment by chabska 1 day ago
Comment by kccqzy 1 day ago
Comment by ncruces 1 day ago
You can still use it though, and I built my tiny decimal library around it: https://github.com/ncruces/decimal
Comment by layer8 1 day ago
A JSON parser MAY accept non-JSON forms or extensions.
An implementation may set limits on the size of texts that it accepts. An implementation may set limits on the maximum depth of nesting. An implementation may set limits on the range and precision of numbers. An implementation may set limits on the length and character contents of strings.
Add to that things like duplicate keys being allowed by the spec. There’s a lot of implementation choices to decide on.
And then things can get really complex with JSON Schema.
Comment by kccqzy 1 day ago
A lot of specs contain these optional limitations. The C standard allowed implementations to truncate variable names to 31 characters if they have external linkage and 63 if internal linkage. Would you be happy if your implementation actually does that? Of course not. Neither LLVM nor GCC does that. That’s because people writing compilers also hold themselves to a higher standard. Now imagine this kind of attitude being pervasive.
Comment by layer8 1 day ago
Comment by queenkjuul 1 day ago
Comment by kccqzy 1 day ago
Comment by ivanjermakov 1 day ago
Comment by yubblegum 1 day ago
Comment by DrBazza 1 day ago
Who knew the same object could have completely different definitions based on some flags?
Comment by jayd16 1 day ago
Comment by DanielHB 1 day ago
Comment by slopnt 1 day ago
Comment by rednafi 1 day ago
Comment by muddi900 1 day ago
"What if it sucks?"
"There are now TWO ways to do each thing! No more than TWO! I promise!"
It's like python again.
Comment by Mawr 1 day ago
So yeah, if you ignore most of what matters, everything is the same.
Comment by muddi900 1 day ago
The problem with “Batteries Included” is that batteries run out of juice eventually.
Comment by hnlmorg 1 day ago
It’s versioned because the function signature differs but the abstraction itself doesn’t.
That’s not to say that Go doesn’t have multiple different abstractions for common problems too. Just that your specific argument here is incorrect.
Comment by muddi900 11 hours ago
"Sir we need a loop" "We will have for loop, and nothing else." "Only one lo.." "Only for loop"
Comment by arbor-group 1 day ago
Comment by videotextly 1 day ago
Comment by quietfeeds 1 day ago
Comment by learntoplaypitc 1 day ago
Comment by birchcove 1 day ago
Comment by lanmu 1 day ago
Comment by blenser4 1 day ago
Comment by minfazhai 1 day ago
Comment by SkiFire13 1 day ago
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.
Comment by travisd 1 day ago
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)Comment by SkiFire13 1 day ago
This is definitely not what I expect in other language. I would argue this is a quick of Go though and not JSON's fault.
Comment by Vaushite 13 hours ago
Comment by devmor 1 day ago
Comment by Someone 1 day ago
That seems like a bad default to me.
Comment by throwaway894345 1 day ago
Comment by c-hendricks 1 day ago
Comment by throwaway894345 1 day ago
Comment by c-hendricks 20 hours ago
Comment by throwaway894345 19 hours ago
Comment by the_gipsy 1 day ago
Just look at the inane rules for map and slice access, what gets nil, what gets the zero value, which built-in mist be used. Even if that were consistent, then you'd still left to wonder if json.field was truly zero or not there.
Comment by hujun236 1 day ago
Comment by 0xmattclark 1 day ago