ziglang / zig

General-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.
https://ziglang.org
MIT License
34.76k stars 2.54k forks source link

std.json: Support for context in parse/stringify #16891

Open thejoshwolfe opened 1 year ago

thejoshwolfe commented 1 year ago

I'm not sure exactly how this would work, but I've got types that don't directly store their string data, but instead use an offset into a string pool. This means in order to render the type as json, we need additional context during the stringify. Additionally, parsing the type needs a reference to the string pool to write the new string data into the pool. (My workaround is to do a conversion step between two almost-identical types where the json-compatible one stores its string data as slices.)

This is a similar idea to how the std.sort callbacks use a context parameter, and i think this paradigm is pretty idiomatic for Zig callbacks. The main issue seems to be that the existing json stringify/parse extensibility follows the OOP paradigm where you implement a method on the type rather than passing in a function to call on your type. This seems necessary given that you can't take pointers to generic (uninstantiated) functions in Zig, but it still seems like we're missing a feature somewhere. Perhaps we need a new idiom for a context parameter on OOP methods/traits.

// existing method:
pub fn jsonParse(                  allocator: Allocator, source: anytype, options: ParseOptions) !@This() { ... }
// proposed method:
pub fn jsonParse(context: anytype, allocator: Allocator, source: anytype, options: ParseOptions) !@This() { ... }

// existing method:
pub fn jsonStringify(self: @This(),                   jws: anytype) !void { ... }
// proposed method:
pub fn jsonStringify(self: @This(), context: anytype, jws: anytype) !void { ... }

This doesn't quite make sense though, because some implementations of this method (e.g. std.json.ArrayHashMap) don't need to take a context, and some ways of invoking the parse (e.g. all existing workflows) don't provide any context.

Should the context be anytype or can each implementation explicitly ask for a specific type in their signature? Some classes can only be used with particular context types, like my string pool example above. Does this mean that certain ways of matching invocation and types being used will be a compile error? I suppose that makes sense.

VisenDev commented 3 months ago

Possibly related

It would also be nice to add some sort of way to set up json stringification for custom types that need special treatment. For example, before you stringify a std.ArrayListUnmanaged, you need to clear the capacity field, orelse it will cause a segfault when the arraylist is parsed and it thinks is has capacity that. It would be nice to have some standardized way of expressing that x operation needs to be carried out during stringification