Repeat patterns and expressions

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:

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:

#|kawa:7|# [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:

#|kawa:9|# (+ 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.

Sub-patterns in repeat patterns

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:

#|kawa:11|# (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 isum definition.)

You can nest repeat patterns, allowing matching against sequences whose elements are sequences.

#|kawa:31|# (define (fun2 [[x ...] ...] [y ...])
#|.....32|#   [[(+ x y) ...] ...])
#|kawa:33|# (fun2 [[1 2 3] [10 11 12]] [100 200])
[[101 102 103] [210 211 212]]

Note that x is double-nested, while y is singly-nested.

Here each element is constrained to be a pair (a -element sequence):

#|kawa:1|# (! [[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))