7.7
xlist
Fancy lists, with bounded or unbounded repetition of elements. Can be used as a
type or match pattern.
To use the type expander, you must first require the
type-expander library.
| | (xList τᵢ ... #:rest rest) | |
|
|
| | (xlist τᵢ ... #:rest rest) | |
| | τᵢ | | = | | type | | | | | | repeated-type | | | | | | repeated-type | | = | | type ^ repeat | | | | | | type ^ {repeat} | | | | | | type {repeat} | | | | | | type superscripted-repeat | | | | | | type * | | | | | | type + | | | | | | superscripted-id | | | | | | repeat | | = | | once | | | | | | nat | | | | | | nat + | | | | | | + | | | | | | nat - nat | | | | | | nat - ∞ | | | | | | nat - | | | | | | - nat | | | | | | - | | | | | | - ∞ | | | | | | * |
| | |
|
The notation
type ^ n, where
n is a number, indicates that
the given type should be repeated
n times within the list. Therefore,
the following two types are equivalent:
The notation type * indicates that the given type may be repeated zero
or more times. Therefore, the following two types are equivalent:
The notation type ^ n + indicates that the given type may be repeated
n or more times. Therefore, the following two types are equivalent:
When the number preceding + is omitted, it defaults to 1.
The notation type ^ once yields the same type as type ^ 1,
but other forms recognise once and treat it specially. For example,
xlist-split splits the corresponding element as a standalone value,
not as a list of length one.
The notation type ^ n - m indicates that the given type may be
repeated between n (inclusive) and m (inclusive) times.
Therefore, the following two types are equivalent:
Be aware that the tail of the xList following the use of
type ^ n - m is repeated n - m times, so if the tail itself
contains uses of -, the resulting macro-expanded type will be huge,
and may easily make Typed/Racket run out of memory, or slow down the type
checking.
If the first bound is omitted, it defaults to 0, and if the second
bound is omitted, it defaults to ∞. This means that - on its
own is equivalent to *, but the latter form is preferred.
The superscripted-repeat is a representation of repeat using
superscripted unicode characters, without spaces (i.e. the
superscripted-repeat is a single identifier):
Digits are replaced by their unicode superscripted counterparts
⁰¹²³⁴⁵⁶⁷⁸⁹
+ and - are replaced by their unicode superscripted
counterparts, respectively ⁺ and ⁻
* is replaced by the unicode character “COMBINING ASTERISK
ABOVE” ⃰ (code point U+20F0)
∞ is always omitted, as n - ∞ and - ∞ are
equivalent to n - and 0 -
A superscripted-id is a type identifier ending with a sequence of
characters which would otherwise be valid for superscripted-repeat. In
other words, if the type is an identifier, the type and the
superscripted-repeat can be coalesced into a single identifier.
The identifier String3 is equivalent to the notations
String 3 (with a space between the identifier and the 3) and
String ^ 3.
Similarly, the identifier String* is equivalent to the notations
String * (with a space between the identifier and the *),
String ^ * (using a regular asterisk, i.e. the multiplication function
in Racket) and String * (using a regular asterisk, i.e. the
multiplication function in Racket).
The same logic applies to the other cases.
|
|
(xlist patᵢ ... #:rest rest) | |
|
|
patᵢ | | = | | pattern-or-spliced | | | | | | repeated-pattern | | | | | | spliced-pattern | | | | | | pattern-or-spliced | | = | | pattern | | | | | | spliced-pattern | | | | | | spliced-pattern | | = | | ,@pattern | | | | | | repeated-pattern | | = | | pattern-or-spliced ^ repeat | | | | | | pattern-or-spliced ^ {repeat} | | | | | | pattern-or-spliced superscripted-repeat | | | | | | pattern-or-spliced * | | | | | | pattern-or-spliced + | | | | | | pattern-or-spliced ooo | | | | | | superscripted-id | | | | | | repeat | | = | | once | | | | | | nat | | | | | | nat + | | | | | | + | | | | | | nat - nat | | | | | | nat - ∞ | | | | | | nat - | | | | | | - nat | | | | | | - ∞ | | | | | | - | | | | | | * | | | | | | ooo | | | | | | ooo | | = | | ... | | | | | | ..k | | | | | | ___ | | | | | | __k | | | | | | ...+ |
|
|
|
This match expander works like the
xList type expander, but instead
controls the repetition of match patterns. The repeated patterns are not
literally copied, as this would likely cause errors related to duplicate
attributes. Instead, the
repeat forms control the number of times a
pattern may be bound, like
... does.
If the repeat is once, or if the pattern does not have a
repeat, then the pattern is not put under ellipses, so that
(match '(42) [(xlist a ^ once) a]) returns 42, whereas
(match '(42) [(xlist a ^ 1) a]) returns '(42).
For convenience and compatibility with existing match patterns, the following
equivalences are provided:
... is equivalent to *
..k is equivalent to k +
___ is equivalent to *
__k is equivalent to k +
...+ is equivalent to +
Additionally, when #,@pattern appears as one of the xlist
elements, the given pattern may match any number of elements in the
list. This is implemented in terms of append from the
match-string library.
The following two match patterns are therefore equivalent:
Applying a repeat indicator on a splice is not supported yet, i.e.
(xlist ,@(list-no-order number? string?) 5) will not work.
Note : Typed/Racket’s type inference is not strong enough (yet) to
support some match patterns, and there is no typed/match
library which would help with that (yet). This means that although by
construction xlist tries to avoid generating such patterns, a few of
the patterns supported by xlist will not work in
typed/racket (rest values and spliced lists are the most
likely to cause problems). As an alternative, try the split-xlist
pattern, which produces code which should propagate type information to the
different sub-lists.
Normalizes the xlist type. The normalized form has one type followed by ^
followed by a repeat within braces (a
type without a repeat is
transformed into
type ^ {once}) for each position in the original
type. It always finishes with #:rest rest-type. This function also performs a
few simplifications on the type, like transforming
^ {3 -} into
^ {3 +}, and transforming
^ {0 -} into
^ {*}.
1 Splitting an xlist in its constituent sublists
(split-xlist pat τᵢ ...)
|
(split-xlist pat τᵢ ... . rest) |
(split-xlist pat τᵢ ... #:rest rest) |
|
τᵢ | | = | | type | | | | | | repeated-type | | | | | | repeated-type | | = | | type ^ repeat | | | | | | type ^ {repeat} | | | | | | type {repeat} | | | | | | type superscripted-repeat | | | | | | type * | | | | | | type + | | | | | | superscripted-id | | | | | | repeat | | = | | once | | | | | | nat | | | | | | nat + | | | | | | + | | | | | | nat - nat | | | | | | nat - ∞ | | | | | | nat - | | | | | | - nat | | | | | | - | | | | | | - ∞ | | | | | | * |
|
|
|
This match pattern splits an xlist into a list of lists, and matches the
result against pat. Each repeated element of the xlist is extracted
into one of these sublists. The type for each sublist is determined based on
the element’s type and its repeat:
If the repeat for that element is once, then the
element is inserted directly, without nesting it within a sublist. In
contrast, it the repeat were 1, the element would be
inserted in a sublist of length one.
If the repeat for that element is * or an
equivalent, the type of the sublist will be (Listof type)
If the repeat for that element is n + or an
equivalent, the type of the sublist will be (xList type ^ n +)
If the repeat for that element is n or an
equivalent, the type of the sublist will be (xList type ^ n)
If the repeat for that element is from - to or an
equivalent, the type of the sublist will be
(xList type ^ from - to)
The #:rest or dotted rest is included as the last element of
the list matched against pat. If the first form without a rest type
is used, the list matched against pat still contains '() as
a last element:
Example:
|
- : (U (Immutable-Vector Number Number Integer) | (Mutable-Vector Number Number Integer)) [more precisely: (Mutable-Vector Number Number Integer)] |
|
'#(3 2 1) |
Note that split-xlist assumes the value it is matched against has
the type (xlist τᵢ ... maybe-rest), but does not apply
(? (make-predicate (xlist τᵢ ... maybe-rest))) to the value itself.
The rationale is that the make-predicate may fail at compile-time if
it cannot generate a contract for the given type. In some cases, however
split-xlist will still manage to successfully generate the match
pattern, and can be used on its own, provided that the value is statically
known to be of the right type.
It is therefore recommended to use split-xlist as follows when the
type of the value is not known to be acceptable by split-xlist:
Examples:
> (define v : Any '(1 2 3)) |
|
- : Symbol [more precisely: 'success] |
'success |
2 Untyped versions of xlist
Untyped version of
xlist.
3 Special identifiers recognised by xlist
This identifier can only be used within xlist forms.
This identifier can only be used within xlist forms.
This identifier is meant to be used within xlist forms, but is also equal to
+inf.0 as a convenience. In the future, this package will make it
possible for other packages to overload the meaning of the
^ and
∞ identifiers, so that the value of
∞ may depend on the
packages loaded (for example a symbolic math package may want to attach a
special value to
∞.