- "op", "path", "value"
- quotes around paths
- colons, commas
- curly braces
In exchange for parentheses.
More generally though, we have an isomorphism. If transformation between the forms is trivial, then use what form is more comfortable for you, but I think being able to identify the 'op' -> 'var-args' pairing is a good thing to keep an eye on. It's macros' bread and butter.
And the s-expressions don't even solve the problem of terseness for wire-format reasons; protobufs beat them out in that category.
With that said, there's EDN and fressian which solve the problems mentioned.
In EDN you could encode an instruction like so for example `(replace :path "/foo" :value 1)`
This reads like a function call because it is a representation of a function call. The semantics are already there.
Even better you could use namespaced keywords and symbols where applicable and suddenly were in a world where we can convey context without nesting, out of bounds information/schema or funny string formats.
This is probably a personal-preference thing; speaking from years of slinging both JSON and elisp, I have to go to the docs to remember the contents of a JSON object far less often than I do to remember the relevant positional arguments to a defun.
The JSON could use positional syntax, since JSON has [ ] arrays, and that would reduce its clutter.
The S-exp could use keyword arguments; it would still be nicer. Note that if we do so, we still want the operation to be positionally determined; i.e. to be the first element.
(patch
(replace :path (baz) :value "boo")
(add :path (hello bim) :value ("world"))
(remove :path (foo)))
JSON becomes even more verbose to achieve something like this ["op", {"kw1" : "arg", "kw2" : 3.1, ...}]I never claimed they weren't extensible. What you're describing as a feature I'm declaring a bug. Named syntaxes self-describe in a way positional syntaxes don't.
(... he says as his IDE helpfully kicks in to tell him what each of the twelve arguments to this auto-generated Java object constructor are in his source code... ;) ).
> JSON becomes even more verbose to achieve something like this
It doesn't because it has key-value pairs as a top-level construct via the object type. LISP represents those by creating positional local dependencies between positional arguments (which is a fine solution, but at that point they're equivalently verbose and if I go with the JSON approach I can take advantage of the JSON libraries already existing in the client and server of my web applications).
In an alternate universe where JavaScript had been just LISP, your approach would be great, but that ship sailed decades ago.
Yes you did because you claimed that it was an advantage of the original. The only purpose of such a remark would be to point out a contrast: extensibility is lost in Y, so I'm pointing out that X has it.
> Named syntaxes self-describe
That's nice but it doesn't connect with the extensibility point.
Named can make it easy to know which argument is serving which role, if you already understand the function and its arguments, where you would otherwise have to remember the argument positions: like which of two identically typed arguments is the destination of the data transfer, the other being source.
> It doesn't because it has key-value pairs as a top-level construct via the object type.
Yes, but this object has no order; there is no way to indicate the first element so you have to use a keyword to indicate the operator too. I believe I communicated this with the example.
My original claim was "The s-expression syntax's reliance on positional inputs is less self-describing and extension can only be done at the end of the s-expressions." You "solved" the issue of "positional inputs... less self-describing" by doubling the number of symbols in your s-expression example so that (with the exception of the first input) they are no longer positional. That doesn't actually solve the problem; it just demonstrates you can do named arguments in s-expressions too. Yes, of course you can; but why bother when my browser already supports JSON?
> That's nice but it doesn't connect with the extensibility point
Correct; they are two separate points. Positional inputs are less self-describing and also extension can only be done at the end of the s-expression. Two criticisms.
> there is no way to indicate the first element so you have to use a keyword to indicate the operator too
Technically, this is untrue; order doesn't matter since the operator can simply be any key that isn't already reserved. So `"add": true` would work to indicate an add stanza.
But I would not recommend this approach. I'd just instead do what the RFC does and specify the operator as another argument: `"op": "add"`. The advantage to this approach (unlike yours) is that, indeed, order does not matter; I do not need the operator name to be the first thing in the list.
The two approaches in the named-argument structure are just about equivalent, except that the JSON approach has the advantage that browsers already have built-in JSON support (which gives that format a huge practical advantage over introducing a novel s-expression format in this problem domain, unfortunately).
Actually, named arguments as such as an impediment to readability when you're already familiar with the API.
This is because they allow the order to be scrambled. No two invocations of the API have to use the same order, so you're always going to have to parse the names, which doubles the effort.
E.g. in C, memcpy(a, b, size) calls don't tell you which is the destination. But once you remember that it's the leftmost argument, it's moot., And it's better than dealing with six possible permutations:
memcpy(dst: a, src: b, size: s);
memcpy(src: b, dst: a, size: s);
memcpy(size: s, src: b, dst: a);
memcpy(size: s, dst: a, src: b);
memcpy(src: b, size: s, dst: a);
memcpy(dst: a, size: s, src: b);
If I drill down on any one of these, and read it carefully, of course there is no mistaking it, but I'd rather have it in a consistent order. Even if the arguments could be optionally labeled with names, I'd want it to work like this: memcpy(src: b, size: s, dst: a); // ERROR: the first argument of memcpy is not called "src".
At least the darned memcpy is out on the left, which it wouldn't be if the function is also another named argument: (src: b, op: memcpy, ...) // 24 permutations!
I'm not a big fan of keyword arguments. I've kept them out of the TXR Lisp language, except for their availability via a "parameter list macro" (a mechanism of my invention for transforming function parameter lists).Once you're reaching for keyword arguments, it means you have a bad API. The fixed, required positional parameters of an API are usually the only part that is designed. The optionals are often less so, and keywords are just a bag of ad hoc afterthoughts which only care about getting some needed effect.
Since I don't get that tooling for wire-protocol content(1), I prefer named properties there because the alternative is usually some nasty flavor of binary alignment or picking semantics out of a densely-packed forest of newline-stripped character streams. But the correct answer in an actual programming language is "Both. Your IDE should make it easy to toggle the names on and off for positional arguments."
If a dev house is already using JSON for all the reasons one might do so, the flat presentation given by the RFC is the right presentation. But the real correct answer is "match your tools to the problem and you don't have to trade off between brevity and clarity."
(1) unless I do. gRPC is a treat to work with because it's thin on-the-wire but the Chrome devtool extensions give you the context. It's not the right solution for the JSON patch protocol because, well, right there in the name... It's JSON. But it's the solution I'd actually recommend to a team putting something new together if they don't need to interface to the outside world / aren't concerned the outside world will balk at a basically binary-on-the-wire data format.
That's just one direction of "extensible". You can't remove existing arguments, which you can with named fields.
Removal isn't extension; so we are no longer on the topic of extensibility. For instance, it wouldn't be considered a compiler/vendor extension if the "for" statement were removed; it would be a (severe) nonconformance.
It may be easier to remove a named argument, if that argument is rarely used, because you can look for uses of that argument by name. If the hits are nonexistent to rare, you have very little work. In a public API, where you don't have access to all the code base which uses the function, you may not have any way of evaluating "rarely used" other than polling the user base.
Needless to say, a data format for a patch operation is a public API.You don't even control the software which implements an argument to be able to remove it. You can write some blurbs in the spec which declare something to be deprecated, and 20 years later, maybe everyone will have stopped using it and removed support.
/// Every `{ "type": "foo" }` object must have a `bar` field.
function foo(obj) {
if (obj.type === "foo") {
reticulate(obj.bar);
}
}
v2.js: /// Relax requirement that every `{ "type": "foo" }` object must have a `bar` field.
function foo(obj) {
if (obj.type === "foo" && typeof obj.bar !== "undefined") {
reticulate(obj.bar);
}
}For the same reason
> If a person puts such a question to you as: ‘I have divided ten into two parts, and multiplying one of these by the other the result was twenty-one;’ then you know that one of the parts is thing and the other is ten minus thing.
is inferior to
> x(10-x)=21
The succinctness helps your brain focus on the important information so you don't get lost in the noise. If you find it hard to read at first, your brain can adapt to the patterns just fine.
> X times open parenthesis ten minus X close parenthesis equals twenty one
I'm not sure if your point is supposed to be that it's easier to understand if read another way or something, but no matter what the symbol version is the easiest for your brain (which is very good at pattern recognition) to see and pick up on.
Maybe I missed your point, so you'll have to be explicit.
By the way, I didn't contrive the example. I googled "before math notation" and chose it from the second result[1]. I had seen similar examples of what math looked like before notation, so I looked for an example.
[1] http://archive.boston.com/bostonglobe/ideas/brainiac/2014/06...
What's your actual point?