more radical change: merge tutorial into readme, cleanup

This commit is contained in:
Simon Cruanes 2017-02-11 14:46:56 +01:00
parent 8d671f03a1
commit 7603a78970
2 changed files with 316 additions and 393 deletions

View file

@ -2,11 +2,23 @@
:toc: macro
:source-highlighter: pygments
What is _containers_? (take a look at the link:TUTORIAL.adoc[tutorial]!
or the http://c-cube.github.io/ocaml-containers/[current documentation])
In `containers` and `containers.data`, all modules abide by
_pay for what you use_: only modules that are used are linked (there are no
cross-module dependencies).
A modular, clean and powerful extension of the OCaml standard library.
Containers is an extension of OCaml's standard library (under BSD license)
focused on data structures, combinators and iterators, without dependencies on
unix, str or num. Every module is independent and is prefixed with 'CC' in the
global namespace. Some modules extend the stdlib (e.g. CCList provides safe
map/fold_right/append, and additional functions on lists).
Alternatively, `open Containers` will bring enhanced versions of the standard
modules into scope.
image:https://ci.cedeela.fr/buildStatus/icon?job=containers[alt="Build Status", link="http://ci.cedeela.fr/job/containers/"]
toc::[]
== Quick Summary
Containers is:
- A usable, reasonably well-designed library that extends OCaml's standard
library (in 'src/core/', packaged under `containers` in ocamlfind. Modules
@ -18,11 +30,9 @@ cross-module dependencies).
`Containers` (intended to be opened, replaces some stdlib modules
with extended ones).
- Several small additional libraries that complement it:
containers.data:: with additional data structures that don't have an
* `containers.data` with additional data structures that don't have an
equivalent in the standard library;
containers.iter:: with list-like and tree-like iterators;
* `containers.iter` with list-like and tree-like iterators;
- Utilities around the `unix` library in `containers.unix` (mainly to spawn
sub-processes easily and deal with resources safely)
- A lightweight S-expression printer and streaming parser in `containers.sexp`
@ -33,10 +43,6 @@ cross-module dependencies).
Some of the modules have been moved to their own repository (e.g. `sequence`,
`gen`, `qcheck`) and are on opam for great fun and profit.
image:https://ci.cedeela.fr/buildStatus/icon?job=containers[alt="Build Status", link="http://ci.cedeela.fr/job/containers/"]
toc::[]
== Change Log
See link:CHANGELOG.adoc[this file].
@ -80,111 +86,8 @@ This code is free, under the BSD license.
== Contents
The library contains a <<core,Core part>> that mostly extends the stdlib
and adds a few very common structures (heap, vector), and sub-libraries
that deal with either more specific things, or require additional dependencies.
Some structural types are used throughout the library:
gen:: `'a gen = unit -> 'a option` is an iterator type. Many combinators
are defined in the opam library https://github.com/c-cube/gen[gen]
sequence:: `'a sequence = (unit -> 'a) -> unit` is also an iterator type.
It is easier to define on data structures than `gen`, but it a bit less
powerful. The opam library https://github.com/c-cube/sequence[sequence]
can be used to consume and produce values of this type.
error:: `'a or_error = ('a, string) result = Error of string | Ok of 'a`
using the standard `result` type, supported in `CCResult`.
klist:: `'a klist = unit -> [`Nil | `Cons of 'a * 'a klist]` is a lazy list
without memoization, used as a persistent iterator. The reference
module is `CCKList` (in `containers.iter`).
printer:: `'a printer = Format.formatter -> 'a -> unit` is a pretty-printer
to be used with the standard module `Format`. In particular, in many cases,
`"foo: %a" Foo.print foo` will type-check.
[[core]]
=== Core Modules (extension of the standard library)
the core library, `containers`, now depends on
https://github.com/mjambon/cppo[cppo] and `base-bytes` (provided
by ocamlfind).
http://c-cube.github.io/ocaml-containers/[Documentation here].
- `CCHeap`, a purely functional heap structure
- `CCVector`, a growable array (pure OCaml, no C) with mutability annotations
- `CCList`, functions on lists, including tail-recursive implementations of `map` and `append` and many other things
- `CCArray`, utilities on arrays
- `CCArray_slice`, array slices
- `CCHashtbl`, `CCMap` extensions of the standard modules `Hashtbl` and `Map`
- `CCInt`
- `CCString` (basic string operations)
- `CCPair` (cartesian products)
- `CCOpt` (options, very useful)
- `CCFun` (function combinators)
- `CCBool`
- `CCFloat`
- `CCOrd` (combinators for total orderings)
- `CCRandom` (combinators for random generators)
- `CCHash` (hashing combinators)
- `CCResult` (monadic error handling, very useful)
- `CCIO`, basic utilities for IO (channels, files)
- `CCInt64,` utils for `int64`
- `CCChar`, utils for `char`
- `CCFormat`, pretty-printing utils around `Format`
=== Containers.data
- `CCBitField`, bitfields embedded in integers
- `CCCache`, memoization caches, LRU, etc.
- `CCFlatHashtbl`, a flat (open-addressing) hashtable functorial implementation
- `CCTrie`, a prefix tree
- `CCHashTrie`, a map where keys are hashed and put in a trie by hash
- `CCMultimap` and `CCMultiset`, functors defining persistent structures
- `CCFQueue`, a purely functional double-ended queue structure
- `CCBV`, mutable bitvectors
- `CCHashSet`, mutable set
- `CCPersistentHashtbl` and `CCPersistentArray`, a semi-persistent array and hashtable
(similar to https://www.lri.fr/~filliatr/ftp/ocaml/ds/parray.ml.html[persistent arrays])
- `CCMixmap`, `CCMixtbl`, `CCMixset`, containers of universal types (heterogenous containers)
- `CCRingBuffer`, a double-ended queue on top of an array-like structure,
with batch operations
- `CCIntMap`, map specialized for integer keys based on Patricia Trees,
with fast merges
- `CCGraph`, a small collection of graph algorithms
- `CCBitField`, a type-safe implementation of bitfields that fit in `int`
- `CCWBTree`, a weight-balanced tree, implementing a map interface
- `CCRAL`, a random-access list structure, with `O(1)` cons/hd/tl and `O(ln(n))`
access to elements by their index.
- `CCImmutArray`, immutable interface to arrays
=== Containers.unix
- `CCUnix`, utils for `Unix`
=== Containers.sexp
A small S-expression library.
- `CCSexp`, a small S-expression library
=== Containers.iter
Iterators:
- `CCKList`, a persistent iterator structure (akin to a lazy list, without memoization)
- `CCKTree`, an abstract lazy tree structure
=== Thread
In the library `containers.thread`, for preemptive system threads:
- `CCFuture`, a set of tools for preemptive threading, including a thread pool,
monadic futures, and MVars (concurrent boxes)
- `CCLock`, values protected by locks
- `CCSemaphore`, a simple implementation of semaphores
- `CCThread` basic wrappers for `Thread`
[[build]]
See http://c-cube.github.io/ocaml-containers/[the documentation]
and <<tutorial,the tutorial below>> for a gentle introduction.
== Documentation
@ -198,6 +101,7 @@ by version:
- http://c-cube.github.io/ocaml-containers/0.19/[0.19]
- http://c-cube.github.io/ocaml-containers/0.17/[0.17]
[[build]]
== Build
You will need OCaml `>=` 4.01.0.
@ -238,3 +142,297 @@ A few guidelines:
- add tests if possible (using `qtest`).
Powered by image:http://oasis.forge.ocamlcore.org/oasis-badge.png[alt="OASIS", style="border: none;", link="http://oasis.forge.ocamlcore.org/"]
[[tutorial]]
== Tutorial
This tutorial contains a few examples to illustrate the features and
usage of containers. We assume containers is installed and that
the library is loaded, e.g. with:
[source,OCaml]
----
#require "containers";;
----
=== Basics
We will start with a few list helpers, then look at other parts of
the library, including printers, maps, etc.
[source,OCaml]
----
(* quick reminder of this awesome standard operator *)
# (|>) ;;
- : 'a -> ('a -> 'b) -> 'b = <fun>
# open CCList.Infix;;
# let l = 1 -- 100;;
val l : int list = [1; 2; .....]
# l
|> CCList.filter_map
(fun x-> if x mod 3=0 then Some (float x) else None)
|> CCList.take 5 ;;
- : float list = [3.; 6.; 9.; 12.; 15.]
# let l2 = l |> CCList.take_while (fun x -> x<10) ;;
val l2 : int list = [1; 2; 3; 4; 5; 6; 7; 8; 9]
(* an extension of Map.Make, compatible with Map.Make(CCInt) *)
# module IntMap = CCMap.Make(CCInt);;
(* conversions using the "sequence" type, fast iterators that are
pervasively used in containers. Combinators can be found
in the opam library "sequence". *)
# let map =
l2
|> List.map (fun x -> x, string_of_int x)
|> CCList.to_seq
|> IntMap.of_seq;;
val map : string CCIntMap.t = <abstr>
(* check the type *)
# CCList.to_seq ;;
- : 'a list -> 'a sequence = <fun>
# IntMap.of_seq ;;
- : (int * 'a) CCMap.sequence -> 'a IntMap.t = <fun>
(* we can print, too *)
# Format.printf "@[<2>map =@ @[<hov>%a@]@]@."
(IntMap.print CCFormat.int CCFormat.string_quoted)
map;;
map =
[1 --> "1", 2 --> "2", 3 --> "3", 4 --> "4", 5 --> "5", 6 --> "6",
7 --> "7", 8 --> "8", 9 --> "9"]
- : unit = ()
(* options are good *)
# IntMap.get 3 map |> CCOpt.map (fun s->s ^ s);;
- : string option = Some "33"
----
=== New types: `CCVector`, `CCHeap`, `CCError`, `CCResult`
Containers also contains (!) a few datatypes that are not from the standard
library but that are useful in a lot of situations:
CCVector::
A resizable array, with a mutability parameter. A value of type
`('a, CCVector.ro) CCVector.t` is an immutable vector of values of type `'a`,
whereas a `('a, CCVector.rw) CCVector.t` is a mutable vector that
can be modified. This way, vectors can be used in a quite functional
way, using operations such as `map` or `flat_map`, or in a more
imperative way.
CCHeap::
A priority queue (currently, leftist heaps) functorized over
a module `sig val t val leq : t -> t -> bool` that provides a type `t`
and a partial order `leq` on `t`.
CCError::
An error type for making error handling more explicit (an error monad,
really, if you're not afraid of the "M"-word). It is similar to the
more recent `CCResult`, but works with polymorphic variants for
compatibility with the numerous libraries that use the same type,
that is, `type ('a, 'b) CCError.t = [`Ok of 'a | `Error of 'b]`.
CCResult::
It uses the new `result` type from the standard library (or from
the retrocompatibility package on opam), and presents an interface
similar to `CCError`. In an indeterminate amount of time, it will
totally replace `CCError`.
Now for a few examples:
[source,OCaml]
----
(* create a new empty vector. It is mutable, for otherwise it would
not be very useful. *)
# CCVector.create;;
- : unit -> ('a, CCVector.rw) CCVector.t = <fun>
(* init, similar to Array.init, can be used to produce a
vector that is mutable OR immutable (see the 'mut parameter?) *)
# CCVector.init ;;
- : int -> (int -> 'a) -> ('a, 'mut) CCVector.t = <fun>c
(* use the infix (--) operator for creating a range. Notice
that v is a vector of integer but its mutability is not
decided yet. *)
# let v = CCVector.(1 -- 10);;
val v : (int, '_a) CCVector.t = <abstr>
# Format.printf "v = @[%a@]@." (CCVector.print CCInt.print) v;;
v = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
(* now let's mutate v *)
# CCVector.push v 42;;
- : unit = ()
(* now v is a mutable vector *)
# v;;
- : (int, CCVector.rw) CCVector.t = <abstr>
(* functional combinators! *)
# let v2 = v
|> CCVector.map (fun x-> x+1)
|> CCVector.filter (fun x-> x mod 2=0)
|> CCVector.rev ;;
val v2 : (int, '_a) CCVector.t = <abstr>
# Format.printf "v2 = @[%a@]@." (CCVector.print CCInt.print) v2;;
v2 = [10, 8, 6, 4, 2]
(* let's transfer to a heap *)
# module IntHeap = CCHeap.Make(struct type t = int let leq = (<=) end);;
# let h = v2 |> CCVector.to_seq |> IntHeap.of_seq ;;
val h : IntHeap.t = <abstr>
(* We can print the content of h
(printing is not necessarily in order, though) *)
# Format.printf "h = [@[%a@]]@." (IntHeap.print CCInt.print) h;;
h = [2,4,6,8,10]
(* we can remove the first element, which also returns a new heap
that does not contain it — CCHeap is a functional data structure *)
# IntHeap.take h;;
- : (IntHeap.t * int) option = Some (<abstr>, 2)
# let h', x = IntHeap.take_exn h ;;
val h' : IntHeap.t = <abstr>
val x : int = 2
(* see, 2 is removed *)
# IntHeap.to_list h' ;;
- : int list = [4; 6; 8; 10]
----
=== IO helpers
The core library contains a module called `CCIO` that provides useful
functions for reading and writing files. It provides functions that
make resource handling easy, following
the pattern `with_resource : resource -> (access -> 'a) -> 'a` where
the type `access` is a temporary handle to the resource (e.g.,
imagine `resource` is a file name and `access` a file descriptor).
Calling `with_resource r f` will access `r`, give the result to `f`,
compute the result of `f` and, whether `f` succeeds or raises an
error, it will free the resource.
Consider for instance:
[source,OCaml]
----
# CCIO.with_out "/tmp/foobar"
(fun out_channel ->
CCIO.write_lines_l out_channel ["hello"; "world"]);;
- : unit = ()
----
This just opened the file '/tmp/foobar', creating it if it didn't exist,
and wrote two lines in it. We did not have to close the file descriptor
because `with_out` took care of it. By the way, the type signatures are:
[source,OCaml]
----
val with_out :
?mode:int -> ?flags:open_flag list ->
string -> (out_channel -> 'a) -> 'a
val write_lines_l : out_channel -> string list -> unit
----
So we see the pattern for `with_out` (which opens a function in write
mode and gives its functional argument the corresponding file descriptor).
NOTE: you should never let the resource escape the
scope of the `with_resource` call, because it will not be valid outside.
OCaml's type system doesn't make it easy to forbid that so we rely
on convention here (it would be possible, but cumbersome, using
a record with an explicitely quantified function type).
Now we can read the file again:
[source,OCaml]
----
# let lines = CCIO.with_in "/tmp/foobar" CCIO.read_lines_l ;;
val lines : string list = ["hello"; "world"]
----
There are some other functions in `CCIO` that return _generators_
instead of lists. The type of generators in containers
is `type 'a gen = unit -> 'a option` (combinators can be
found in the opam library called "gen"). A generator is to be called
to obtain successive values, until it returns `None` (which means it
has been exhausted). In particular, python users might recognize
the function
[source,OCaml]
----
# CCIO.File.walk ;;
- : string -> walk_item gen = <fun>;;
----
where `type walk_item = [ `Dir | `File ] * string` is a path
paired with a flag distinguishing files from directories.
=== To go further: containers.data
There is also a sub-library called `containers.data`, with lots of
more specialized data-structures.
The documentation contains the API for all the modules
(see link:README.adoc[the readme]); they also provide
interface to `sequence` and, as the rest of containers, minimize
dependencies over other modules. To use `containers.data` you need to link it,
either in your build system or by `#require containers.data;;`
A quick example based on purely functional double-ended queues:
[source,OCaml]
----
# #require "containers.data";;
# #install_printer CCFQueue.print;; (* better printing of queues! *)
# let q = CCFQueue.of_list [2;3;4] ;;
val q : int CCFQueue.t = queue {2; 3; 4}
# let q2 = q |> CCFQueue.cons 1 |> CCFQueue.cons 0 ;;
val q2 : int CCFQueue.t = queue {0; 1; 2; 3; 4}
(* remove first element *)
# CCFQueue.take_front q2;;
- : (int * int CCFQueue.t) option = Some (0, queue {1; 2; 3; 4})
(* q was not changed *)
# CCFQueue.take_front q;;
- : (int * int CCFQueue.t) option = Some (2, queue {3; 4})
(* take works on both ends of the queue *)
# CCFQueue.take_back_l 2 q2;;
- : int CCFQueue.t * int list = (queue {0; 1; 2}, [3; 4])
----
=== Common Type Definitions
Some structural types are used throughout the library:
gen:: `'a gen = unit -> 'a option` is an iterator type. Many combinators
are defined in the opam library https://github.com/c-cube/gen[gen]
sequence:: `'a sequence = (unit -> 'a) -> unit` is also an iterator type.
It is easier to define on data structures than `gen`, but it a bit less
powerful. The opam library https://github.com/c-cube/sequence[sequence]
can be used to consume and produce values of this type.
error:: `'a or_error = ('a, string) result = Error of string | Ok of 'a`
using the standard `result` type, supported in `CCResult`.
klist:: `'a klist = unit -> [`Nil | `Cons of 'a * 'a klist]` is a lazy list
without memoization, used as a persistent iterator. The reference
module is `CCKList` (in `containers.iter`).
printer:: `'a printer = Format.formatter -> 'a -> unit` is a pretty-printer
to be used with the standard module `Format`. In particular, in many cases,
`"foo: %a" Foo.print foo` will type-check.

View file

@ -1,275 +0,0 @@
= Tutorial
:source-highlighter: pygments
This tutorial contains a few examples to illustrate the features and
usage of containers. We assume containers is installed and that
the library is loaded, e.g. with:
[source,OCaml]
----
#require "containers";;
----
== Basics
We will start with a few list helpers, then look at other parts of
the library, including printers, maps, etc.
[source,OCaml]
----
(* quick reminder of this awesome standard operator *)
# (|>) ;;
- : 'a -> ('a -> 'b) -> 'b = <fun>
# open CCList.Infix;;
# let l = 1 -- 100;;
val l : int list = [1; 2; .....]
# l
|> CCList.filter_map
(fun x-> if x mod 3=0 then Some (float x) else None)
|> CCList.take 5 ;;
- : float list = [3.; 6.; 9.; 12.; 15.]
# let l2 = l |> CCList.take_while (fun x -> x<10) ;;
val l2 : int list = [1; 2; 3; 4; 5; 6; 7; 8; 9]
(* an extension of Map.Make, compatible with Map.Make(CCInt) *)
# module IntMap = CCMap.Make(CCInt);;
(* conversions using the "sequence" type, fast iterators that are
pervasively used in containers. Combinators can be found
in the opam library "sequence". *)
# let map =
l2
|> List.map (fun x -> x, string_of_int x)
|> CCList.to_seq
|> IntMap.of_seq;;
val map : string CCIntMap.t = <abstr>
(* check the type *)
# CCList.to_seq ;;
- : 'a list -> 'a sequence = <fun>
# IntMap.of_seq ;;
- : (int * 'a) CCMap.sequence -> 'a IntMap.t = <fun>
(* we can print, too *)
# Format.printf "@[<2>map =@ @[<hov>%a@]@]@."
(IntMap.print CCFormat.int CCFormat.string_quoted)
map;;
map =
[1 --> "1", 2 --> "2", 3 --> "3", 4 --> "4", 5 --> "5", 6 --> "6",
7 --> "7", 8 --> "8", 9 --> "9"]
- : unit = ()
(* options are good *)
# IntMap.get 3 map |> CCOpt.map (fun s->s ^ s);;
- : string option = Some "33"
----
== New types: `CCVector`, `CCHeap`, `CCError`, `CCResult`
Containers also contains (!) a few datatypes that are not from the standard
library but that are useful in a lot of situations:
CCVector::
A resizable array, with a mutability parameter. A value of type
`('a, CCVector.ro) CCVector.t` is an immutable vector of values of type `'a`,
whereas a `('a, CCVector.rw) CCVector.t` is a mutable vector that
can be modified. This way, vectors can be used in a quite functional
way, using operations such as `map` or `flat_map`, or in a more
imperative way.
CCHeap::
A priority queue (currently, leftist heaps) functorized over
a module `sig val t val leq : t -> t -> bool` that provides a type `t`
and a partial order `leq` on `t`.
CCError::
An error type for making error handling more explicit (an error monad,
really, if you're not afraid of the "M"-word). It is similar to the
more recent `CCResult`, but works with polymorphic variants for
compatibility with the numerous libraries that use the same type,
that is, `type ('a, 'b) CCError.t = [`Ok of 'a | `Error of 'b]`.
CCResult::
It uses the new `result` type from the standard library (or from
the retrocompatibility package on opam), and presents an interface
similar to `CCError`. In an indeterminate amount of time, it will
totally replace `CCError`.
Now for a few examples:
[source,OCaml]
----
(* create a new empty vector. It is mutable, for otherwise it would
not be very useful. *)
# CCVector.create;;
- : unit -> ('a, CCVector.rw) CCVector.t = <fun>
(* init, similar to Array.init, can be used to produce a
vector that is mutable OR immutable (see the 'mut parameter?) *)
# CCVector.init ;;
- : int -> (int -> 'a) -> ('a, 'mut) CCVector.t = <fun>c
(* use the infix (--) operator for creating a range. Notice
that v is a vector of integer but its mutability is not
decided yet. *)
# let v = CCVector.(1 -- 10);;
val v : (int, '_a) CCVector.t = <abstr>
# Format.printf "v = @[%a@]@." (CCVector.print CCInt.print) v;;
v = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
(* now let's mutate v *)
# CCVector.push v 42;;
- : unit = ()
(* now v is a mutable vector *)
# v;;
- : (int, CCVector.rw) CCVector.t = <abstr>
(* functional combinators! *)
# let v2 = v
|> CCVector.map (fun x-> x+1)
|> CCVector.filter (fun x-> x mod 2=0)
|> CCVector.rev ;;
val v2 : (int, '_a) CCVector.t = <abstr>
# Format.printf "v2 = @[%a@]@." (CCVector.print CCInt.print) v2;;
v2 = [10, 8, 6, 4, 2]
(* let's transfer to a heap *)
# module IntHeap = CCHeap.Make(struct type t = int let leq = (<=) end);;
# let h = v2 |> CCVector.to_seq |> IntHeap.of_seq ;;
val h : IntHeap.t = <abstr>
(* We can print the content of h
(printing is not necessarily in order, though) *)
# Format.printf "h = [@[%a@]]@." (IntHeap.print CCInt.print) h;;
h = [2,4,6,8,10]
(* we can remove the first element, which also returns a new heap
that does not contain it — CCHeap is a functional data structure *)
# IntHeap.take h;;
- : (IntHeap.t * int) option = Some (<abstr>, 2)
# let h', x = IntHeap.take_exn h ;;
val h' : IntHeap.t = <abstr>
val x : int = 2
(* see, 2 is removed *)
# IntHeap.to_list h' ;;
- : int list = [4; 6; 8; 10]
----
== IO helpers
The core library contains a module called `CCIO` that provides useful
functions for reading and writing files. It provides functions that
make resource handling easy, following
the pattern `with_resource : resource -> (access -> 'a) -> 'a` where
the type `access` is a temporary handle to the resource (e.g.,
imagine `resource` is a file name and `access` a file descriptor).
Calling `with_resource r f` will access `r`, give the result to `f`,
compute the result of `f` and, whether `f` succeeds or raises an
error, it will free the resource.
Consider for instance:
[source,OCaml]
----
# CCIO.with_out "/tmp/foobar"
(fun out_channel ->
CCIO.write_lines_l out_channel ["hello"; "world"]);;
- : unit = ()
----
This just opened the file '/tmp/foobar', creating it if it didn't exist,
and wrote two lines in it. We did not have to close the file descriptor
because `with_out` took care of it. By the way, the type signatures are:
[source,OCaml]
----
val with_out :
?mode:int -> ?flags:open_flag list ->
string -> (out_channel -> 'a) -> 'a
val write_lines_l : out_channel -> string list -> unit
----
So we see the pattern for `with_out` (which opens a function in write
mode and gives its functional argument the corresponding file descriptor).
NOTE: you should never let the resource escape the
scope of the `with_resource` call, because it will not be valid outside.
OCaml's type system doesn't make it easy to forbid that so we rely
on convention here (it would be possible, but cumbersome, using
a record with an explicitely quantified function type).
Now we can read the file again:
[source,OCaml]
----
# let lines = CCIO.with_in "/tmp/foobar" CCIO.read_lines_l ;;
val lines : string list = ["hello"; "world"]
----
There are some other functions in `CCIO` that return _generators_
instead of lists. The type of generators in containers
is `type 'a gen = unit -> 'a option` (combinators can be
found in the opam library called "gen"). A generator is to be called
to obtain successive values, until it returns `None` (which means it
has been exhausted). In particular, python users might recognize
the function
[source,OCaml]
----
# CCIO.File.walk ;;
- : string -> walk_item gen = <fun>;;
----
where `type walk_item = [ `Dir | `File ] * string` is a path
paired with a flag distinguishing files from directories.
== To go further: containers.data
There is also a sub-library called `containers.data`, with lots of
more specialized data-structures.
The documentation contains the API for all the modules
(see link:README.adoc[the readme]); they also provide
interface to `sequence` and, as the rest of containers, minimize
dependencies over other modules. To use `containers.data` you need to link it,
either in your build system or by `#require containers.data;;`
A quick example based on purely functional double-ended queues:
[source,OCaml]
----
# #require "containers.data";;
# #install_printer CCFQueue.print;; (* better printing of queues! *)
# let q = CCFQueue.of_list [2;3;4] ;;
val q : int CCFQueue.t = queue {2; 3; 4}
# let q2 = q |> CCFQueue.cons 1 |> CCFQueue.cons 0 ;;
val q2 : int CCFQueue.t = queue {0; 1; 2; 3; 4}
(* remove first element *)
# CCFQueue.take_front q2;;
- : (int * int CCFQueue.t) option = Some (0, queue {1; 2; 3; 4})
(* q was not changed *)
# CCFQueue.take_front q;;
- : (int * int CCFQueue.t) option = Some (2, queue {3; 4})
(* take works on both ends of the queue *)
# CCFQueue.take_back_l 2 q2;;
- : int CCFQueue.t * int list = (queue {0; 1; 2}, [3; 4])
----