[ANN] Sequence 1.0

I have the pleasure to announce that sequence has reached its version 1.0. Sequence is a lightweight, high-performance library of iterators that is compatible with existing iter functions. Sequence 1.0 is a breaking release that comes with some simplification of the API and a bump of the minimum supported OCaml version to 4.02 that comes from jbuilder.

Short tutorial to see what Sequence is about
Online doc at https://c-cube.github.io/sequence/
Repo at https://github.com/c-cube/sequence

Cheers!

14 Likes

Very cool. This really should be part of the standard library IMO.

3 Likes

This is great, thank you!

Is there a writeup somewhere on how to pick between sequence and gen? Both seem similar in intent. Is one generally a better choice than the other?

I just wrote a basic explanation here: https://github.com/c-cube/sequence#comparison-with-a-href-https-github-com-c-cube-gen-gen-a . I’ll try to expand it if it’s not clear enough.

4 Likes

That’s perfect, thank you very much.

Nitpicking, but the claim in the comparison with gen that “Gen (external iterator) is more expressive than Sequence (internal iterator)” is false or at least incomplete. External iterators are more expressive for consumers and more constraining for producers, while internal iterators are more expressive for producers and more constraining for consumers. (For reference, see my old Gagallium blog post: Generators, iterators, control and continuations).

5 Likes

Ah, I forgot to link to your blogpost :+1: (fixed now). I’m slightly uneasy calling the producer side “expressiveness” but it’s indeed easier (and less demanding) to produce sequences. I do mention that gen is more difficult to produce.

1 Like

Very cool!
This may be a matter of taste, but IMHO the API could be improved by using JaneStreet-style named arguments.

There’s a SequenceLabels module, fwiw. But otherwise I’m usually happy with chaining many operations using |>

1 Like

Ah, nice!

I don’t understand. How do named arguments prevent composition using |>?

let p x = x mod 5 = 0 in
Sequence.(1 -- 5_000
  |> filter ~f:p
  |> map ~f:(fun x -> x * x)
  |> fold ~f:(+) ~init:0
)

I’m not saying they prevent composition, I’m just saying I don’t feel lilke I need labels.

Yes, like I said I think it’s probably a matter of personal preference. I see three advantages in using named arguments:

  1. One does not have to memorize the order of the parameters.
  2. The code tends to be more self-documenting.
  3. The freedom to choose an arbitrary order of arguments can lead to better code.

To illustrate point three, compare

let vars t =
  Sequence.filter_map
    (function Var s -> Some s | _ -> None)
    (subterms t)

with

let vars t =
  Sequence.filter_map (subterms t)
    ~f:(function Var s -> Some s | _ -> None)

or better yet

let vars t =
  Sequence.filter_map (subterms t) ~f:(function
    | Var s -> Some s 
    | _ -> None
  )

The ability to supply (subterms t) as the first argument is especially nice when the supplied function is long and spans several lines.

I’d prefer writing something like

let vars t =
  subterms t
  |> Sequence.filter_map
    (function
    | Var s -> Some s
    | _ -> None)

personally; I think that the |> operator is nicer than using labeled arguments, and is nicer without labeled arguments.

1 Like

A case where you want Labels is when you’re folding over a Sequence,
building another Sequence: apart from the folding function, is the first
parameter the accumulator or the sequence you’re folding ? This can (and
personally did) lead to nasty bugs that go totally unnoticed by the type
system.

Yet I’m not advocating for using Labels everywhere (and even less for
changing the existing API): SequenceLabels does address this issue
perfectly.