What more do you want than what's covered in the post?
To me, this post is proof zig doesn't need "proper support". You can already have extremely ergonomic error payloads with existing language features.
Earlier version of this post had some language feature ideas, but then I realized Zig already had all the capabilities, so I just removed that section.
For example, I used to think it'd be nice for functions to be a namespace, so I didn't have `myFunc` and `MyFuncDiagnostics`. But then I realized that the Diagnostics type doesn't need its own name; you can just put the type in the function signature, and use a function like `diagnostics.OfFunction(fn)` to extract the type from the function definition, so you can just write this:
var diag: diagnostics.OfFunction(myFunc) = .{};
const res = myFunc(foo, bar, &diag) catch |err| ...;
As another example, I used to write out the `error{OutOfMemory, ...}` type explicitly in addition to the tagged union payload, but then realized you can generate an error set from the union tags at comptime.
Do you want automatic inference of the error payload type? So zig creates a special tagged union error payloads type for you automatically? It seems complicated and maybe not a good idea. Do you really want your function to return an invisible 20 elements union on error? Do you want to call a someone else's function which returns an invisible 20 elements union on error? You know, maybe it's not a good idea.
More than anything I want "Here's the current officially sanctioned best practice of how to report errors with payloads". For a language that's highly opinionated about everything it's strangely unopinionated here and worse off for it because many libraries just swallow useful diagnostic information, some of my own projects included.
There's a barrier to setting up the diagnostic pattern. When you're in the greenfield phase it's easy to search for information about error reporting, discover various different approaches and long threads of people arguing about what better and just say "ah screw it, I have more important things to do right now" and postpone the decision.
Your approach is fine, I don't love how verbose it is but it could probably be tweaked. If this is the way forward then it should be included in stdlib, documented, and promoted as the recommended way of reporting errors.
I agree. You understand why I wrote this post. It's what I wanted to read 3 weeks ago. We're told "Use Diagnostics like the json stdlib module!" but then you realize the json module is way too simplistic for a complicated application.
But also, I'm sure this method has flaws and can be greatly improved, so hopefully we can come to the right solution.
Are you sure? Just because you feel that way doesn't mean you can just make up a statistic that supports your viewpoint.
In my experience, ergonomics in Zig means something other than ergonomics in many other languages. In plenty of languages, ergonomic code is basically just writing as few characters as possible and being able to achieve complex logic with little boilerplate. In Zig, it feels good to be able to compose the conceptually simple language features into an optimal solution to your problem, even if it results in code that is maybe not aesthetically pleasing on first glance.
I'm not going to declare that this solution is ergonomic or not, you can't always tell just by looking at Zig code whether using it is ergonomic, but by that same logic we shouldn't dismiss it off hand.
I thought so too at first, coming from a language (Hare) where they are very easy and common, but the Diagnostics pattern isn't that bad once you expect it. Various examples: https://ziggit.dev/search?q=Diagnostics
You can't really easily have useful user defined payloads in errors without implicit allocations.
Best way to do it is to pass a payload pointer into the function call. You can put it into an options struct with a default null pointer that noops if you prefer the ergonomics of a kwarg.
Right. It's one thing to build the equivalent of Result into the language -- great. It's another to make it only support simple enum variants and not be extensible.
What more do you want than what's covered in the post?
To me, this post is proof zig doesn't need "proper support". You can already have extremely ergonomic error payloads with existing language features.
Earlier version of this post had some language feature ideas, but then I realized Zig already had all the capabilities, so I just removed that section.
For example, I used to think it'd be nice for functions to be a namespace, so I didn't have `myFunc` and `MyFuncDiagnostics`. But then I realized that the Diagnostics type doesn't need its own name; you can just put the type in the function signature, and use a function like `diagnostics.OfFunction(fn)` to extract the type from the function definition, so you can just write this:
As another example, I used to write out the `error{OutOfMemory, ...}` type explicitly in addition to the tagged union payload, but then realized you can generate an error set from the union tags at comptime.
Do you want automatic inference of the error payload type? So zig creates a special tagged union error payloads type for you automatically? It seems complicated and maybe not a good idea. Do you really want your function to return an invisible 20 elements union on error? Do you want to call a someone else's function which returns an invisible 20 elements union on error? You know, maybe it's not a good idea.
More than anything I want "Here's the current officially sanctioned best practice of how to report errors with payloads". For a language that's highly opinionated about everything it's strangely unopinionated here and worse off for it because many libraries just swallow useful diagnostic information, some of my own projects included.
There's a barrier to setting up the diagnostic pattern. When you're in the greenfield phase it's easy to search for information about error reporting, discover various different approaches and long threads of people arguing about what better and just say "ah screw it, I have more important things to do right now" and postpone the decision.
Your approach is fine, I don't love how verbose it is but it could probably be tweaked. If this is the way forward then it should be included in stdlib, documented, and promoted as the recommended way of reporting errors.
I agree. You understand why I wrote this post. It's what I wanted to read 3 weeks ago. We're told "Use Diagnostics like the json stdlib module!" but then you realize the json module is way too simplistic for a complicated application.
But also, I'm sure this method has flaws and can be greatly improved, so hopefully we can come to the right solution.
> You can already have extremely ergonomic error payloads with existing language features.
I think you meant extremely unergonomic. If you take a dev poll, 8/10 developers would not find the solution ergonomic at all.
Are you sure? Just because you feel that way doesn't mean you can just make up a statistic that supports your viewpoint.
In my experience, ergonomics in Zig means something other than ergonomics in many other languages. In plenty of languages, ergonomic code is basically just writing as few characters as possible and being able to achieve complex logic with little boilerplate. In Zig, it feels good to be able to compose the conceptually simple language features into an optimal solution to your problem, even if it results in code that is maybe not aesthetically pleasing on first glance.
I'm not going to declare that this solution is ergonomic or not, you can't always tell just by looking at Zig code whether using it is ergonomic, but by that same logic we shouldn't dismiss it off hand.
2 replies →
I thought so too at first, coming from a language (Hare) where they are very easy and common, but the Diagnostics pattern isn't that bad once you expect it. Various examples: https://ziggit.dev/search?q=Diagnostics
You can't really easily have useful user defined payloads in errors without implicit allocations.
Best way to do it is to pass a payload pointer into the function call. You can put it into an options struct with a default null pointer that noops if you prefer the ergonomics of a kwarg.
Right. It's one thing to build the equivalent of Result into the language -- great. It's another to make it only support simple enum variants and not be extensible.