-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Description
@wader made me initially aware of @nicowilliams's co-expressions PR.
I'm not sure yet whether I understand it correctly. But if what follows is not what that PR is about, I think that it would still be interesting on its own sake.
So what I understand is that a co-expression allows you to capture all outputs of an expression into a variable, such that each further occasion of that variable yields the next output of the expression.
For example:
(1, 2, 3) as $@s | ($@s, $@s)would yield 1, 2.
(I just made up a bit of syntax. I think that @nicowilliams uses just @s to denote a "co-variable", but this might cause conflicts with the escaping syntax @uri "foo\(.)bla". Let's not bikeshed for now, that can be done later. ^^)
I was quite excited to realise that you can implement zip/2 quite easily with such a construct, which you cannot do with current jq:
def zip($@x; $@y): def rec: $@x as $x | $@y as $y | [$x, $y]; rec;(I suppose that if the expression that has been captured by a co-variable $@x does not yield any more outputs, then $@x simply yields empty.)
Furthermore, this would allow us to implement inputs / input very naturally.
In particular, just like $ENV is currently bound on the top-level in every jq program, we could bind $@input on the toplevel, which would allow us to write:
def inputs: $@input | ., inputs;
def input : $@input;And now for something completely different: foreach. I found that we can even simulate that with co-expressions:
foreach xs as $x (init; update; project) ===
init as $init | xs as $@x | $init |
def rec: label $exit | ifempty($@x; break $exit) as $x | update($x) | (project($x), rec); recHere, isempty(f; g) is a bit like f // g, except that it calls g only if f is empty, otherwise it returns all outputs of f.
Of course, this version of foreach is not as good performance-wise as the built-in foreach, e.g. because the label breaks tail recursion and so on. I'm not proposing here to replace the built-in foreach by what is here. But it is at least a very good sign that you can simulate built-in operators with this approach which you could not even closely simulate before.
All in all, I am pretty excited about this. Even if it would definitely change the characteristics of jq towards a language with side effects, I think that the gained power could be worth it. I also think that I could implement this in my jq implementation jaq with quite little effort.
If somebody would extract the relevant parts of @nicowilliams's PR and make a smaller PR out of it that contains only to co-expression functionality, then I think I would be motivated to go ahead and implement this in jaq.
What do you think?