-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Alternative operator // applied on a stream #2189
Comments
My initial reaction is that this is a bug, but the behavior that's actually happening here is that you're getting all values from that stream that pass the alternation operator. If none of the values in that stream pass the alternation operator, then you get the alternative value:
I'm not sure if it's a bug now- I would need to dig into the parser code to see why it generates the bytecode it does for that program. @nicowilliams do you have thoughts on if this is a bug or a "feature" that should be documented? |
@wtlangford Thanks for your feedback. I agree that the actual behavior definitely makes sense, simply not the most intuitive one to me. That said, even if it's not the behavior intended initially, changing it now may cause too many regressions for users, so acknowledging this as a "feature" in the manual sounds indeed a sensible option :) |
@luciole75w - Please note that the jq FAQ summarizes the behavior of
Thus the behavior you describe is both longstanding and documented, though as you observe, the documentation in the manual is at best confusing. (Perhaps it was written on the assumption that The conflation of |
Ah, good catch- I always forget to check the FAQ. I think this is a documentation bug, then. We should update the manual to be more clear about how The most surprising part to me is the implication of the binding precedence of tl;dr- This is a documentation bug, I think. Let's fix this by improving the explanation in the manual. |
@pkoppstein Good point indeed, thanks. I confess to have never checked the wiki so far, my bad :/ So if you plan to update the manual, ideally with an additional example to illustrate, then it's fine. Cheers. |
See jqlang#2189 Tweak explanation and add examples.
@wtlangford wrote:
Currently, manual.yml explains // as follows:
I propose to change this explanatory paragraph so that it reads:
And here is one of the new examples I propose be added to the manual, in the
How would that be? cc: @itchyny |
It's been like this forever. I'm not sure what @stedolan's intent was. Essentially # The bytecode for // is generated by `gen_definedor()` in `src/compile.c` in a close analog of this:
def definedor(a; b): foreach a as $a ([false]; if $a then [true,$a] else [false] end; if .[0
] then .[1] else empty end); dedfinedor(false, false, 1; 0) which... is a bit strange. I'm not sure what the intent was, and I'm not sure how important this is for backwards compatibility. Because I don't know what @stedolan's intent was I'm not yet inclined to update docs, nor, for that matter, the implementation, at least not for 1.7. It's been thus for a decade, it can stay thus till 1.8. I.e., this one isn't urgent. I would like us to have a change freeze until 1.7 is done. We're very close. (@pkoppstein, just a few days ago you were begging us to release now, and now you're insisting we fix this that and the other issues -- they have to be severe for us to fix them when we're this close. Please keep this in mind.) |
While this:
seems accurate, it is not a very satisfying description [perhaps only not yet], perhaps because When I say this needs research, I say so because: a) I would like to hear from @stedolan, b) I'm thinking it might be useful to have And again, this goes back to at least November 25, 2012 if not September 4, 2012, thus I feel no sense of urgency here -- not as to the 1.7 release. I'll consider this over the next few days as we get closer to a 1.7 release though -- I need a few days to think about this one. |
890 block gen_definedor(block a, block b) {
891 // var found := false
892 block found_var = gen_op_var_fresh(STOREV, "found");
893 block init = BLOCK(gen_op_simple(DUP), gen_const(jv_false()), found_var);
894
895 // if found, backtrack. Otherwise execute b
896 block backtrack = gen_op_simple(BACKTRACK);
897 block tail = BLOCK(gen_op_simple(DUP),
898 gen_op_bound(LOADV, found_var),
899 gen_op_target(JUMP_F, backtrack),
900 backtrack,
901 gen_op_simple(POP),
902 b);
903
904 // try again
905 block if_notfound = gen_op_simple(BACKTRACK);
906
907 // found := true, produce result
908 block if_found = BLOCK(gen_op_simple(DUP),
909 gen_const(jv_true()),
910 gen_op_bound(STOREV, found_var),
911 gen_op_target(JUMP, tail));
912
913 return BLOCK(init,
914 gen_op_target(FORK, if_notfound),
915 a,
916 gen_op_target(JUMP_F, if_found),
917 if_found,
918 if_notfound,
919 tail);
920 }
The net effect is that:
thus my earlier description of def definedor($a; b): if $a then $a else b end; Except now if I think I'm starting to understand what @stedolan wanted here: to avoid multiple evaluation of |
@nicowilliams - please bear in mind that, in this thread at least, there was agreement nearly three years ago that the documentation should be fixed, and that in the interim, both gojq and jaq have adopted the same semantics (based on a|select(.)). Also, there’s a lot of code out there that essentially depends on $falsey//$x emitting $x, so I would think it would be extremely difficult to come up with an alternative semantics that affords compatibility with the de facto behavior for the basic usage. Incidentally, I would be fine with some weasel words to indicate the paragraph was descriptive and not to be interpreted as prescriptive. As for my hopes, please note that I had suggested 1.6.1 precisely to avoid anyone feeling undue pressure. Since you had time for binary strings (great stuff, by the way!), I thought it would be worthwhile if the rest of us tried to attend to the important long-standing documentation issues. |
@wtlangford asked me for my input and I was AWOL. Now I'm not. It's a stretch to say that there was agreement in 2020 when William and I were the only maintainers. |
We're not going to do micro updates. We do not have the energy for that unless it becomes absolutely necessary (e.g., for a security vulnerability). I suspect that will continue to be the case for a long time. Remember, we're volunteers, and none of us have jq maintainership as a full-time job. |
As I said, I need(ed) time to digest this issue. I do believe that I now understand what @stedolan intended to do, and now I also understand why it's surprising. But are you surprised that I've never run into this? Had you ever run into this? I suspect I never ran into this because a) I mostly never use Now consider the Though first and foremost I needed to understand the background, which meant divining @stedolan's intent, and now that I think I understand that, maybe I will be more amenable to a docs update. |
@pkoppstein since you went looking, what do the alternative implementations of |
I would also be fine with not saying anything about this in 1.7 after more than ten years of not saying anything about this. I really don't see why you think this is so pressing when we clearly have interesting work lined up for a 1.8 to follow soon after a 1.7. |
@nicowilliams wrote:
I mainly pay attention to gojq and jaq. jaq's documentation is notable for the paper "Denotational Semantics and a Fast Interpreter for jq", which, however, does not bother with // at all. Other than this paper, both gojq and jaq generally take the combination of jq's behavior and documentation as the basis for their own documentation, which is basically written as a "delta" against both. However, the documentation for gojq and jaq is silent on (A question for @itchyny - Did this jq FAQ entry played a role at all in your decision not to document gojq's // in the gojq README?) |
@nicowilliams wrote:
I wrote the |
Worry not. I'll submit a PR tomorrow that clarifies |
Hi,
I'm getting a result which doesn't seem logical to me, considering the documented behavior of the alternative operator
//
.Excerpt from the manual:
Here are the test commands.
The first result is ok. For the second command, I would expect either the same result as the first one, or the complete stream
false, 1, null, 2
as is (if the jq grammar defined the operator//
to apply to a stream as a whole). Instead of one of these two options, here the output is an altered form of the left-hand side. Tested with jq 1.5, 1.6 and master (on Linux Mint 19.3).If it's not a bug then wouldn't it be worth mentionning it in the manual?
The text was updated successfully, but these errors were encountered: