Many programming languages have some variant of list comprehension syntax. Kawa splits this into two separate forms, that can be in separate parts of the program:
A repeat pattern as you might guess
repeats a pattern by matching the pattern once for each element of a sequence.
For example, assume
A is a some sequence-valued expression.
(! [a::integer ...] A)
a::integer ...’ is a
repeat pattern that
matches all the elements pf
We call ‘
a::integer’ the repeated pattern - it matches an
individual element of
Any variable define in a repeated pattern is a repeat variable.
In the example, that would be
A repeat expression creates a sequence by repeating an expression for each element of the result.
[(* 2 a) ...][4 6 10 14 22]
In this case ‘
(* 2 a) ...’ is the repeat expression.
The repeated expression is ‘
(* 2 a)’.
The repeated expression is evaluated once for each element
of any contained repeat variable.
If there is more than one repeat variable, they are
repeated in parallel, as many times as the “shortest” repeat variable,
similar to the
(If there is no repeat variable, the repeated expression is potentially
evaluated infinitely many times, which is not allowed.
A planned extension will allow it for lazy repeated expression.)
The use of ‘
...’ for repeat patterns and expressions mirrors
exactly their use in
syntax-rules patterns and templates.
It is an error to use a repeat variable outside of repeat context:
#|kawa:5|# a /dev/stdin:2:1: using repeat variable 'a' while not in repeat context
The repeat form feature is not yet complete. It is missing functionality such as selecting only some elements from a repeat sequence, lazy sequences, and it could be optimized more.
A repeat variable can be used multiple times in the same repeat expressions, or different repeat expressions:
[a ... a ...][2 3 5 7 11 2 3 5 7 11] #|kawa:8|#
[(* a a) ...][4 9 25 49 121]
Repeat expressions are useful not just in sequence literals, but in the argument list of a procedure call, where the resulting sequence is spliced into the argument list. This is especially useful for functions that take a variable number of arguments, because that enables a convenient way to do fold/accumulate/reduce operations. For example:
(+ a ...)28
because 28 is the result of
(+ 2 3 5 7 11).
An elegant way to implement dot product:
(define (dot-product [x ...] [y ...]) (+ (* x y) ...))
When an ellipse expression references two or more distinct repeat variables then they are processed “in parallel”. That does not (necessarily) imply muliple threads, but that the first element of the repeat result is evaluated using the first element of all the repeat sequences, the second element of the result uses the second element of all the repeat sequences, and so on.
While the repeated pattern before the ‘
is commonly in identifier, it may be a more complex pattern.
We showed earlier the repeated pattern with a type specifier,
which applies to each element:
(define (isum [x::integer ...]) (+ x ...))#|kawa:12|#
(isum [4 5 6])15 #|kawa:12|#
(isum [4 5.1 6])Argument #1 (null) to 'isum' has wrong type at gnu.mapping.CallContext.matchError(CallContext.java:189) at atInteractiveLevel-6.isum$check(stdin:11) ...
(The stack trace line number
stdin:11 is that of the
You can nest repeat patterns, allowing matching against sequences whose elements are sequences.
(define (fun2 [[x ...] ...] [y ...])#|.....32|#
[[(+ x y) ...] ...])#|kawa:33|#
(fun2 [[1 2 3] [10 11 12]] [100 200])[[101 102 103] [210 211 212]]
x is double-nested, while
y is singly-nested.
Here each element is constrained to be a pair (a -element sequence):
(! [[x y] ...] [[11 12] [21 22] [31 32]])#|kawa:2|#
[(+ x y) ...]#(23 43 63) #|kawa:3|#
[[x ...] [y ...]]#(#(11 21 31) #(12 22 32))