package ppx_seq
Sequence Literals for your OCaml
ppx_seq
is a lightweight syntax rewriter that hopes to make dealing with functional iterators as pain-free as possible. It does so by giving users:
- Literals
- Ranges (WIP)
- Patterns (WIP)
- Notes on side-effects
The rewriter's surface area is so incredibly small that this document alone should be enough to cover it all. Here's how to use it:
Literals
Seq literals can be used wherever an expression is expected. The syntax for them takes the general form [%seq ...]
.
Empty literal
[%seq] => fun () -> Seq.Nil
The empty sequence, which may also be expressed as Seq.empty
NOTE: an older syntax was also used to express the empty sequence: [%seq.empty]
, but it's no longer supported.
Occupied literal
[%seq 1; 2; 3] => fun () -> Seq.Cons(1, fun () -> Seq.Cons(2, ...))
This is the original syntax that motivated the creation of ppx_seq
, every value in the sequence is properly delayed inside a thunk.
Ranges
Similar to Literals, can be used whenever an expression is expected. However, their payload is evaluated eagerly. See: Notes on side-effects (this may change in a future release).
+============================================================================+ | IMPORTANT | | | | Ranges rely on the following definitions being in scope: | | - compare | | - succ | | - (+), (-) | | | | This could be useful for overriding behaviour, but may lead to bugs if not | | handled with care. | | (this may change in a future release). | +============================================================================+
If you're familiar with Haskell, this range syntax follows closely its enumFrom[Then|To][To]
sugar. As a reminder, that sugar has four forms:
[a,b..]
and its special form[a..]
[a,b..c]
and its special form[a..c]
However, unlike Haskell, the default behaviour of this range syntax is not to allow infinite ranges to be constructed from the third and fourth "bounded" forms.
If you're not familiar with Haskell, the semantics will be explained below.
Infinite
Infinite ranges are specified using [%seq.inf ...]
.
First form:
[%seq.inf a, b]
is equivalent to
let a = a and s = b - a in [%seq a; s + a; s + s + a; ...]
Second form:
[%seq.inf a]
is similar, but replace (s + _)
with (succ _)
So it's equivalent to:
let a = a in [%seq a; succ a; succ (succ a); ...]
Finite
(WIP)
Finite ranges are specified using [%seq.fin ...]
.
First form:
[%seq.fin a, b, c]
is equivalent to
let a = a and s = b - a and c = c in
[%seq a; s + a; s + s + a; ...; f]
(* where f = n * s + a and f <= c *)
NOTE: there are checks performed to make sure an infinite sequence is not produced:
- if
a
andc
are equal, no steps are taken, and we get a singleton seq. - if
a
andb
are equal,a
andc
are not, this would produce an infinite sequence, which is not allowed, so we return an empty seq.
Second form:
[%seq.fin a, b]
is similar, but replace (s + _)
with:
(succ _)
whenb
is greater thana
(pred _)
whenb
is less thana
Patterns
TODO
Notes on side-effects
[%seq ...]
Literals are just a normal Seq
as we've seen above, which means they also do recomputations on traversal for side-effectful expressions.
let x = ref 0 in
let s = [%seq incr x] in
let a = !x in
let b =
Seq.iter ignore s;
Seq.iter ignore s;
!x
in
a, b
=> - : int * int = (0, 2)
Meanwhile, [%seq.fin ...]
and [%seq.inf ...]
Ranges are generated by evaluating their payload, so every subexpression in the payload will be evaluated one-per-range.
let r = ref 0 in
let s = [%seq.inf (incr r; 1)] in
let _ = s() in
let a = !r in
let _ = [%seq.inf (incr r; 1), (incr r; 2)] in
let b = !r in
a, b
=> - : int * int = (1, 3)
Notice that we don't really "force/use" the second sequence anywhere up there.
for more info or examples see the tests on <ppx_seq_repo>/test/..
.