const foo = JSON.parse(arbitraryJsonString);
and not having to worry about the structure up front. const foo = JSON.parse(arbitraryJsonString);
and not having to worry about the structure up front.That's not power, that's a shotgun aimed at your crotch whose trigger is connected to a cosmic ray detector.
The API then becomes a runtime fallible one, which is perfectly sound.
Also, how do you then access nested objects, like data['key'][0]['attr'] in Python?
Then you write one short operator (and I agree that some static languages make this more cumbersome than it should be) to say so, and either handle the case where it isn't, or explicitly declare yourself partial and not handling it.
> Also, how do you then access nested objects, like data['key'][0]['attr'] in Python?
With lenses, something like:
data ^? (key "key") >>> (nth 0) >>> (key "attr")
If you do several unsafe operations in a row then this is cumbersome by design - you want to be clear which parts of your program are safe and which are unsafe, so that readers can understand and know where to review. But a good language should let you compose together several unsafe operations in a lightweight way and then execute them as a single unsafe operation, for cases like this where you want to work in the unsafe part of the language for a bit.But my main point is that HideousKojima's "statically-typed" solution would result in a runtime type error if it was given unexpected input, just like a dynamically typed solution.
I don't think HideousKojima ever called it a "statically-typed solution". Their point was that statically-typed languages still let you write unchecked code when you want to - and yes, of course such unchecked code can fail at runtime - but give you the option of having checking in the cases where you want it.
let foo: serde_json::Value = serde_json::from_str(arbitraryJsonString)?;
There, just as powerful [1]. But you know what's even more powerful? After you've done your dynamic checks, you can do this on the entire JSON tree, or on a subtree: let bar: MyStaticType = serde_json::from_value(foo)?;
and you get a fully parsed instance of a static type, with all the guarantees and performance benefits that entails.[1] Value represents a JSON tree: https://docs.serde.rs/serde_json/enum.Value.html
For anything even remotely production-y I'll always prefer explicitly parsing JSON into a known structure, but there's a lot of value in in being able to do some exploratory scripting without those constraints.
There are also recursive types that help you model JSON, but knowing that it's an arbitrary deep nested map/list of maps/lists and number and bool and string mixed like a Bloody Mary cocktail doesn't really help :)
With NestJS it's very easy to add decorators/annotations to fields of a class, and the framework handles validation (throws HTTP 422 with nice descriptions of what failed) and then in your controller you can again work in a type safe environment.
https://www.typescriptlang.org/docs/handbook/release-notes/t...
https://www.typescriptlang.org/docs/handbook/2/narrowing.htm...
const Json = union(enum) {
null,
number: f64,
bool: bool,
string: []const u8,
array: []const Json,
object: HashMap([]const u8, Json),
};
Usually it's a tagged union of the base JSON types which can easily be consumed by most statically typed languages or a variant of it.EDIT: added "tagged"
Now you get type checking on JSON at compile time :)
As soon as you try to do anything useful to foo it's not arbitrary anymore. You have to make some kind of an assumption on the underlying type, check for keys, nulls, maybe it's a number (the right number?), maybe it's a list. So now you have to scatter some boilerplate checks everywhere you touch a part of foo.
If you could parse it into a typed structure up front, you'd only have to deal with this in one spot, and have guarantees for everything else that follows.
Bonus: if your typed language has good support for records, you can even do this in a way that only provides structure to the parts you care about, and is robust to changes to any other parts of the json.
eval('alert("hello, ' + userInput.name + '!")')