knome
yesterday at 6:37 PM
I would add another to the list, which is languages where every expression yields zero or more values, particularly `jq`. there are some antecedents in Icon and xquery, but these generally require explicitly opting into either production or consumption of value streams, where jq does this stream processing automatically from the ground up. (icon requires use of a suspend and needs an every clause to walk the generated values, xquery requires explicit 'for' statements over streams as many builtin operators fail on value streams)
in jq, the comma separates expressions, which independently yield values. a span of such expressions is called a 'filter', since they are always run by passing values from the prior filter into them (with the initial values sourcing from json objects on stdin, or an implicit null if you pass -n to the program).
$ jq -nc ' def x: "a", "b", "c" ; def y: 1, 2, 3 ; x, y '
"a"
"b"
"c"
1
2
3
$ jq -c '. + 10, . + 20' <<< '1 2 3'
11
21
12
22
13
23
brackets collect values yielded inside of them.
$ jq -nc ' def x: "a", "b", "c" ; def y: 1, 2, 3 ; [x,y] '
["a","b","c",1,2,3]
if you have a complex object that includes multiple expressions yielding multiple values, construction will permute over them.
$ jq -nc ' def x: "a", "b", "c" ; def y: 1, 2, 3 ; {"foo": x, "bar": y} '
{"foo":"a","bar":1}
{"foo":"a","bar":2}
{"foo":"a","bar":3}
{"foo":"b","bar":1}
{"foo":"b","bar":2}
{"foo":"b","bar":3}
{"foo":"c","bar":1}
{"foo":"c","bar":2}
{"foo":"c","bar":3}
the pipe operator `|` runs the next filter with each value yielded by the prior, that value represented by the current value operator `.`.
$ jq -nc ' 1,2,3 | 10 + . '
11
12
13
$ jq -nc ' 1,2,3 | (10 + .) * . '
11
24
39
binding variables in the language is similarly done for each value their source yields
$ jq -nc ' (1,2,3) as $A | $A + $A '
2
4
6
functions in the language are neat because you can choose to accept arguments as either early bound values, or as thunks, with the former prefixed with a $.
for example, this runs `. + 100` parameters context, with `.` as the 10,20,30 passed to it:
$ jq -nc ' def f($t): 1,2,3|$t ; 10,20,30|f(. + 100) '
110
110
110
120
120
120
130
130
130
where this runs `. + 100` in the context of its use inside the function, instead receiving 1,2,3:
$ jq -nc ' def f(t): 1,2,3|t ; 10,20,30|f(. + 100) '
101
102
103
101
102
103
101
102
103
so you could define map taking a current-value array and applying an expression to each entry like so:
$ jq -nc ' def m(todo): [.[]|todo] ; [1,2,3]|m(. * 10) '
[10,20,30]
it's a fun little language for some quick data munging, but the semantics themselves are a decent reason to learn it.