package ppx_seq

  1. Overview
  2. Docs

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:

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 and c are equal, no steps are taken, and we get a singleton seq.
  • if a and b are equal, a and c 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 _) when b is greater than a
  • (pred _) when b is less than a

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/...