Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Positional syntaxes are extensible: you can add new optional arguments at the tail end, such that when those are missing, the old behavior is provided.

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, ...}]


> you can add new optional arguments at the tail end

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.


> I never claimed they weren't extensible

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.


> 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.

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).


"add" : true is not required to print first in the { ... } object syntax, which is an impediment to readability.

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.


This entire argument is a bit of a honey-pot on my part, because the real answer is "Neither is ideal; the tooling around the language should allow the developer to switch back-and-forth arbitrarily, as needed. It should also allow sorting of named arguments."

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.


>Positional syntaxes are extensible: you can add new optional arguments at the tail end, such that when those are missing, the old behavior is provided.

That's just one direction of "extensible". You can't remove existing arguments, which you can with named fields.


You cannot remove an argument from a function without checking all the code which calls it, to make sure it's not using that argument.

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.


v1.js:

    /// 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);
        }
    }




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: