mirror of
https://github.com/c-cube/ocaml-containers.git
synced 2025-12-06 11:15:31 -05:00
remove containers.advanced
This commit is contained in:
parent
13b283a91d
commit
7628e654f7
13 changed files with 6 additions and 2795 deletions
2
Makefile
2
Makefile
|
|
@ -74,8 +74,6 @@ QTESTABLE=$(filter-out $(DONTTEST), \
|
||||||
$(wildcard src/unix/*.mli) \
|
$(wildcard src/unix/*.mli) \
|
||||||
$(wildcard src/sexp/*.ml) \
|
$(wildcard src/sexp/*.ml) \
|
||||||
$(wildcard src/sexp/*.mli) \
|
$(wildcard src/sexp/*.mli) \
|
||||||
$(wildcard src/advanced/*.ml) \
|
|
||||||
$(wildcard src/advanced/*.mli) \
|
|
||||||
$(wildcard src/iter/*.ml) \
|
$(wildcard src/iter/*.ml) \
|
||||||
$(wildcard src/iter/*.mli) \
|
$(wildcard src/iter/*.mli) \
|
||||||
$(wildcard src/bigarray/*.ml) \
|
$(wildcard src/bigarray/*.ml) \
|
||||||
|
|
|
||||||
|
|
@ -199,15 +199,6 @@ In the module `Containers_string`:
|
||||||
- `KMP`: Knuth-Morris-Pratt substring algorithm
|
- `KMP`: Knuth-Morris-Pratt substring algorithm
|
||||||
- `Parse`: simple parser combinators
|
- `Parse`: simple parser combinators
|
||||||
|
|
||||||
=== Advanced
|
|
||||||
|
|
||||||
See http://cedeela.fr/~simon/software/containers/Containers_advanced[doc].
|
|
||||||
|
|
||||||
In the module `Containers_advanced`:
|
|
||||||
- `CCLinq`, high-level query language over collections
|
|
||||||
- `CCCat`, a few categorical structures
|
|
||||||
- `CCBatch`, to combine operations on collections into one traversal
|
|
||||||
|
|
||||||
=== Thread
|
=== Thread
|
||||||
|
|
||||||
In the library `containers.thread`, for preemptive system threads:
|
In the library `containers.thread`, for preemptive system threads:
|
||||||
|
|
|
||||||
25
_oasis
25
_oasis
|
|
@ -36,10 +36,6 @@ Flag "bench"
|
||||||
Description: Build and run benchmarks
|
Description: Build and run benchmarks
|
||||||
Default: true
|
Default: true
|
||||||
|
|
||||||
Flag "advanced"
|
|
||||||
Description: Build advanced combinators (requires "sequence")
|
|
||||||
Default: true
|
|
||||||
|
|
||||||
Library "containers"
|
Library "containers"
|
||||||
Path: src/core
|
Path: src/core
|
||||||
Modules: CCVector, CCError, CCHeap, CCList, CCOpt, CCPair,
|
Modules: CCVector, CCError, CCHeap, CCList, CCOpt, CCPair,
|
||||||
|
|
@ -89,15 +85,6 @@ Library "containers_string"
|
||||||
FindlibName: string
|
FindlibName: string
|
||||||
FindlibParent: containers
|
FindlibParent: containers
|
||||||
|
|
||||||
Library "containers_advanced"
|
|
||||||
Path: src/advanced
|
|
||||||
Modules: Containers_advanced, CCLinq, CCBatch, CCCat, CCMonadIO
|
|
||||||
Build$: flag(advanced)
|
|
||||||
Install$: flag(advanced)
|
|
||||||
FindlibName: advanced
|
|
||||||
FindlibParent: containers
|
|
||||||
BuildDepends: containers, sequence
|
|
||||||
|
|
||||||
Library "containers_thread"
|
Library "containers_thread"
|
||||||
Path: src/threads/
|
Path: src/threads/
|
||||||
Modules: CCPool, CCLock, CCSemaphore, CCThread, CCBlockingQueue,
|
Modules: CCPool, CCLock, CCSemaphore, CCThread, CCBlockingQueue,
|
||||||
|
|
@ -122,7 +109,7 @@ Document containers
|
||||||
Title: Containers docs
|
Title: Containers docs
|
||||||
Type: ocamlbuild (0.3)
|
Type: ocamlbuild (0.3)
|
||||||
BuildTools+: ocamldoc
|
BuildTools+: ocamldoc
|
||||||
Build$: flag(docs) && flag(advanced) && flag(unix)
|
Build$: flag(docs) && flag(unix)
|
||||||
Install: true
|
Install: true
|
||||||
XOCamlbuildPath: .
|
XOCamlbuildPath: .
|
||||||
XOCamlbuildExtraArgs:
|
XOCamlbuildExtraArgs:
|
||||||
|
|
@ -130,7 +117,7 @@ Document containers
|
||||||
XOCamlbuildLibraries:
|
XOCamlbuildLibraries:
|
||||||
containers, containers.iter, containers.data,
|
containers, containers.iter, containers.data,
|
||||||
containers.string, containers.thread,
|
containers.string, containers.thread,
|
||||||
containers.advanced, containers.unix, containers.sexp
|
containers.unix, containers.sexp
|
||||||
|
|
||||||
Executable run_benchs
|
Executable run_benchs
|
||||||
Path: benchs/
|
Path: benchs/
|
||||||
|
|
@ -138,7 +125,7 @@ Executable run_benchs
|
||||||
CompiledObject: best
|
CompiledObject: best
|
||||||
Build$: flag(bench)
|
Build$: flag(bench)
|
||||||
MainIs: run_benchs.ml
|
MainIs: run_benchs.ml
|
||||||
BuildDepends: containers, containers.advanced, qcheck,
|
BuildDepends: containers, qcheck,
|
||||||
containers.data, containers.string, containers.iter,
|
containers.data, containers.string, containers.iter,
|
||||||
containers.thread, sequence, gen, benchmark, hamt
|
containers.thread, sequence, gen, benchmark, hamt
|
||||||
|
|
||||||
|
|
@ -157,9 +144,9 @@ Executable run_qtest
|
||||||
Install: false
|
Install: false
|
||||||
CompiledObject: best
|
CompiledObject: best
|
||||||
MainIs: run_qtest.ml
|
MainIs: run_qtest.ml
|
||||||
Build$: flag(tests) && flag(bigarray) && flag(unix) && flag(advanced)
|
Build$: flag(tests) && flag(bigarray) && flag(unix)
|
||||||
BuildDepends: containers, containers.string, containers.iter,
|
BuildDepends: containers, containers.string, containers.iter,
|
||||||
containers.advanced, containers.sexp,
|
containers.sexp,
|
||||||
containers.unix, containers.thread,
|
containers.unix, containers.thread,
|
||||||
containers.data,
|
containers.data,
|
||||||
sequence, gen, unix, oUnit, qcheck
|
sequence, gen, unix, oUnit, qcheck
|
||||||
|
|
@ -167,7 +154,7 @@ Executable run_qtest
|
||||||
Test all
|
Test all
|
||||||
Command: ./run_qtest.native
|
Command: ./run_qtest.native
|
||||||
TestTools: run_qtest
|
TestTools: run_qtest
|
||||||
Run$: flag(tests) && flag(unix) && flag(advanced) && flag(bigarray)
|
Run$: flag(tests) && flag(unix) && flag(bigarray)
|
||||||
|
|
||||||
Executable mem_measure
|
Executable mem_measure
|
||||||
Path: benchs/
|
Path: benchs/
|
||||||
|
|
|
||||||
|
|
@ -131,15 +131,6 @@ CCLevenshtein
|
||||||
CCParse
|
CCParse
|
||||||
}
|
}
|
||||||
|
|
||||||
{4 Advanced}
|
|
||||||
|
|
||||||
{b findlib name}: containers.advanced
|
|
||||||
|
|
||||||
This module is qualified with [Containers_advanced]. It
|
|
||||||
requires {{:https://github.com/c-cube/sequence} Sequence}.
|
|
||||||
|
|
||||||
{!modules: CCLinq CCCat CCBatch}
|
|
||||||
|
|
||||||
{4 Misc}
|
{4 Misc}
|
||||||
|
|
||||||
Moved to its own repository.
|
Moved to its own repository.
|
||||||
|
|
|
||||||
|
|
@ -1,237 +0,0 @@
|
||||||
(*
|
|
||||||
copyright (c) 2013-2014, Simon Cruanes, Gabriel Radanne
|
|
||||||
all rights reserved.
|
|
||||||
|
|
||||||
redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer. redistributions in binary
|
|
||||||
form must reproduce the above copyright notice, this list of conditions and the
|
|
||||||
following disclaimer in the documentation and/or other materials provided with
|
|
||||||
the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*)
|
|
||||||
|
|
||||||
(** {1 Batch Operations on Collections} *)
|
|
||||||
|
|
||||||
module type COLLECTION = sig
|
|
||||||
type 'a t
|
|
||||||
|
|
||||||
val empty : 'a t
|
|
||||||
val fold : ('a -> 'b -> 'a) -> 'a -> 'b t -> 'a
|
|
||||||
val map : ('a -> 'b) -> 'a t -> 'b t
|
|
||||||
val filter : ('a -> bool) -> 'a t -> 'a t
|
|
||||||
val filter_map : ('a -> 'b option) -> 'a t -> 'b t
|
|
||||||
val flat_map : ('a -> 'b t) -> 'a t -> 'b t
|
|
||||||
end
|
|
||||||
|
|
||||||
module type S = sig
|
|
||||||
type 'a t
|
|
||||||
|
|
||||||
type ('a,'b) op
|
|
||||||
(** Operation that converts a ['a t] into a ['b t] *)
|
|
||||||
|
|
||||||
val apply : ('a,'b) op -> 'a t -> 'b t
|
|
||||||
(** Apply the operation to the collection. *)
|
|
||||||
|
|
||||||
val apply_fold : ('a, 'b) op -> ('c -> 'b -> 'c) -> 'c -> 'a t -> 'c
|
|
||||||
(** Apply the operation plus a fold to the collection. *)
|
|
||||||
|
|
||||||
val apply' : 'a t -> ('a,'b) op -> 'b t
|
|
||||||
(** Flip of {!apply} *)
|
|
||||||
|
|
||||||
(** {6 Combinators} *)
|
|
||||||
|
|
||||||
val id : ('a, 'a) op
|
|
||||||
|
|
||||||
val map : ('a -> 'b) -> ('a, 'b) op
|
|
||||||
|
|
||||||
val filter : ('a -> bool) -> ('a,'a) op
|
|
||||||
|
|
||||||
val filter_map : ('a -> 'b option) -> ('a,'b) op
|
|
||||||
|
|
||||||
val flat_map : ('a -> 'b t) -> ('a,'b) op
|
|
||||||
|
|
||||||
val extern : ('a t -> 'b t) -> ('a,'b) op
|
|
||||||
|
|
||||||
val compose : ('b,'c) op -> ('a,'b) op -> ('a,'c) op
|
|
||||||
val (>>>) : ('a,'b) op -> ('b,'c) op -> ('a,'c) op
|
|
||||||
end
|
|
||||||
|
|
||||||
module Make(C : COLLECTION) = struct
|
|
||||||
type 'a t = 'a C.t
|
|
||||||
type (_,_) op =
|
|
||||||
| Nil : ('a,'a) op
|
|
||||||
| Compose : ('a,'b) base_op * ('b, 'c) op -> ('a, 'c) op
|
|
||||||
and (_,_) base_op =
|
|
||||||
| Map : ('a -> 'b) -> ('a, 'b) base_op
|
|
||||||
| Filter : ('a -> bool) -> ('a, 'a) base_op
|
|
||||||
| FilterMap : ('a -> 'b option) -> ('a,'b) base_op
|
|
||||||
| FlatMap : ('a -> 'b t) -> ('a,'b) base_op
|
|
||||||
| Extern : ('a t -> 'b t) -> ('a,'b) base_op
|
|
||||||
|
|
||||||
(* associativity: put parenthesis on the right *)
|
|
||||||
let rec _compose : type a b c. (a,b) op -> (b,c) op -> (a,c) op
|
|
||||||
= fun f g -> match f with
|
|
||||||
| Compose (f1, Nil) -> Compose (f1, g)
|
|
||||||
| Compose (f1, f2) -> Compose (f1, _compose f2 g)
|
|
||||||
| Nil -> g
|
|
||||||
|
|
||||||
(* After optimization, the op is a list of flatmaps and external operations,
|
|
||||||
with maybe something else at the end *)
|
|
||||||
type (_,_) optimized_op =
|
|
||||||
| OptNil : ('a, 'a) optimized_op
|
|
||||||
| OptBase : ('a,'b) base_op * ('b, 'c) optimized_op -> ('a,'c) optimized_op
|
|
||||||
| OptFlatMap : ('a -> 'b t) * ('b, 'c) optimized_op -> ('a, 'c) optimized_op
|
|
||||||
| OptExtern : ('a t -> 'b t) * ('b, 'c) optimized_op -> ('a, 'c) optimized_op
|
|
||||||
|
|
||||||
(* As compose, but optimize recursively on the way. *)
|
|
||||||
let rec optimize_compose
|
|
||||||
: type a b c. (a,b) base_op -> (b,c) op -> (a,c) optimized_op
|
|
||||||
= fun base_op op -> match base_op, op with
|
|
||||||
| f, Nil -> OptBase (f, OptNil)
|
|
||||||
| Map f, Compose (Map g, cont) ->
|
|
||||||
optimize_compose (Map (fun x -> g (f x))) cont
|
|
||||||
| Map f, Compose (Filter p, cont) ->
|
|
||||||
optimize_compose
|
|
||||||
(FilterMap (fun x -> let y = f x in if p y then Some y else None)) cont
|
|
||||||
| Map f, Compose (FilterMap f', cont) ->
|
|
||||||
optimize_compose
|
|
||||||
(FilterMap (fun x -> f' (f x))) cont
|
|
||||||
| Map f, Compose (FlatMap f', cont) ->
|
|
||||||
optimize_compose
|
|
||||||
(FlatMap (fun x -> f' (f x))) cont
|
|
||||||
| Filter p, Compose (Filter p', cont) ->
|
|
||||||
optimize_compose (Filter (fun x -> p x && p' x)) cont
|
|
||||||
| Filter p, Compose (Map g, cont) ->
|
|
||||||
optimize_compose
|
|
||||||
(FilterMap (fun x -> if p x then Some (g x) else None)) cont
|
|
||||||
| Filter p, Compose (FilterMap f', cont) ->
|
|
||||||
optimize_compose
|
|
||||||
(FilterMap (fun x -> if p x then f' x else None)) cont
|
|
||||||
| Filter p, Compose (FlatMap f', cont) ->
|
|
||||||
optimize_compose
|
|
||||||
(FlatMap (fun x -> if p x then f' x else C.empty)) cont
|
|
||||||
| FilterMap f, Compose (FilterMap f', cont) ->
|
|
||||||
optimize_compose
|
|
||||||
(FilterMap
|
|
||||||
(fun x -> match f x with None -> None | Some y -> f' y))
|
|
||||||
cont
|
|
||||||
| FilterMap f, Compose (Filter p, cont) ->
|
|
||||||
optimize_compose
|
|
||||||
(FilterMap
|
|
||||||
(fun x -> match f x with
|
|
||||||
| (Some y) as res when p y -> res
|
|
||||||
| _ -> None))
|
|
||||||
cont
|
|
||||||
| FilterMap f, Compose (Map f', cont) ->
|
|
||||||
optimize_compose
|
|
||||||
(FilterMap
|
|
||||||
(fun x -> match f x with
|
|
||||||
| None -> None
|
|
||||||
| Some y -> Some (f' y)))
|
|
||||||
cont
|
|
||||||
| FilterMap f, Compose (FlatMap f', cont) ->
|
|
||||||
optimize_compose
|
|
||||||
(FlatMap
|
|
||||||
(fun x -> match f x with
|
|
||||||
| None -> C.empty
|
|
||||||
| Some y -> f' y))
|
|
||||||
cont
|
|
||||||
| FlatMap f, Compose (f', tail) ->
|
|
||||||
merge_flat_map f (optimize_compose f' tail)
|
|
||||||
| Extern f, Compose (f', tail) ->
|
|
||||||
OptExtern (f, optimize_compose f' tail)
|
|
||||||
| op, Compose (Extern f', cont) ->
|
|
||||||
OptBase (op, optimize_compose (Extern f') cont)
|
|
||||||
|
|
||||||
and merge_flat_map
|
|
||||||
: type a b c. (a -> b C.t) -> (b,c) optimized_op -> (a,c) optimized_op =
|
|
||||||
fun f op -> match op with
|
|
||||||
| OptNil -> OptFlatMap (f, op)
|
|
||||||
| OptFlatMap (f', cont) ->
|
|
||||||
merge_flat_map
|
|
||||||
(fun x ->
|
|
||||||
let a = f x in
|
|
||||||
C.flat_map f' a)
|
|
||||||
cont
|
|
||||||
| OptExtern _ -> OptFlatMap (f, op)
|
|
||||||
| OptBase _ -> OptFlatMap (f, op)
|
|
||||||
|
|
||||||
(* Optimize a batch operation by fusion *)
|
|
||||||
let optimize : type a b. (a,b) op -> (a,b) optimized_op
|
|
||||||
= fun op -> match op with
|
|
||||||
| Compose (a, b) -> optimize_compose a b
|
|
||||||
| Nil -> OptNil
|
|
||||||
|
|
||||||
let rec apply_optimized : type a b. (a,b) optimized_op -> a t -> b t
|
|
||||||
= fun op a -> match op with
|
|
||||||
| OptNil -> a
|
|
||||||
| OptBase (f,c) -> apply_optimized c (apply_base f a)
|
|
||||||
| OptFlatMap (f,c) -> apply_optimized c (C.flat_map f a)
|
|
||||||
| OptExtern (f,c) -> apply_optimized c (f a)
|
|
||||||
and apply_base : type a b. (a,b) base_op -> a t -> b t
|
|
||||||
= fun op a -> match op with
|
|
||||||
| Map f -> C.map f a
|
|
||||||
| Filter p -> C.filter p a
|
|
||||||
| FlatMap f -> C.flat_map f a
|
|
||||||
| FilterMap f -> C.filter_map f a
|
|
||||||
| Extern f -> f a
|
|
||||||
|
|
||||||
let fusion_fold : type a b c. (a,b) base_op -> (c -> b -> c) -> c -> a -> c
|
|
||||||
= fun op f' -> match op with
|
|
||||||
| Map f -> (fun z x -> f' z (f x))
|
|
||||||
| Filter p -> (fun z x -> if p x then f' z x else z)
|
|
||||||
| FlatMap f -> (fun z x -> C.fold f' z (f x))
|
|
||||||
| FilterMap f -> (fun z x -> match f x with Some x' -> f' z x' | None -> z)
|
|
||||||
| Extern _ -> assert false
|
|
||||||
|
|
||||||
let rec apply_optimized_with_fold
|
|
||||||
: type a b c. (a,b) optimized_op -> (c -> b -> c) -> c -> a t -> c
|
|
||||||
= fun op fold z a -> match op with
|
|
||||||
| OptNil -> C.fold fold z a
|
|
||||||
| OptBase (Extern f, OptNil) ->
|
|
||||||
C.fold fold z (f a)
|
|
||||||
| OptBase (f,OptNil) ->
|
|
||||||
(* terminal fold *)
|
|
||||||
C.fold (fusion_fold f fold) z a
|
|
||||||
| OptBase (f,c) ->
|
|
||||||
(* make intermediate collection and continue *)
|
|
||||||
apply_optimized_with_fold c fold z (apply_base f a)
|
|
||||||
| OptExtern (f,c) -> apply_optimized_with_fold c fold z (f a)
|
|
||||||
| OptFlatMap (f,c) -> apply_optimized_with_fold c fold z (C.flat_map f a)
|
|
||||||
|
|
||||||
(* Optimize and run *)
|
|
||||||
let apply op a =
|
|
||||||
let op' = optimize op in
|
|
||||||
apply_optimized op' a
|
|
||||||
|
|
||||||
let apply_fold op fold z a =
|
|
||||||
let op' = optimize op in
|
|
||||||
apply_optimized_with_fold op' fold z a
|
|
||||||
|
|
||||||
let apply' a op = apply op a
|
|
||||||
|
|
||||||
(** {6 Combinators} *)
|
|
||||||
|
|
||||||
let id = Nil
|
|
||||||
let map f = Compose (Map f, Nil)
|
|
||||||
let filter p = Compose (Filter p, Nil)
|
|
||||||
let filter_map f = Compose (FilterMap f, Nil)
|
|
||||||
let flat_map f = Compose (FlatMap f, Nil)
|
|
||||||
let extern f = Compose (Extern f, Nil)
|
|
||||||
|
|
||||||
let compose f g = _compose g f
|
|
||||||
let (>>>) f g = _compose f g
|
|
||||||
end
|
|
||||||
|
|
@ -1,80 +0,0 @@
|
||||||
(*
|
|
||||||
copyright (c) 2013-2014, simon cruanes
|
|
||||||
all rights reserved.
|
|
||||||
|
|
||||||
redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer. redistributions in binary
|
|
||||||
form must reproduce the above copyright notice, this list of conditions and the
|
|
||||||
following disclaimer in the documentation and/or other materials provided with
|
|
||||||
the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*)
|
|
||||||
|
|
||||||
(** {1 Batch Operations on Collections}
|
|
||||||
Define and combine operations on a collection of elements, then
|
|
||||||
run the composition of those operations on some collection. The
|
|
||||||
composition is optimized to minimize the number of intermediate
|
|
||||||
collections *)
|
|
||||||
|
|
||||||
(** {2 Definition of a Collection} *)
|
|
||||||
module type COLLECTION = sig
|
|
||||||
type 'a t
|
|
||||||
|
|
||||||
val empty : 'a t
|
|
||||||
val fold : ('a -> 'b -> 'a) -> 'a -> 'b t -> 'a
|
|
||||||
val map : ('a -> 'b) -> 'a t -> 'b t
|
|
||||||
val filter : ('a -> bool) -> 'a t -> 'a t
|
|
||||||
val filter_map : ('a -> 'b option) -> 'a t -> 'b t
|
|
||||||
val flat_map : ('a -> 'b t) -> 'a t -> 'b t
|
|
||||||
end
|
|
||||||
|
|
||||||
(** {2 Definition of a Batch operations} *)
|
|
||||||
module type S = sig
|
|
||||||
type 'a t
|
|
||||||
|
|
||||||
type ('a,'b) op
|
|
||||||
(** Operation that converts a ['a t] into a ['b t] *)
|
|
||||||
|
|
||||||
val apply : ('a,'b) op -> 'a t -> 'b t
|
|
||||||
(** Apply the operation to the collection. *)
|
|
||||||
|
|
||||||
val apply_fold : ('a, 'b) op -> ('c -> 'b -> 'c) -> 'c -> 'a t -> 'c
|
|
||||||
(** Apply the operation plus a fold to the collection. *)
|
|
||||||
|
|
||||||
val apply' : 'a t -> ('a,'b) op -> 'b t
|
|
||||||
(** Flip of {!apply} *)
|
|
||||||
|
|
||||||
(** {6 Combinators} *)
|
|
||||||
|
|
||||||
val id : ('a, 'a) op
|
|
||||||
|
|
||||||
val map : ('a -> 'b) -> ('a, 'b) op
|
|
||||||
|
|
||||||
val filter : ('a -> bool) -> ('a,'a) op
|
|
||||||
|
|
||||||
val filter_map : ('a -> 'b option) -> ('a,'b) op
|
|
||||||
|
|
||||||
val flat_map : ('a -> 'b t) -> ('a,'b) op
|
|
||||||
|
|
||||||
val extern : ('a t -> 'b t) -> ('a,'b) op
|
|
||||||
(** Use a specific function that won't be optimized *)
|
|
||||||
|
|
||||||
val compose : ('b,'c) op -> ('a,'b) op -> ('a,'c) op
|
|
||||||
val (>>>) : ('a,'b) op -> ('b,'c) op -> ('a,'c) op
|
|
||||||
end
|
|
||||||
|
|
||||||
(** {2 Functor} *)
|
|
||||||
module Make(C : COLLECTION) : S with type 'a t = 'a C.t
|
|
||||||
|
|
@ -1,144 +0,0 @@
|
||||||
(*
|
|
||||||
copyright (c) 2013-2014, simon cruanes
|
|
||||||
all rights reserved.
|
|
||||||
|
|
||||||
redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer. redistributions in binary
|
|
||||||
form must reproduce the above copyright notice, this list of conditions and the
|
|
||||||
following disclaimer in the documentation and/or other materials provided with
|
|
||||||
the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*)
|
|
||||||
|
|
||||||
(** {1 Categorical Constructs} *)
|
|
||||||
|
|
||||||
(** {2 Signatures} *)
|
|
||||||
|
|
||||||
module type MONOID = sig
|
|
||||||
type t
|
|
||||||
val empty : t
|
|
||||||
val append : t -> t -> t
|
|
||||||
end
|
|
||||||
|
|
||||||
module type FUNCTOR = sig
|
|
||||||
type +'a t
|
|
||||||
val map : ('a -> 'b) -> 'a t -> 'b t
|
|
||||||
end
|
|
||||||
|
|
||||||
module type APPLICATIVE = sig
|
|
||||||
type +'a t
|
|
||||||
include FUNCTOR with type 'a t := 'a t
|
|
||||||
val pure : 'a -> 'a t
|
|
||||||
val (<*>) : ('a -> 'b) t -> 'a t -> 'b t
|
|
||||||
end
|
|
||||||
|
|
||||||
module type MONAD_BARE = sig
|
|
||||||
type +'a t
|
|
||||||
val return : 'a -> 'a t
|
|
||||||
val (>>=) : 'a t -> ('a -> 'b t) -> 'b t
|
|
||||||
end
|
|
||||||
|
|
||||||
module type MONAD = sig
|
|
||||||
include MONAD_BARE
|
|
||||||
include APPLICATIVE with type 'a t := 'a t
|
|
||||||
end
|
|
||||||
|
|
||||||
module type MONAD_TRANSFORMER = sig
|
|
||||||
include MONAD
|
|
||||||
module M : MONAD
|
|
||||||
val lift : 'a M.t -> 'a t
|
|
||||||
end
|
|
||||||
|
|
||||||
type 'a sequence = ('a -> unit) -> unit
|
|
||||||
|
|
||||||
module type FOLDABLE = sig
|
|
||||||
type 'a t
|
|
||||||
val to_seq : 'a t -> 'a sequence
|
|
||||||
end
|
|
||||||
|
|
||||||
module type TRAVERSE = functor(M : MONAD) -> sig
|
|
||||||
type +'a t
|
|
||||||
|
|
||||||
val sequence_m : 'a M.t t -> 'a t M.t
|
|
||||||
|
|
||||||
val fold_m : ('b -> 'a -> 'b M.t) -> 'b -> 'a t -> 'b M.t
|
|
||||||
|
|
||||||
val map_m : ('a -> 'b M.t) -> 'a t -> 'b t M.t
|
|
||||||
end
|
|
||||||
|
|
||||||
module type FREE_MONAD = sig
|
|
||||||
module F : FUNCTOR
|
|
||||||
|
|
||||||
type +'a t =
|
|
||||||
| Return of 'a
|
|
||||||
| Roll of 'a t F.t
|
|
||||||
|
|
||||||
include MONAD with type 'a t := 'a t
|
|
||||||
val inj : 'a F.t -> 'a t
|
|
||||||
end
|
|
||||||
|
|
||||||
(** {2 Some Implementations} *)
|
|
||||||
|
|
||||||
module WrapMonad(M : MONAD_BARE) = struct
|
|
||||||
include M
|
|
||||||
|
|
||||||
let map f x = x >>= (fun x -> return (f x))
|
|
||||||
|
|
||||||
let pure = return
|
|
||||||
|
|
||||||
let (<*>) f x = f >>= fun f -> x >>= fun x -> return (f x)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
module MakeFree(F : FUNCTOR) = struct
|
|
||||||
module F = F
|
|
||||||
|
|
||||||
type 'a t = Return of 'a | Roll of ('a t F.t)
|
|
||||||
|
|
||||||
let return x = Return x
|
|
||||||
let pure = return
|
|
||||||
|
|
||||||
let rec map : type a b. (a -> b) -> a t -> b t
|
|
||||||
= fun f x -> match x with
|
|
||||||
| Return x -> Return (f x)
|
|
||||||
| Roll xs -> Roll (F.map (map f) xs)
|
|
||||||
|
|
||||||
let rec _bind : type a b. (a -> b t) -> a t -> b t
|
|
||||||
= fun f x -> match x with
|
|
||||||
| Return x -> f x
|
|
||||||
| Roll y -> Roll (F.map (_bind f) y)
|
|
||||||
|
|
||||||
let (>>=) x f = _bind f x
|
|
||||||
|
|
||||||
let rec _app : type a b. (a -> b) t -> a t -> b t
|
|
||||||
= fun f x -> match f, x with
|
|
||||||
| Return f, Return x -> Return (f x)
|
|
||||||
| Return f, Roll xs -> Roll (F.map (map f) xs)
|
|
||||||
| Roll fs, _ -> Roll (F.map (fun f -> _app f x) fs)
|
|
||||||
|
|
||||||
let (<*>) = _app
|
|
||||||
|
|
||||||
let inj x = Roll (F.map return x)
|
|
||||||
end
|
|
||||||
|
|
||||||
module MakeFreeFold(FM : FREE_MONAD)(Fold : FOLDABLE with type 'a t = 'a FM.F.t) = struct
|
|
||||||
type 'a t = 'a FM.t
|
|
||||||
|
|
||||||
let rec to_seq : type a. a FM.t -> a sequence
|
|
||||||
= fun x k -> match x with
|
|
||||||
| FM.Return x -> k x
|
|
||||||
| FM.Roll xs -> Fold.to_seq xs (fun x -> to_seq x k)
|
|
||||||
end
|
|
||||||
|
|
@ -1,113 +0,0 @@
|
||||||
(*
|
|
||||||
copyright (c) 2013-2014, simon cruanes
|
|
||||||
all rights reserved.
|
|
||||||
|
|
||||||
redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer. redistributions in binary
|
|
||||||
form must reproduce the above copyright notice, this list of conditions and the
|
|
||||||
following disclaimer in the documentation and/or other materials provided with
|
|
||||||
the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*)
|
|
||||||
|
|
||||||
(** {1 Categorical Constructs}
|
|
||||||
|
|
||||||
Attempt to copy some structures from Haskell and the likes. Disclaimer:
|
|
||||||
I don't know much about category theory, only about type signatures ;). *)
|
|
||||||
|
|
||||||
(** {2 Signatures} *)
|
|
||||||
|
|
||||||
module type MONOID = sig
|
|
||||||
type t
|
|
||||||
val empty : t
|
|
||||||
val append : t -> t -> t
|
|
||||||
end
|
|
||||||
|
|
||||||
module type FUNCTOR = sig
|
|
||||||
type +'a t
|
|
||||||
val map : ('a -> 'b) -> 'a t -> 'b t
|
|
||||||
end
|
|
||||||
|
|
||||||
module type APPLICATIVE = sig
|
|
||||||
type +'a t
|
|
||||||
include FUNCTOR with type 'a t := 'a t
|
|
||||||
val pure : 'a -> 'a t
|
|
||||||
val (<*>) : ('a -> 'b) t -> 'a t -> 'b t
|
|
||||||
end
|
|
||||||
|
|
||||||
module type MONAD_BARE = sig
|
|
||||||
type +'a t
|
|
||||||
val return : 'a -> 'a t
|
|
||||||
val (>>=) : 'a t -> ('a -> 'b t) -> 'b t
|
|
||||||
end
|
|
||||||
|
|
||||||
module type MONAD = sig
|
|
||||||
include MONAD_BARE
|
|
||||||
include APPLICATIVE with type 'a t := 'a t
|
|
||||||
end
|
|
||||||
|
|
||||||
module type MONAD_TRANSFORMER = sig
|
|
||||||
include MONAD
|
|
||||||
module M : MONAD
|
|
||||||
val lift : 'a M.t -> 'a t
|
|
||||||
end
|
|
||||||
|
|
||||||
(** Cheating: use an equivalent of "to List" with a sequence *)
|
|
||||||
type 'a sequence = ('a -> unit) -> unit
|
|
||||||
|
|
||||||
module type FOLDABLE = sig
|
|
||||||
type 'a t
|
|
||||||
val to_seq : 'a t -> 'a sequence
|
|
||||||
end
|
|
||||||
|
|
||||||
module type TRAVERSE = functor(M : MONAD) -> sig
|
|
||||||
type +'a t
|
|
||||||
|
|
||||||
val sequence_m : 'a M.t t -> 'a t M.t
|
|
||||||
|
|
||||||
val fold_m : ('b -> 'a -> 'b M.t) -> 'b -> 'a t -> 'b M.t
|
|
||||||
|
|
||||||
val map_m : ('a -> 'b M.t) -> 'a t -> 'b t M.t
|
|
||||||
end
|
|
||||||
|
|
||||||
(** The free monad is built by nesting applications of a functor [F].
|
|
||||||
|
|
||||||
For instance, Lisp-like nested lists can be built and dealt with like this:
|
|
||||||
{[
|
|
||||||
module Lisp = CCCat.FreeMonad(CCList);;
|
|
||||||
|
|
||||||
let l = Lisp.(inj [1;2;3] >>= fun x -> inj [x; x*2; x+100]);;
|
|
||||||
]} *)
|
|
||||||
module type FREE_MONAD = sig
|
|
||||||
module F : FUNCTOR
|
|
||||||
|
|
||||||
type +'a t =
|
|
||||||
| Return of 'a
|
|
||||||
| Roll of 'a t F.t
|
|
||||||
|
|
||||||
include MONAD with type 'a t := 'a t
|
|
||||||
val inj : 'a F.t -> 'a t
|
|
||||||
end
|
|
||||||
|
|
||||||
(** {2 Some Implementations} *)
|
|
||||||
|
|
||||||
(** Implement the applicative and functor modules from only return and bind *)
|
|
||||||
module WrapMonad(M : MONAD_BARE) : MONAD with type 'a t = 'a M.t
|
|
||||||
|
|
||||||
module MakeFree(F : FUNCTOR) : FREE_MONAD with module F = F
|
|
||||||
|
|
||||||
module MakeFreeFold(FM : FREE_MONAD)(Fold : FOLDABLE with type 'a t = 'a FM.F.t)
|
|
||||||
: FOLDABLE with type 'a t = 'a FM.t
|
|
||||||
|
|
@ -1,888 +0,0 @@
|
||||||
(*
|
|
||||||
copyright (c) 2013-2014, simon cruanes
|
|
||||||
all rights reserved.
|
|
||||||
|
|
||||||
redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer. redistributions in binary
|
|
||||||
form must reproduce the above copyright notice, this list of conditions and the
|
|
||||||
following disclaimer in the documentation and/or other materials provided with
|
|
||||||
the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*)
|
|
||||||
|
|
||||||
(** {1 LINQ-like operations on collections} *)
|
|
||||||
|
|
||||||
type 'a sequence = ('a -> unit) -> unit
|
|
||||||
type 'a equal = 'a -> 'a -> bool
|
|
||||||
type 'a ord = 'a -> 'a -> int
|
|
||||||
type 'a hash = 'a -> int
|
|
||||||
type 'a with_err = [`Ok of 'a | `Error of string ]
|
|
||||||
|
|
||||||
let _id x = x
|
|
||||||
|
|
||||||
exception ExitWithError of string
|
|
||||||
let _exit_with_error s = raise (ExitWithError s)
|
|
||||||
let _error_of_exn f = try `Ok (f ()) with ExitWithError s -> `Error s
|
|
||||||
|
|
||||||
module PMap = struct
|
|
||||||
type ('a, 'b) t = {
|
|
||||||
is_empty : unit -> bool;
|
|
||||||
size : unit -> int; (* Number of keys *)
|
|
||||||
get : 'a -> 'b option;
|
|
||||||
fold : 'c. ('c -> 'a -> 'b -> 'c) -> 'c -> 'c;
|
|
||||||
to_seq : ('a * 'b) sequence;
|
|
||||||
}
|
|
||||||
|
|
||||||
let get m x = m.get x
|
|
||||||
let mem m x = match m.get x with
|
|
||||||
| None -> false
|
|
||||||
| Some _ -> true
|
|
||||||
let to_seq m = m.to_seq
|
|
||||||
let fold f acc m = m.fold f acc
|
|
||||||
let size m = m.size ()
|
|
||||||
|
|
||||||
type ('a, 'b) build = {
|
|
||||||
mutable cur : ('a, 'b) t;
|
|
||||||
add : 'a -> 'b -> unit;
|
|
||||||
update : 'a -> ('b option -> 'b option) -> unit;
|
|
||||||
}
|
|
||||||
|
|
||||||
let build_get b = b.cur
|
|
||||||
let add b x y = b.add x y
|
|
||||||
let update b f = b.update f
|
|
||||||
|
|
||||||
(* careful to use this map linearly *)
|
|
||||||
let make_hash (type key) ?(eq=(=)) ?(hash=Hashtbl.hash) () =
|
|
||||||
let module H = Hashtbl.Make(struct
|
|
||||||
type t = key
|
|
||||||
let equal = eq
|
|
||||||
let hash = hash
|
|
||||||
end) in
|
|
||||||
(* build table *)
|
|
||||||
let tbl = H.create 32 in
|
|
||||||
let cur = {
|
|
||||||
is_empty = (fun () -> H.length tbl = 0);
|
|
||||||
size = (fun () -> H.length tbl);
|
|
||||||
get = (fun k ->
|
|
||||||
try Some (H.find tbl k)
|
|
||||||
with Not_found -> None);
|
|
||||||
fold = (fun f acc -> H.fold (fun k v acc -> f acc k v) tbl acc);
|
|
||||||
to_seq = (fun k -> H.iter (fun key v -> k (key,v)) tbl);
|
|
||||||
} in
|
|
||||||
{ cur;
|
|
||||||
add = (fun k v -> H.replace tbl k v);
|
|
||||||
update = (fun k f ->
|
|
||||||
match (try f (Some (H.find tbl k)) with Not_found -> f None) with
|
|
||||||
| None -> H.remove tbl k
|
|
||||||
| Some v' -> H.replace tbl k v');
|
|
||||||
}
|
|
||||||
|
|
||||||
let make_cmp (type key) ?(cmp=Pervasives.compare) () =
|
|
||||||
let module M = Sequence.Map.Make(struct
|
|
||||||
type t = key
|
|
||||||
let compare = cmp
|
|
||||||
end) in
|
|
||||||
let map = ref M.empty in
|
|
||||||
let cur = {
|
|
||||||
is_empty = (fun () -> M.is_empty !map);
|
|
||||||
size = (fun () -> M.cardinal !map);
|
|
||||||
get = (fun k ->
|
|
||||||
try Some (M.find k !map)
|
|
||||||
with Not_found -> None);
|
|
||||||
fold = (fun f acc ->
|
|
||||||
M.fold
|
|
||||||
(fun key set acc -> f acc key set) !map acc
|
|
||||||
);
|
|
||||||
to_seq = (fun k -> M.to_seq !map k);
|
|
||||||
} in
|
|
||||||
{
|
|
||||||
cur;
|
|
||||||
add = (fun k v -> map := M.add k v !map);
|
|
||||||
update = (fun k f ->
|
|
||||||
match (try f (Some (M.find k !map)) with Not_found -> f None) with
|
|
||||||
| None -> map := M.remove k !map
|
|
||||||
| Some v' -> map := M.add k v' !map);
|
|
||||||
}
|
|
||||||
|
|
||||||
type 'a build_method =
|
|
||||||
| FromCmp of 'a ord
|
|
||||||
| FromHash of 'a equal * 'a hash
|
|
||||||
| Default
|
|
||||||
|
|
||||||
let make ?(build=Default) () = match build with
|
|
||||||
| Default -> make_hash ()
|
|
||||||
| FromCmp cmp -> make_cmp ~cmp ()
|
|
||||||
| FromHash (eq,hash) -> make_hash ~eq ~hash ()
|
|
||||||
|
|
||||||
(* choose a build method from the optional arguments *)
|
|
||||||
let _make_build ?cmp ?eq ?hash () =
|
|
||||||
let _maybe default o = match o with
|
|
||||||
| Some x -> x
|
|
||||||
| None -> default
|
|
||||||
in
|
|
||||||
match eq, hash with
|
|
||||||
| Some _, _
|
|
||||||
| _, Some _ ->
|
|
||||||
FromHash ( _maybe (=) eq, _maybe Hashtbl.hash hash)
|
|
||||||
| _ ->
|
|
||||||
match cmp with
|
|
||||||
| Some f -> FromCmp f
|
|
||||||
| _ -> Default
|
|
||||||
|
|
||||||
let multimap_of_seq ?(build=make ()) seq =
|
|
||||||
seq (fun (k,v) ->
|
|
||||||
build.update k (function
|
|
||||||
| None -> Some [v]
|
|
||||||
| Some l -> Some (v::l)));
|
|
||||||
build.cur
|
|
||||||
|
|
||||||
let count_of_seq ?(build=make ()) seq =
|
|
||||||
seq (fun x ->
|
|
||||||
build.update x
|
|
||||||
(function
|
|
||||||
| None -> Some 1
|
|
||||||
| Some n -> Some (n+1)));
|
|
||||||
build.cur
|
|
||||||
|
|
||||||
(* map values *)
|
|
||||||
let map f m = {
|
|
||||||
is_empty = m.is_empty;
|
|
||||||
size = m.size;
|
|
||||||
get = (fun k -> match m.get k with
|
|
||||||
| None -> None
|
|
||||||
| Some v -> Some (f v)
|
|
||||||
);
|
|
||||||
to_seq = Sequence.map (fun (x,y) -> x, f y) m.to_seq;
|
|
||||||
fold = (fun f' acc ->
|
|
||||||
m.fold (fun acc x y -> f' acc x (f y)) acc
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let to_list m = Sequence.to_rev_list m.to_seq
|
|
||||||
|
|
||||||
let reverse_ ~build m =
|
|
||||||
let build = make ~build () in
|
|
||||||
let seq = Sequence.map (fun (x,y) -> y,x) (to_seq m) in
|
|
||||||
multimap_of_seq ~build seq
|
|
||||||
|
|
||||||
let reverse_multimap_ ~build m =
|
|
||||||
let build = make ~build () in
|
|
||||||
let seq = to_seq m in
|
|
||||||
let seq = Sequence.flat_map
|
|
||||||
(fun (x,l) -> Sequence.map (fun y -> y,x) (Sequence.of_list l)
|
|
||||||
) seq
|
|
||||||
in
|
|
||||||
multimap_of_seq ~build seq
|
|
||||||
|
|
||||||
let reverse ?cmp ?eq ?hash () m =
|
|
||||||
let build = _make_build ?cmp ?eq ?hash () in
|
|
||||||
reverse_ ~build m
|
|
||||||
|
|
||||||
let reverse_multimap ?cmp ?eq ?hash () m =
|
|
||||||
let build = _make_build ?cmp ?eq ?hash () in
|
|
||||||
reverse_multimap_ ~build m
|
|
||||||
|
|
||||||
let fold_multimap f acc m =
|
|
||||||
m.fold (fun acc x l -> List.fold_left (fun acc y -> f acc x y) acc l) acc
|
|
||||||
|
|
||||||
let get_seq key m = match get m key with
|
|
||||||
| None -> Sequence.empty
|
|
||||||
| Some x -> Sequence.return x
|
|
||||||
|
|
||||||
let iter m = m.to_seq
|
|
||||||
|
|
||||||
let flatten m =
|
|
||||||
let seq = Sequence.flat_map
|
|
||||||
(fun (k,v) -> Sequence.map (fun v' -> k,v') v)
|
|
||||||
m.to_seq
|
|
||||||
in
|
|
||||||
seq
|
|
||||||
|
|
||||||
let flatten_l m =
|
|
||||||
let seq = Sequence.flatMap
|
|
||||||
(fun (k,v) -> Sequence.map (fun v' -> k,v') (Sequence.of_list v))
|
|
||||||
m.to_seq
|
|
||||||
in
|
|
||||||
seq
|
|
||||||
end
|
|
||||||
|
|
||||||
type 'a search_result =
|
|
||||||
| SearchContinue
|
|
||||||
| SearchStop of 'a
|
|
||||||
|
|
||||||
type ('a,'b,'key,'c) join_descr = {
|
|
||||||
join_key1 : 'a -> 'key;
|
|
||||||
join_key2 : 'b -> 'key;
|
|
||||||
join_merge : 'key -> 'a -> 'b -> 'c option;
|
|
||||||
join_build : 'key PMap.build_method;
|
|
||||||
}
|
|
||||||
|
|
||||||
type ('a,'b) group_join_descr = {
|
|
||||||
gjoin_proj : 'b -> 'a;
|
|
||||||
gjoin_build : 'a PMap.build_method;
|
|
||||||
}
|
|
||||||
|
|
||||||
module ImplemSetOps = struct
|
|
||||||
let choose s = Sequence.take 1 s
|
|
||||||
|
|
||||||
let distinct ~cmp s = Sequence.sort_uniq ~cmp s
|
|
||||||
|
|
||||||
let search obj s =
|
|
||||||
match
|
|
||||||
Sequence.find
|
|
||||||
(fun x -> match obj#check x with
|
|
||||||
| SearchContinue -> None
|
|
||||||
| SearchStop y -> Some y
|
|
||||||
) s
|
|
||||||
with None -> obj#failure
|
|
||||||
| Some x -> x
|
|
||||||
|
|
||||||
let do_join ~join c1 c2 =
|
|
||||||
let build1 =
|
|
||||||
let seq = Sequence.map (fun x -> join.join_key1 x, x) c1 in
|
|
||||||
PMap.multimap_of_seq ~build:(PMap.make ~build:join.join_build ()) seq
|
|
||||||
in
|
|
||||||
let l = Sequence.fold
|
|
||||||
(fun acc y ->
|
|
||||||
let key = join.join_key2 y in
|
|
||||||
match PMap.get build1 key with
|
|
||||||
| None -> acc
|
|
||||||
| Some l1 ->
|
|
||||||
List.fold_left
|
|
||||||
(fun acc x -> match join.join_merge key x y with
|
|
||||||
| None -> acc
|
|
||||||
| Some res -> res::acc
|
|
||||||
) acc l1
|
|
||||||
) [] c2
|
|
||||||
in
|
|
||||||
Sequence.of_list l
|
|
||||||
|
|
||||||
let do_group_join ~gjoin c1 c2 =
|
|
||||||
let build = PMap.make ~build:gjoin.gjoin_build () in
|
|
||||||
c1 (fun x -> PMap.add build x []);
|
|
||||||
c2
|
|
||||||
(fun y ->
|
|
||||||
(* project [y] into some element of [c1] *)
|
|
||||||
let x = gjoin.gjoin_proj y in
|
|
||||||
PMap.update build x
|
|
||||||
(function
|
|
||||||
| None -> None (* [x] not present, ignore! *)
|
|
||||||
| Some l -> Some (y::l)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
PMap.build_get build
|
|
||||||
|
|
||||||
let do_union ~build c1 c2 =
|
|
||||||
let build = PMap.make ~build () in
|
|
||||||
c1 (fun x -> PMap.add build x ());
|
|
||||||
c2 (fun x -> PMap.add build x ());
|
|
||||||
let seq = PMap.to_seq (PMap.build_get build) in
|
|
||||||
Sequence.map fst seq
|
|
||||||
|
|
||||||
type inter_status =
|
|
||||||
| InterLeft
|
|
||||||
| InterDone (* already output *)
|
|
||||||
|
|
||||||
let do_inter ~build c1 c2 =
|
|
||||||
let build = PMap.make ~build () in
|
|
||||||
let l = ref [] in
|
|
||||||
c1 (fun x -> PMap.add build x InterLeft);
|
|
||||||
c2 (fun x ->
|
|
||||||
PMap.update build x
|
|
||||||
(function
|
|
||||||
| None -> Some InterDone
|
|
||||||
| Some InterDone as foo -> foo
|
|
||||||
| Some InterLeft ->
|
|
||||||
l := x :: !l;
|
|
||||||
Some InterDone
|
|
||||||
)
|
|
||||||
);
|
|
||||||
Sequence.of_list !l
|
|
||||||
|
|
||||||
let do_diff ~build c1 c2 =
|
|
||||||
let build = PMap.make ~build () in
|
|
||||||
c2 (fun x -> PMap.add build x ());
|
|
||||||
let map = PMap.build_get build in
|
|
||||||
(* output elements of [c1] not in [map] *)
|
|
||||||
Sequence.filter (fun x -> not (PMap.mem map x)) c1
|
|
||||||
end
|
|
||||||
|
|
||||||
(** {2 Query operators} *)
|
|
||||||
|
|
||||||
type (_, _) unary =
|
|
||||||
| Map : ('a -> 'b) -> ('a, 'b ) unary
|
|
||||||
| Filter : ('a -> bool) -> ('a, 'a ) unary
|
|
||||||
| Fold : ('b -> 'a -> 'b) * 'b -> ('a, 'b) unary
|
|
||||||
| Reduce : ('a -> 'b) * ('a -> 'b -> 'b) * ('b -> 'c)
|
|
||||||
-> ('a, 'c) unary
|
|
||||||
| Size : ('a, int) unary
|
|
||||||
| Choose : ('a, 'a) unary
|
|
||||||
| FilterMap : ('a -> 'b option) -> ('a, 'b) unary
|
|
||||||
| FlatMap : ('a -> 'b sequence) -> ('a, 'b) unary
|
|
||||||
| Take : int -> ('a, 'a) unary
|
|
||||||
| TakeWhile : ('a -> bool) -> ('a, 'a) unary
|
|
||||||
| Sort : 'a ord -> ('a, 'a) unary
|
|
||||||
| Distinct : 'a ord -> ('a, 'a) unary
|
|
||||||
| Search :
|
|
||||||
< check: ('a -> 'b search_result);
|
|
||||||
failure : 'b;
|
|
||||||
> -> ('a, 'b) unary
|
|
||||||
| Contains : 'a equal * 'a -> ('a, bool) unary
|
|
||||||
| GroupBy : 'b PMap.build_method * ('a -> 'b)
|
|
||||||
-> ('a, ('b,'a list) PMap.t) unary
|
|
||||||
| Count : 'a PMap.build_method -> ('a, ('a, int) PMap.t) unary
|
|
||||||
| Lazy : ('a lazy_t, 'a) unary
|
|
||||||
|
|
||||||
type set_op =
|
|
||||||
| Union
|
|
||||||
| Inter
|
|
||||||
| Diff
|
|
||||||
|
|
||||||
type (_, _, _) binary =
|
|
||||||
| App : ('a -> 'b, 'a, 'b) binary
|
|
||||||
| Join : ('a, 'b, 'key, 'c) join_descr
|
|
||||||
-> ('a, 'b, 'c) binary
|
|
||||||
| GroupJoin : ('a, 'b) group_join_descr
|
|
||||||
-> ('a, 'b, ('a, 'b list) PMap.t) binary
|
|
||||||
| Product : ('a, 'b, ('a*'b)) binary
|
|
||||||
| Append : ('a, 'a, 'a) binary
|
|
||||||
| SetOp : set_op * 'a PMap.build_method
|
|
||||||
-> ('a, 'a, 'a) binary
|
|
||||||
|
|
||||||
(* type of queries that return a 'a *)
|
|
||||||
and 'a t =
|
|
||||||
| Return : 'a -> 'a t
|
|
||||||
| OfSeq : 'a sequence -> 'a t
|
|
||||||
| Unary : ('a, 'b) unary * 'a t -> 'b t
|
|
||||||
| Binary : ('a, 'b, 'c) binary * 'a t * 'b t -> 'c t
|
|
||||||
| Bind : ('a -> 'b t) * 'a t -> 'b t
|
|
||||||
| Reflect : 'a t -> 'a sequence t
|
|
||||||
|
|
||||||
let start x = Return x
|
|
||||||
|
|
||||||
let of_list l =
|
|
||||||
OfSeq (Sequence.of_list l)
|
|
||||||
|
|
||||||
let of_array a =
|
|
||||||
OfSeq (Sequence.of_array a)
|
|
||||||
|
|
||||||
let of_array_i a =
|
|
||||||
OfSeq (Sequence.of_array_i a)
|
|
||||||
|
|
||||||
let of_hashtbl h =
|
|
||||||
OfSeq (Sequence.of_hashtbl h)
|
|
||||||
|
|
||||||
let range i j = OfSeq (Sequence.int_range ~start:i ~stop:j)
|
|
||||||
|
|
||||||
let (--) = range
|
|
||||||
|
|
||||||
let of_seq seq =
|
|
||||||
OfSeq seq
|
|
||||||
|
|
||||||
let of_queue q =
|
|
||||||
OfSeq (Sequence.of_queue q)
|
|
||||||
|
|
||||||
let of_stack s =
|
|
||||||
OfSeq (Sequence.of_stack s)
|
|
||||||
|
|
||||||
let of_string s =
|
|
||||||
OfSeq (Sequence.of_str s)
|
|
||||||
|
|
||||||
(** {6 Execution} *)
|
|
||||||
|
|
||||||
let rec _optimize : type a. a t -> a t
|
|
||||||
= fun q -> match q with
|
|
||||||
| Return _ -> q
|
|
||||||
| Unary (u, q) ->
|
|
||||||
_optimize_unary u (_optimize q)
|
|
||||||
| Binary (b, q1, q2) ->
|
|
||||||
_optimize_binary b (_optimize q1) (_optimize q2)
|
|
||||||
| Reflect q -> Reflect (_optimize q)
|
|
||||||
| OfSeq _ -> q
|
|
||||||
| Bind (f,q) -> Bind(f, _optimize q) (* cannot optimize [f] before execution *)
|
|
||||||
and _optimize_unary : type a b. (a,b) unary -> a t -> b t
|
|
||||||
= fun u q -> match u, q with
|
|
||||||
| Size, Unary (Choose, _) -> Return 1
|
|
||||||
| Map f, Unary (Map g, q') ->
|
|
||||||
_optimize_unary (Map (fun x -> f (g x))) q'
|
|
||||||
| Filter p, Unary (Map f, cont) ->
|
|
||||||
_optimize_unary
|
|
||||||
(FilterMap (fun x -> let y = f x in if p y then Some y else None))
|
|
||||||
cont
|
|
||||||
| Filter p, Unary (Filter p', q) ->
|
|
||||||
_optimize_unary (Filter (fun x -> p x && p' x)) q
|
|
||||||
| FilterMap f, Unary (Map g, q') ->
|
|
||||||
_optimize_unary (FilterMap (fun x -> f (g x))) q'
|
|
||||||
| Map f, Unary (Filter p, cont) ->
|
|
||||||
_optimize_unary
|
|
||||||
(FilterMap (fun x -> if p x then Some (f x) else None))
|
|
||||||
cont
|
|
||||||
| Map _, Binary (Append, q1, q2) ->
|
|
||||||
_optimize_binary Append (Unary (u, q1)) (Unary (u, q2))
|
|
||||||
| Filter _, Binary (Append, q1, q2) ->
|
|
||||||
_optimize_binary Append (Unary (u, q1)) (Unary (u, q2))
|
|
||||||
| Fold (f,acc), Unary (Map f', cont) ->
|
|
||||||
_optimize_unary
|
|
||||||
(Fold ((fun acc x -> f acc (f' x)), acc))
|
|
||||||
cont
|
|
||||||
| Reduce (start, mix, stop), Unary (Map f, cont) ->
|
|
||||||
_optimize_unary
|
|
||||||
(Reduce (
|
|
||||||
(fun x -> start (f x)),
|
|
||||||
(fun x acc -> mix (f x) acc),
|
|
||||||
stop))
|
|
||||||
cont
|
|
||||||
| Size, Unary (Map _, cont) ->
|
|
||||||
_optimize_unary Size cont (* ignore the map! *)
|
|
||||||
| Size, Unary (Sort _, cont) ->
|
|
||||||
_optimize_unary Size cont
|
|
||||||
| _ -> Unary (u, _optimize q)
|
|
||||||
(* TODO: other cases *)
|
|
||||||
and _optimize_binary : type a b c. (a,b,c) binary -> a t -> b t -> c t
|
|
||||||
= fun b q1 q2 -> match b, q1, q2 with
|
|
||||||
| App, Return f, Return x -> Return (f x)
|
|
||||||
| App, Return f, x -> _optimize_unary (Map f) x
|
|
||||||
| App, f, Return x -> _optimize_unary (Map (fun f -> f x)) f
|
|
||||||
| App, _, _ -> Binary (b, _optimize q1, _optimize q2)
|
|
||||||
| Join _, _, _ -> Binary (b, _optimize q1, _optimize q2)
|
|
||||||
| GroupJoin _, _, _ -> Binary (b, _optimize q1, _optimize q2)
|
|
||||||
| Product, _, _ -> Binary (b, _optimize q1, _optimize q2)
|
|
||||||
| Append, _, _ -> Binary (b, _optimize q1, _optimize q2)
|
|
||||||
| SetOp _, _, _ -> Binary (b, _optimize q1, _optimize q2)
|
|
||||||
|
|
||||||
(* apply a unary operator on a collection *)
|
|
||||||
let _do_unary : type a b. (a,b) unary -> a sequence -> b sequence
|
|
||||||
= fun u c -> match u with
|
|
||||||
| Map f -> Sequence.map f c
|
|
||||||
| Filter p -> Sequence.filter p c
|
|
||||||
| Fold (f, acc) -> Sequence.return (Sequence.fold f acc c)
|
|
||||||
| Reduce (start, mix, stop) ->
|
|
||||||
let acc = Sequence.fold
|
|
||||||
(fun acc x -> match acc with
|
|
||||||
| None -> Some (start x)
|
|
||||||
| Some acc -> Some (mix x acc)
|
|
||||||
) None c
|
|
||||||
in
|
|
||||||
begin match acc with
|
|
||||||
| None -> Sequence.empty
|
|
||||||
| Some x -> Sequence.return (stop x)
|
|
||||||
end
|
|
||||||
| Size -> Sequence.return (Sequence.length c)
|
|
||||||
| Choose -> ImplemSetOps.choose c
|
|
||||||
| FilterMap f -> Sequence.filter_map f c
|
|
||||||
| FlatMap f -> Sequence.flat_map f c
|
|
||||||
| Take n -> Sequence.take n c
|
|
||||||
| TakeWhile p -> Sequence.take_while p c
|
|
||||||
| Sort cmp -> Sequence.sort ~cmp c
|
|
||||||
| Distinct cmp -> ImplemSetOps.distinct ~cmp c
|
|
||||||
| Search obj -> Sequence.return (ImplemSetOps.search obj c)
|
|
||||||
| GroupBy (build,f) ->
|
|
||||||
let seq = Sequence.map (fun x -> f x, x) c in
|
|
||||||
Sequence.return (PMap.multimap_of_seq ~build:(PMap.make ~build ()) seq)
|
|
||||||
| Contains (eq, x) -> Sequence.return (Sequence.mem ~eq x c)
|
|
||||||
| Count build ->
|
|
||||||
Sequence.return (PMap.count_of_seq ~build:(PMap.make ~build ()) c)
|
|
||||||
| Lazy -> Sequence.map Lazy.force c
|
|
||||||
|
|
||||||
let _do_binary : type a b c. (a, b, c) binary -> a sequence -> b sequence -> c sequence
|
|
||||||
= fun b c1 c2 -> match b with
|
|
||||||
| Join join -> ImplemSetOps.do_join ~join c1 c2
|
|
||||||
| GroupJoin gjoin -> Sequence.return (ImplemSetOps.do_group_join ~gjoin c1 c2)
|
|
||||||
| Product -> Sequence.product c1 c2
|
|
||||||
| Append -> Sequence.append c1 c2
|
|
||||||
| App -> Sequence.(c1 <*> c2)
|
|
||||||
| SetOp (Inter,build) -> ImplemSetOps.do_inter ~build c1 c2
|
|
||||||
| SetOp (Union,build) -> ImplemSetOps.do_union ~build c1 c2
|
|
||||||
| SetOp (Diff,build) -> ImplemSetOps.do_diff ~build c1 c2
|
|
||||||
|
|
||||||
let rec _run : type a. opt:bool -> a t -> a sequence
|
|
||||||
= fun ~opt q -> match q with
|
|
||||||
| Return c -> Sequence.return c
|
|
||||||
| Unary (u, q') -> _do_unary u (_run ~opt q')
|
|
||||||
| Binary (b, q1, q2) -> _do_binary b (_run ~opt q1) (_run ~opt q2)
|
|
||||||
| OfSeq s -> s
|
|
||||||
| Bind (f, q') ->
|
|
||||||
let seq = _run ~opt q' in
|
|
||||||
Sequence.flat_map
|
|
||||||
(fun x ->
|
|
||||||
let q'' = f x in
|
|
||||||
let q'' = if opt then _optimize q'' else q'' in
|
|
||||||
_run ~opt q''
|
|
||||||
) seq
|
|
||||||
| Reflect q ->
|
|
||||||
let seq = Sequence.persistent_lazy (_run ~opt q) in
|
|
||||||
Sequence.return seq
|
|
||||||
|
|
||||||
let _apply_limit ?limit seq = match limit with
|
|
||||||
| None -> seq
|
|
||||||
| Some l -> Sequence.take l seq
|
|
||||||
|
|
||||||
(* safe execution *)
|
|
||||||
let run ?limit q =
|
|
||||||
let seq = _run ~opt:true (_optimize q) in
|
|
||||||
_apply_limit ?limit seq
|
|
||||||
|
|
||||||
let run_no_optim ?limit q =
|
|
||||||
let seq = _run ~opt:false q in
|
|
||||||
_apply_limit ?limit seq
|
|
||||||
|
|
||||||
let run1 q =
|
|
||||||
let seq = _run ~opt:true (_optimize q) in
|
|
||||||
match Sequence.head seq with
|
|
||||||
| Some x -> x
|
|
||||||
| None -> raise Not_found
|
|
||||||
|
|
||||||
(** {6 Basics} *)
|
|
||||||
|
|
||||||
let empty = OfSeq Sequence.empty
|
|
||||||
|
|
||||||
let map f q = Unary (Map f, q)
|
|
||||||
|
|
||||||
let (>|=) q f = Unary (Map f, q)
|
|
||||||
|
|
||||||
let filter p q = Unary (Filter p, q)
|
|
||||||
|
|
||||||
let choose q = Unary (Choose, q)
|
|
||||||
|
|
||||||
let filter_map f q = Unary (FilterMap f, q)
|
|
||||||
|
|
||||||
let flat_map f q = Unary (FlatMap f, q)
|
|
||||||
|
|
||||||
let flat_map_l f q =
|
|
||||||
let f' x = Sequence.of_list (f x) in
|
|
||||||
Unary (FlatMap f', q)
|
|
||||||
|
|
||||||
let flatten_seq q = Unary (FlatMap (fun x->x), q)
|
|
||||||
|
|
||||||
let flatten q = Unary (FlatMap Sequence.of_list, q)
|
|
||||||
|
|
||||||
let take n q = Unary (Take n, q)
|
|
||||||
|
|
||||||
let take_while p q = Unary (TakeWhile p, q)
|
|
||||||
|
|
||||||
let sort ?(cmp=Pervasives.compare) () q = Unary (Sort cmp, q)
|
|
||||||
|
|
||||||
let distinct ?(cmp=Pervasives.compare) () q =
|
|
||||||
Unary (Distinct cmp, q)
|
|
||||||
|
|
||||||
let group_by ?cmp ?eq ?hash f q =
|
|
||||||
Unary (GroupBy (PMap._make_build ?cmp ?eq ?hash (),f), q)
|
|
||||||
|
|
||||||
let group_by' ?cmp ?eq ?hash f q =
|
|
||||||
flat_map PMap.iter (group_by ?cmp ?eq ?hash f q)
|
|
||||||
|
|
||||||
let count ?cmp ?eq ?hash () q =
|
|
||||||
Unary (Count (PMap._make_build ?cmp ?eq ?hash ()), q)
|
|
||||||
|
|
||||||
let count' ?cmp () q =
|
|
||||||
flat_map PMap.iter (count ?cmp () q)
|
|
||||||
|
|
||||||
let fold f acc q =
|
|
||||||
Unary (Fold (f, acc), q)
|
|
||||||
|
|
||||||
let size q = Unary (Size, q)
|
|
||||||
|
|
||||||
let sum q = Unary (Fold ((+), 0), q)
|
|
||||||
|
|
||||||
let reduce start mix stop q =
|
|
||||||
Unary (Reduce (start,mix,stop), q)
|
|
||||||
|
|
||||||
let _avg_start x = (x,1)
|
|
||||||
let _avg_mix x (y,n) = (x+y,n+1)
|
|
||||||
let _avg_stop (x,n) = x/n
|
|
||||||
|
|
||||||
let _lift_some f x y = match y with
|
|
||||||
| None -> Some x
|
|
||||||
| Some y -> Some (f x y)
|
|
||||||
|
|
||||||
let max q = Unary (Reduce (_id, Pervasives.max, _id), q)
|
|
||||||
let min q = Unary (Reduce (_id, Pervasives.min, _id), q)
|
|
||||||
let average q = Unary (Reduce (_avg_start, _avg_mix, _avg_stop), q)
|
|
||||||
|
|
||||||
let is_empty q =
|
|
||||||
Unary (Search (object
|
|
||||||
method check _ = SearchStop false (* stop in case there is an element *)
|
|
||||||
method failure = true
|
|
||||||
end), q)
|
|
||||||
|
|
||||||
let contains ?(eq=(=)) x q =
|
|
||||||
Unary (Contains (eq, x), q)
|
|
||||||
|
|
||||||
let for_all p q =
|
|
||||||
Unary (Search (object
|
|
||||||
method check x = if p x then SearchContinue else SearchStop false
|
|
||||||
method failure = true
|
|
||||||
end), q)
|
|
||||||
|
|
||||||
let exists p q =
|
|
||||||
Unary (Search (object
|
|
||||||
method check x = if p x then SearchStop true else SearchContinue
|
|
||||||
method failure = false
|
|
||||||
end), q)
|
|
||||||
|
|
||||||
let find p q =
|
|
||||||
Unary (Search (object
|
|
||||||
method check x = if p x then SearchStop (Some x) else SearchContinue
|
|
||||||
method failure = None
|
|
||||||
end), q)
|
|
||||||
|
|
||||||
let find_map f q =
|
|
||||||
Unary (Search (object
|
|
||||||
method check x = match f x with
|
|
||||||
| Some y -> SearchStop (Some y)
|
|
||||||
| None -> SearchContinue
|
|
||||||
method failure = None
|
|
||||||
end), q)
|
|
||||||
|
|
||||||
(** {6 Binary Operators} *)
|
|
||||||
|
|
||||||
let join ?cmp ?eq ?hash join_key1 join_key2 ~merge q1 q2 =
|
|
||||||
let join_build = PMap._make_build ?eq ?hash ?cmp () in
|
|
||||||
let j = {
|
|
||||||
join_key1;
|
|
||||||
join_key2;
|
|
||||||
join_merge=merge;
|
|
||||||
join_build;
|
|
||||||
} in
|
|
||||||
Binary (Join j, q1, q2)
|
|
||||||
|
|
||||||
let group_join ?cmp ?eq ?hash gjoin_proj q1 q2 =
|
|
||||||
let gjoin_build = PMap._make_build ?eq ?hash ?cmp () in
|
|
||||||
let j = {
|
|
||||||
gjoin_proj;
|
|
||||||
gjoin_build;
|
|
||||||
} in
|
|
||||||
Binary (GroupJoin j, q1, q2)
|
|
||||||
|
|
||||||
let product q1 q2 = Binary (Product, q1, q2)
|
|
||||||
|
|
||||||
let append q1 q2 = Binary (Append, q1, q2)
|
|
||||||
|
|
||||||
let inter ?cmp ?eq ?hash () q1 q2 =
|
|
||||||
let build = PMap._make_build ?cmp ?eq ?hash () in
|
|
||||||
Binary (SetOp (Inter, build), q1, q2)
|
|
||||||
|
|
||||||
let union ?cmp ?eq ?hash () q1 q2 =
|
|
||||||
let build = PMap._make_build ?cmp ?eq ?hash () in
|
|
||||||
Binary (SetOp (Union, build), q1, q2)
|
|
||||||
|
|
||||||
let diff ?cmp ?eq ?hash () q1 q2 =
|
|
||||||
let build = PMap._make_build ?cmp ?eq ?hash () in
|
|
||||||
Binary (SetOp (Diff, build), q1, q2)
|
|
||||||
|
|
||||||
let fst q = map fst q
|
|
||||||
let snd q = map snd q
|
|
||||||
|
|
||||||
let map1 f q = map (fun (x,y) -> f x, y) q
|
|
||||||
let map2 f q = map (fun (x,y) -> x, f y) q
|
|
||||||
|
|
||||||
let flatten_opt q = filter_map _id q
|
|
||||||
|
|
||||||
let opt_unwrap q =
|
|
||||||
Unary
|
|
||||||
(Map
|
|
||||||
(function
|
|
||||||
| Some x -> x
|
|
||||||
| None -> _exit_with_error "opt_unwrap"),
|
|
||||||
q
|
|
||||||
)
|
|
||||||
|
|
||||||
(** {6 Applicative} *)
|
|
||||||
|
|
||||||
let pure x = Return x
|
|
||||||
|
|
||||||
let app f x = Binary (App, f, x)
|
|
||||||
|
|
||||||
let (<*>) = app
|
|
||||||
|
|
||||||
(** {6 Monadic stuff} *)
|
|
||||||
|
|
||||||
let return x = Return x
|
|
||||||
|
|
||||||
let bind f q = Bind (f,q)
|
|
||||||
|
|
||||||
let (>>=) x f = Bind (f, x)
|
|
||||||
|
|
||||||
(** {6 Misc} *)
|
|
||||||
|
|
||||||
let lazy_ q = Unary (Lazy, q)
|
|
||||||
|
|
||||||
let reflect q = Reflect q
|
|
||||||
|
|
||||||
(** {6 Infix} *)
|
|
||||||
|
|
||||||
module Infix = struct
|
|
||||||
let (>>=) = (>>=)
|
|
||||||
let (>|=) = (>|=)
|
|
||||||
let (<*>) = (<*>)
|
|
||||||
let (--) = (--)
|
|
||||||
end
|
|
||||||
|
|
||||||
(** {6 Adapters} *)
|
|
||||||
|
|
||||||
let to_seq q = reflect q
|
|
||||||
|
|
||||||
let to_hashtbl q =
|
|
||||||
Unary (Map (fun c -> Sequence.to_hashtbl c), Reflect q)
|
|
||||||
|
|
||||||
let to_queue q =
|
|
||||||
Unary (Map (fun c -> let q = Queue.create() in Sequence.to_queue q c; q), Reflect q)
|
|
||||||
|
|
||||||
let to_stack q =
|
|
||||||
Unary (Map (fun c -> let s = Stack.create () in Sequence.to_stack s c; s), Reflect q)
|
|
||||||
|
|
||||||
module List = struct
|
|
||||||
let of_list l = OfSeq (Sequence.of_list l)
|
|
||||||
let to_list q = map Sequence.to_list (Reflect q)
|
|
||||||
let run q = run1 (to_list q)
|
|
||||||
end
|
|
||||||
|
|
||||||
module Array = struct
|
|
||||||
let of_array a = OfSeq (Sequence.of_array a)
|
|
||||||
let to_array q =
|
|
||||||
map (fun s -> Array.of_list (Sequence.to_list s)) (Reflect q)
|
|
||||||
let run q = run1 (to_array q)
|
|
||||||
end
|
|
||||||
|
|
||||||
module AdaptSet(S : Set.S) = struct
|
|
||||||
let of_set set = OfSeq (fun k -> S.iter k set)
|
|
||||||
|
|
||||||
let to_set q =
|
|
||||||
let f c = Sequence.fold (fun set x -> S.add x set) S.empty c in
|
|
||||||
map f (reflect q)
|
|
||||||
|
|
||||||
let run q = run1 (to_set q)
|
|
||||||
end
|
|
||||||
|
|
||||||
module AdaptMap(M : Map.S) = struct
|
|
||||||
let _to_seq m k = M.iter (fun x y -> k (x,y)) m
|
|
||||||
|
|
||||||
let of_map map = OfSeq (_to_seq map)
|
|
||||||
|
|
||||||
let to_pmap m = {
|
|
||||||
PMap.get = (fun x -> try Some (M.find x m) with Not_found -> None);
|
|
||||||
PMap.size = (fun () -> M.cardinal m);
|
|
||||||
PMap.is_empty = (fun () -> M.is_empty m);
|
|
||||||
PMap.fold = (fun f acc -> M.fold (fun x y acc -> f acc x y) m acc);
|
|
||||||
PMap.to_seq = _to_seq m;
|
|
||||||
}
|
|
||||||
|
|
||||||
let to_map q =
|
|
||||||
let f c =
|
|
||||||
Sequence.fold (fun m (x,y) -> M.add x y m) M.empty c
|
|
||||||
in
|
|
||||||
map f (reflect q)
|
|
||||||
|
|
||||||
let run q = run1 (to_map q)
|
|
||||||
end
|
|
||||||
|
|
||||||
module IO = struct
|
|
||||||
let _slurp with_input =
|
|
||||||
let l = lazy (with_input (fun ic -> CCIO.read_all ic)) in
|
|
||||||
lazy_ (return l)
|
|
||||||
|
|
||||||
let slurp ic = _slurp (fun f -> f ic)
|
|
||||||
|
|
||||||
let _with_file_in filename f =
|
|
||||||
try
|
|
||||||
let ic = open_in filename in
|
|
||||||
try
|
|
||||||
let x = f ic in
|
|
||||||
close_in ic;
|
|
||||||
x
|
|
||||||
with e ->
|
|
||||||
close_in ic;
|
|
||||||
_exit_with_error (Printexc.to_string e)
|
|
||||||
with e ->
|
|
||||||
_exit_with_error (Printexc.to_string e)
|
|
||||||
|
|
||||||
let _with_file_out filename f =
|
|
||||||
try
|
|
||||||
let oc = open_out filename in
|
|
||||||
try
|
|
||||||
let x = f oc in
|
|
||||||
close_out oc;
|
|
||||||
x
|
|
||||||
with e ->
|
|
||||||
close_out oc;
|
|
||||||
_exit_with_error (Printexc.to_string e)
|
|
||||||
with e ->
|
|
||||||
_exit_with_error (Printexc.to_string e)
|
|
||||||
|
|
||||||
let slurp_file filename = _slurp (_with_file_in filename)
|
|
||||||
|
|
||||||
(* find [c] in [s], starting at offset [i] *)
|
|
||||||
let rec _find s c i =
|
|
||||||
if i >= String.length s then None
|
|
||||||
else if s.[i] = c then Some i
|
|
||||||
else _find s c (i+1)
|
|
||||||
|
|
||||||
let rec _lines s i k = match _find s '\n' i with
|
|
||||||
| None ->
|
|
||||||
if i<String.length s then k (String.sub s i (String.length s-i))
|
|
||||||
| Some j ->
|
|
||||||
let s' = String.sub s i (j-i) in
|
|
||||||
k s';
|
|
||||||
_lines s (j+1) k
|
|
||||||
|
|
||||||
let lines q =
|
|
||||||
(* sequence of lines *)
|
|
||||||
let f s = _lines s 0 in
|
|
||||||
flat_map f q
|
|
||||||
|
|
||||||
let lines' q =
|
|
||||||
let f s = lazy (Sequence.to_list (_lines s 0)) in
|
|
||||||
lazy_ (map f q)
|
|
||||||
|
|
||||||
let _join ~sep ?(stop="") seq =
|
|
||||||
let buf = Buffer.create 128 in
|
|
||||||
Sequence.iteri
|
|
||||||
(fun i x ->
|
|
||||||
if i>0 then Buffer.add_string buf sep;
|
|
||||||
Buffer.add_string buf x)
|
|
||||||
seq;
|
|
||||||
Buffer.add_string buf stop;
|
|
||||||
Buffer.contents buf
|
|
||||||
|
|
||||||
let unlines q =
|
|
||||||
let f l = lazy (_join ~sep:"\n" ~stop:"\n" l) in
|
|
||||||
lazy_ (map f (reflect q))
|
|
||||||
|
|
||||||
let join sep q =
|
|
||||||
let f l = lazy (_join ~sep l) in
|
|
||||||
lazy_ (map f (reflect q))
|
|
||||||
|
|
||||||
let out oc q =
|
|
||||||
output_string oc (run1 q)
|
|
||||||
|
|
||||||
let out_lines oc q =
|
|
||||||
let x = run q in
|
|
||||||
Sequence.iter (fun l -> output_string oc l; output_char oc '\n') x
|
|
||||||
|
|
||||||
let to_file_exn filename q =
|
|
||||||
_with_file_out filename (fun oc -> out oc q)
|
|
||||||
|
|
||||||
let to_file filename q =
|
|
||||||
try `Ok (_with_file_out filename (fun oc -> out oc q))
|
|
||||||
with Failure s -> `Error s
|
|
||||||
|
|
||||||
let to_file_lines_exn filename q =
|
|
||||||
_with_file_out filename (fun oc -> out_lines oc q)
|
|
||||||
|
|
||||||
let to_file_lines filename q =
|
|
||||||
try `Ok (_with_file_out filename (fun oc -> out_lines oc q))
|
|
||||||
with Failure s -> `Error s
|
|
||||||
end
|
|
||||||
|
|
@ -1,424 +0,0 @@
|
||||||
(*
|
|
||||||
copyright (c) 2013-2014, simon cruanes
|
|
||||||
all rights reserved.
|
|
||||||
|
|
||||||
redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer. redistributions in binary
|
|
||||||
form must reproduce the above copyright notice, this list of conditions and the
|
|
||||||
following disclaimer in the documentation and/or other materials provided with
|
|
||||||
the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*)
|
|
||||||
|
|
||||||
(** {1 LINQ-like operations on collections}
|
|
||||||
|
|
||||||
The purpose is to provide powerful combinators to express iteration,
|
|
||||||
transformation and combination of collections of items. This module depends
|
|
||||||
on several other modules, including {!CCList} and {!CCSequence}.
|
|
||||||
|
|
||||||
Functions and operations are assumed to be referentially transparent, i.e.
|
|
||||||
they should not rely on external side effects, they should not rely on
|
|
||||||
the order of execution.
|
|
||||||
|
|
||||||
@deprecated use {{: https://github.com/c-cube/olinq} OLinq}
|
|
||||||
|
|
||||||
{[
|
|
||||||
|
|
||||||
CCLinq.(
|
|
||||||
of_list [1;2;3]
|
|
||||||
|> flat_map (fun x -> Sequence.(x -- (x+10)))
|
|
||||||
|> sort ()
|
|
||||||
|> count ()
|
|
||||||
|> flat_map PMap.to_seq
|
|
||||||
|> List.run
|
|
||||||
);;
|
|
||||||
- : (int * int) list = [(13, 1); (12, 2); (11, 3); (10, 3); (9, 3);
|
|
||||||
(8, 3); (7, 3); (6, 3); (5, 3); (4, 3); (3, 3); (2, 2); (1, 1)]
|
|
||||||
|
|
||||||
|
|
||||||
CCLinq.(
|
|
||||||
IO.slurp_file "/tmp/foo"
|
|
||||||
|> IO.lines
|
|
||||||
|> sort ()
|
|
||||||
|> IO.to_file_lines "/tmp/bar"
|
|
||||||
);;
|
|
||||||
- : `Ok ()
|
|
||||||
]}
|
|
||||||
|
|
||||||
{b DEPRECATED, use "OLinq" (standalone library) instead}
|
|
||||||
|
|
||||||
{b status: deprecated}
|
|
||||||
|
|
||||||
*)
|
|
||||||
|
|
||||||
type 'a sequence = ('a -> unit) -> unit
|
|
||||||
type 'a equal = 'a -> 'a -> bool
|
|
||||||
type 'a ord = 'a -> 'a -> int
|
|
||||||
type 'a hash = 'a -> int
|
|
||||||
type 'a with_err = [`Ok of 'a | `Error of string ]
|
|
||||||
|
|
||||||
(** {2 Polymorphic Maps} *)
|
|
||||||
module PMap : sig
|
|
||||||
type ('a, 'b) t
|
|
||||||
|
|
||||||
val get : ('a,'b) t -> 'a -> 'b option
|
|
||||||
|
|
||||||
val size : (_,_) t -> int
|
|
||||||
|
|
||||||
val to_seq : ('a, 'b) t -> ('a * 'b) sequence
|
|
||||||
|
|
||||||
val map : ('b -> 'c) -> ('a, 'b) t -> ('a, 'c) t
|
|
||||||
(** Transform values *)
|
|
||||||
|
|
||||||
val to_list : ('a,'b) t -> ('a*'b) list
|
|
||||||
|
|
||||||
val reverse : ?cmp:'b ord -> ?eq:'b equal -> ?hash:'b hash -> unit ->
|
|
||||||
('a,'b) t -> ('b,'a list) t
|
|
||||||
(** Reverse relation of the map, as a multimap *)
|
|
||||||
|
|
||||||
val reverse_multimap : ?cmp:'b ord -> ?eq:'b equal -> ?hash:'b hash -> unit ->
|
|
||||||
('a,'b list) t -> ('b,'a list) t
|
|
||||||
(** Reverse relation of the multimap *)
|
|
||||||
|
|
||||||
val fold : ('acc -> 'a -> 'b -> 'acc) -> 'acc -> ('a,'b) t -> 'acc
|
|
||||||
(** Fold on the items of the map *)
|
|
||||||
|
|
||||||
val fold_multimap : ('acc -> 'a -> 'b -> 'acc) -> 'acc ->
|
|
||||||
('a,'b list) t -> 'acc
|
|
||||||
(** Fold on the items of the multimap *)
|
|
||||||
|
|
||||||
val get_seq : 'a -> ('a, 'b) t -> 'b sequence
|
|
||||||
(** Select a key from a map and wrap into sequence *)
|
|
||||||
|
|
||||||
val iter : ('a,'b) t -> ('a*'b) sequence
|
|
||||||
(** View a multimap as a proper collection *)
|
|
||||||
|
|
||||||
val flatten : ('a,'b sequence) t -> ('a*'b) sequence
|
|
||||||
(** View a multimap as a collection of individual key/value pairs *)
|
|
||||||
|
|
||||||
val flatten_l : ('a,'b list) t -> ('a*'b) sequence
|
|
||||||
(** View a multimap as a list of individual key/value pairs *)
|
|
||||||
end
|
|
||||||
|
|
||||||
(** {2 Query operators} *)
|
|
||||||
|
|
||||||
type 'a t
|
|
||||||
(** Type of a query that returns zero, one or more values of type 'a *)
|
|
||||||
|
|
||||||
(** {6 Initial values} *)
|
|
||||||
|
|
||||||
val empty : 'a t
|
|
||||||
(** Empty collection *)
|
|
||||||
|
|
||||||
val start : 'a -> 'a t
|
|
||||||
(** Start with a single value
|
|
||||||
@deprecated since 0.13, use {!return} instead *)
|
|
||||||
|
|
||||||
val return : 'a -> 'a t
|
|
||||||
(** Return one value *)
|
|
||||||
|
|
||||||
val of_list : 'a list -> 'a t
|
|
||||||
(** Query that just returns the elements of the list *)
|
|
||||||
|
|
||||||
val of_array : 'a array -> 'a t
|
|
||||||
val of_array_i : 'a array -> (int * 'a) t
|
|
||||||
|
|
||||||
val range : int -> int -> int t
|
|
||||||
(** [range i j] goes from [i] up to [j] included *)
|
|
||||||
|
|
||||||
val (--) : int -> int -> int t
|
|
||||||
(** Synonym to {!range} *)
|
|
||||||
|
|
||||||
val of_hashtbl : ('a,'b) Hashtbl.t -> ('a * 'b) t
|
|
||||||
|
|
||||||
val of_seq : 'a sequence -> 'a t
|
|
||||||
(** Query that returns the elements of the given sequence. *)
|
|
||||||
|
|
||||||
val of_queue : 'a Queue.t -> 'a t
|
|
||||||
|
|
||||||
val of_stack : 'a Stack.t -> 'a t
|
|
||||||
|
|
||||||
val of_string : string -> char t
|
|
||||||
(** Traverse the characters of the string *)
|
|
||||||
|
|
||||||
(** {6 Execution} *)
|
|
||||||
|
|
||||||
val run : ?limit:int -> 'a t -> 'a sequence
|
|
||||||
(** Execute the query, possibly returning an error if things go wrong
|
|
||||||
@param limit max number of values to return *)
|
|
||||||
|
|
||||||
val run1 : 'a t -> 'a
|
|
||||||
(** Run the query and return the first value
|
|
||||||
@raise Not_found if the query succeeds with 0 element *)
|
|
||||||
|
|
||||||
val run_no_optim : ?limit:int -> 'a t -> 'a sequence
|
|
||||||
(** Run without any optimization *)
|
|
||||||
|
|
||||||
(** {6 Basics} *)
|
|
||||||
|
|
||||||
val map : ('a -> 'b) -> 'a t -> 'b t
|
|
||||||
(** Map each value *)
|
|
||||||
|
|
||||||
val (>|=) : 'a t -> ('a -> 'b) -> 'b t
|
|
||||||
(** Infix synonym of {!map} *)
|
|
||||||
|
|
||||||
val filter : ('a -> bool) -> 'a t -> 'a t
|
|
||||||
(** Filter out values that do not satisfy predicate *)
|
|
||||||
|
|
||||||
val size : _ t -> int t
|
|
||||||
(** [size t] returns one value, the number of items returned by [t] *)
|
|
||||||
|
|
||||||
val choose : 'a t -> 'a t
|
|
||||||
(** Choose one element (if any, otherwise empty) in the collection.
|
|
||||||
This is like a "cut" in prolog. *)
|
|
||||||
|
|
||||||
val filter_map : ('a -> 'b option) -> 'a t -> 'b t
|
|
||||||
(** Filter and map elements at once *)
|
|
||||||
|
|
||||||
val flat_map : ('a -> 'b sequence) -> 'a t -> 'b t
|
|
||||||
(** Same as {!flat_map} but using sequences *)
|
|
||||||
|
|
||||||
val flat_map_l : ('a -> 'b list) -> 'a t -> 'b t
|
|
||||||
(** map each element to a collection and flatten the result *)
|
|
||||||
|
|
||||||
val flatten : 'a list t -> 'a t
|
|
||||||
|
|
||||||
val flatten_seq : 'a sequence t -> 'a t
|
|
||||||
|
|
||||||
val take : int -> 'a t -> 'a t
|
|
||||||
(** Take at most [n] elements *)
|
|
||||||
|
|
||||||
val take_while : ('a -> bool) -> 'a t -> 'a t
|
|
||||||
(** Take elements while they satisfy a predicate *)
|
|
||||||
|
|
||||||
val sort : ?cmp:'a ord -> unit -> 'a t -> 'a t
|
|
||||||
(** Sort items by the given comparison function *)
|
|
||||||
|
|
||||||
val distinct : ?cmp:'a ord -> unit -> 'a t -> 'a t
|
|
||||||
(** Remove duplicate elements from the input collection.
|
|
||||||
All elements in the result are distinct. *)
|
|
||||||
|
|
||||||
(** {6 Aggregation} *)
|
|
||||||
|
|
||||||
val group_by : ?cmp:'b ord -> ?eq:'b equal -> ?hash:'b hash ->
|
|
||||||
('a -> 'b) -> 'a t -> ('b,'a list) PMap.t t
|
|
||||||
(** [group_by f] takes a collection [c] as input, and returns
|
|
||||||
a multimap [m] such that for each [x] in [c],
|
|
||||||
[x] occurs in [m] under the key [f x]. In other words, [f] is used
|
|
||||||
to obtain a key from [x], and [x] is added to the multimap using this key. *)
|
|
||||||
|
|
||||||
val group_by' : ?cmp:'b ord -> ?eq:'b equal -> ?hash:'b hash ->
|
|
||||||
('a -> 'b) -> 'a t -> ('b * 'a list) t
|
|
||||||
|
|
||||||
val count : ?cmp:'a ord -> ?eq:'a equal -> ?hash:'a hash ->
|
|
||||||
unit -> 'a t -> ('a, int) PMap.t t
|
|
||||||
(** [count c] returns a map from elements of [c] to the number
|
|
||||||
of time those elements occur. *)
|
|
||||||
|
|
||||||
val count' : ?cmp:'a ord -> unit -> 'a t -> ('a * int) t
|
|
||||||
|
|
||||||
val fold : ('b -> 'a -> 'b) -> 'b -> 'a t -> 'b t
|
|
||||||
(** Fold over the collection *)
|
|
||||||
|
|
||||||
val reduce : ('a -> 'b) -> ('a -> 'b -> 'b) -> ('b -> 'c) ->
|
|
||||||
'a t -> 'c t
|
|
||||||
(** [reduce start mix stop q] uses [start] on the first element of [q],
|
|
||||||
and combine the result with following elements using [mix]. The final
|
|
||||||
value is transformed using [stop]. *)
|
|
||||||
|
|
||||||
val is_empty : 'a t -> bool t
|
|
||||||
|
|
||||||
val sum : int t -> int t
|
|
||||||
|
|
||||||
val contains : ?eq:'a equal -> 'a -> 'a t -> bool t
|
|
||||||
|
|
||||||
val average : int t -> int t
|
|
||||||
val max : int t -> int t
|
|
||||||
val min : int t -> int t
|
|
||||||
|
|
||||||
val for_all : ('a -> bool) -> 'a t -> bool t
|
|
||||||
val exists : ('a -> bool) -> 'a t -> bool t
|
|
||||||
val find : ('a -> bool) -> 'a t -> 'a option t
|
|
||||||
val find_map : ('a -> 'b option) -> 'a t -> 'b option t
|
|
||||||
|
|
||||||
(** {6 Binary Operators} *)
|
|
||||||
|
|
||||||
val join : ?cmp:'key ord -> ?eq:'key equal -> ?hash:'key hash ->
|
|
||||||
('a -> 'key) -> ('b -> 'key) ->
|
|
||||||
merge:('key -> 'a -> 'b -> 'c option) ->
|
|
||||||
'a t -> 'b t -> 'c t
|
|
||||||
(** [join key1 key2 ~merge] is a binary operation
|
|
||||||
that takes two collections [a] and [b], projects their
|
|
||||||
elements resp. with [key1] and [key2], and combine
|
|
||||||
values [(x,y)] from [(a,b)] with the same [key]
|
|
||||||
using [merge]. If [merge] returns [None], the combination
|
|
||||||
of values is discarded. *)
|
|
||||||
|
|
||||||
val group_join : ?cmp:'a ord -> ?eq:'a equal -> ?hash:'a hash ->
|
|
||||||
('b -> 'a) -> 'a t -> 'b t ->
|
|
||||||
('a, 'b list) PMap.t t
|
|
||||||
(** [group_join key2] associates to every element [x] of
|
|
||||||
the first collection, all the elements [y] of the second
|
|
||||||
collection such that [eq x (key y)] *)
|
|
||||||
|
|
||||||
val product : 'a t -> 'b t -> ('a * 'b) t
|
|
||||||
(** Cartesian product *)
|
|
||||||
|
|
||||||
val append : 'a t -> 'a t -> 'a t
|
|
||||||
(** Append two collections together *)
|
|
||||||
|
|
||||||
val inter : ?cmp:'a ord -> ?eq:'a equal -> ?hash:'a hash -> unit ->
|
|
||||||
'a t -> 'a t -> 'a t
|
|
||||||
(** Intersection of two collections. Each element will occur at most once
|
|
||||||
in the result *)
|
|
||||||
|
|
||||||
val union : ?cmp:'a ord -> ?eq:'a equal -> ?hash:'a hash -> unit ->
|
|
||||||
'a t -> 'a t -> 'a t
|
|
||||||
(** Union of two collections. Each element will occur at most once
|
|
||||||
in the result *)
|
|
||||||
|
|
||||||
val diff : ?cmp:'a ord -> ?eq:'a equal -> ?hash:'a hash -> unit ->
|
|
||||||
'a t -> 'a t -> 'a t
|
|
||||||
(** Set difference *)
|
|
||||||
|
|
||||||
(** {6 Tuple and Options} *)
|
|
||||||
|
|
||||||
(** Specialized projection operators *)
|
|
||||||
|
|
||||||
val fst : ('a * 'b) t -> 'a t
|
|
||||||
|
|
||||||
val snd : ('a * 'b) t -> 'b t
|
|
||||||
|
|
||||||
val map1 : ('a -> 'b) -> ('a * 'c) t -> ('b * 'c) t
|
|
||||||
|
|
||||||
val map2 : ('a -> 'b) -> ('c * 'a) t -> ('c * 'b) t
|
|
||||||
|
|
||||||
val flatten_opt : 'a option t -> 'a t
|
|
||||||
(** Flatten the collection by removing options *)
|
|
||||||
|
|
||||||
(** {6 Applicative} *)
|
|
||||||
|
|
||||||
val pure : 'a -> 'a t
|
|
||||||
(** Synonym to {!return} *)
|
|
||||||
|
|
||||||
val app : ('a -> 'b) t -> 'a t -> 'b t
|
|
||||||
(** Apply each function to each value *)
|
|
||||||
|
|
||||||
val (<*>) : ('a -> 'b) t -> 'a t -> 'b t
|
|
||||||
(** Infix synonym to {!app} *)
|
|
||||||
|
|
||||||
(** {6 Monad}
|
|
||||||
|
|
||||||
Careful, those operators do not allow any optimization before running the
|
|
||||||
query, they might therefore be pretty slow. *)
|
|
||||||
|
|
||||||
val bind : ('a -> 'b t) -> 'a t -> 'b t
|
|
||||||
(** Use the result of a query to build another query and immediately run it. *)
|
|
||||||
|
|
||||||
val (>>=) : 'a t -> ('a -> 'b t) -> 'b t
|
|
||||||
(** Infix version of {!bind} *)
|
|
||||||
|
|
||||||
(** {6 Misc} *)
|
|
||||||
|
|
||||||
val lazy_ : 'a lazy_t t -> 'a t
|
|
||||||
|
|
||||||
val opt_unwrap : 'a option t -> 'a t
|
|
||||||
|
|
||||||
val reflect : 'a t -> 'a sequence t
|
|
||||||
(** [reflect q] evaluates all values in [q] and returns a sequence
|
|
||||||
of all those values. Also blocks optimizations *)
|
|
||||||
|
|
||||||
(** {6 Infix} *)
|
|
||||||
|
|
||||||
module Infix : sig
|
|
||||||
val (>>=) : 'a t -> ('a -> 'b t) -> 'b t
|
|
||||||
val (>|=) : 'a t -> ('a -> 'b) -> 'b t
|
|
||||||
val (<*>) : ('a -> 'b) t -> 'a t -> 'b t
|
|
||||||
val (--) : int -> int -> int t
|
|
||||||
end
|
|
||||||
|
|
||||||
(** {6 Adapters} *)
|
|
||||||
|
|
||||||
val to_seq : 'a t -> 'a sequence t
|
|
||||||
(** Build a (re-usable) sequence of elements, which can then be
|
|
||||||
converted into other structures *)
|
|
||||||
|
|
||||||
val to_hashtbl : ('a * 'b) t -> ('a, 'b) Hashtbl.t t
|
|
||||||
(** Build a hashtable from the collection *)
|
|
||||||
|
|
||||||
val to_queue : 'a t -> 'a Queue.t t
|
|
||||||
|
|
||||||
val to_stack : 'a t -> 'a Stack.t t
|
|
||||||
|
|
||||||
module List : sig
|
|
||||||
val of_list : 'a list -> 'a t
|
|
||||||
val to_list : 'a t -> 'a list t
|
|
||||||
val run : 'a t -> 'a list
|
|
||||||
end
|
|
||||||
|
|
||||||
module Array : sig
|
|
||||||
val of_array : 'a array -> 'a t
|
|
||||||
val to_array : 'a t -> 'a array t
|
|
||||||
val run : 'a t -> 'a array
|
|
||||||
end
|
|
||||||
|
|
||||||
module AdaptSet(S : Set.S) : sig
|
|
||||||
val of_set : S.t -> S.elt t
|
|
||||||
val to_set : S.elt t -> S.t t
|
|
||||||
val run : S.elt t -> S.t
|
|
||||||
end
|
|
||||||
|
|
||||||
module AdaptMap(M : Map.S) : sig
|
|
||||||
val of_map : 'a M.t -> (M.key * 'a) t
|
|
||||||
val to_pmap : 'a M.t -> (M.key, 'a) PMap.t
|
|
||||||
val to_map : (M.key * 'a) t -> 'a M.t t
|
|
||||||
val run : (M.key * 'a) t -> 'a M.t
|
|
||||||
end
|
|
||||||
|
|
||||||
module IO : sig
|
|
||||||
val slurp : in_channel -> string t
|
|
||||||
(** Slurp the whole channel in (blocking), returning the
|
|
||||||
corresponding string. The channel will be read at most once
|
|
||||||
during execution, and its content cached; however the channel
|
|
||||||
might never get read because evaluation is lazy. *)
|
|
||||||
|
|
||||||
val slurp_file : string -> string t
|
|
||||||
(** Read a whole file (given by name) and return its content as
|
|
||||||
a string *)
|
|
||||||
|
|
||||||
val lines : string t -> string t
|
|
||||||
(** Convert a string into a collection of lines *)
|
|
||||||
|
|
||||||
val lines' : string t -> string list t
|
|
||||||
(** Convert a string into a list of lines *)
|
|
||||||
|
|
||||||
val join : string -> string t -> string t
|
|
||||||
|
|
||||||
val unlines : string t -> string t
|
|
||||||
(** Join lines together *)
|
|
||||||
|
|
||||||
val out : out_channel -> string t -> unit
|
|
||||||
val out_lines : out_channel -> string t -> unit
|
|
||||||
(** Evaluate the query and print it line by line on the output *)
|
|
||||||
|
|
||||||
(** {8 Run methods} *)
|
|
||||||
|
|
||||||
val to_file : string -> string t -> unit with_err
|
|
||||||
val to_file_exn : string -> string t -> unit
|
|
||||||
|
|
||||||
val to_file_lines : string -> string t -> unit with_err
|
|
||||||
val to_file_lines_exn : string -> string t -> unit
|
|
||||||
end
|
|
||||||
|
|
@ -1,519 +0,0 @@
|
||||||
(*
|
|
||||||
copyright (c) 2013-2014, simon cruanes
|
|
||||||
all rights reserved.
|
|
||||||
|
|
||||||
redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer. redistributions in binary
|
|
||||||
form must reproduce the above copyright notice, this list of conditions and the
|
|
||||||
following disclaimer in the documentation and/or other materials provided with
|
|
||||||
the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*)
|
|
||||||
|
|
||||||
(** {1 IO Monad} *)
|
|
||||||
|
|
||||||
type _ t =
|
|
||||||
| Return : 'a -> 'a t
|
|
||||||
| Fail : string -> 'a t
|
|
||||||
| Map : ('a -> 'b) * 'a t -> 'b t
|
|
||||||
| Bind : ('a -> 'b t) * 'a t -> 'b t
|
|
||||||
| WithGuard: unit t * 'a t -> 'a t (* run guard in any case *)
|
|
||||||
| Star : ('a -> 'b) t * 'a t -> 'b t
|
|
||||||
| Repeat : int * 'a t -> 'a list t
|
|
||||||
| RepeatIgnore : int * 'a t -> unit t
|
|
||||||
| Wrap : (unit -> 'a) -> 'a t
|
|
||||||
| SequenceMap : ('a -> 'b t) * 'a list -> 'b list t
|
|
||||||
|
|
||||||
type 'a io = 'a t
|
|
||||||
type 'a with_finalizer = ('a t * unit t) t
|
|
||||||
type 'a or_error = [ `Ok of 'a | `Error of string ]
|
|
||||||
|
|
||||||
let (>>=) x f = Bind(f,x)
|
|
||||||
|
|
||||||
let bind ?finalize f a = match finalize with
|
|
||||||
| None -> Bind(f,a)
|
|
||||||
| Some b -> WithGuard (b, Bind (f,a))
|
|
||||||
|
|
||||||
let map f x = Map(f, x)
|
|
||||||
|
|
||||||
let (>|=) x f = Map(f, x)
|
|
||||||
|
|
||||||
let return x = Return x
|
|
||||||
let pure = return
|
|
||||||
|
|
||||||
let fail msg = Fail msg
|
|
||||||
|
|
||||||
let (<*>) f a = Star (f, a)
|
|
||||||
|
|
||||||
let lift = map
|
|
||||||
|
|
||||||
let lift2 f a b =
|
|
||||||
a >>= fun x -> map (f x) b
|
|
||||||
|
|
||||||
let lift3 f a b c =
|
|
||||||
a >>= fun x ->
|
|
||||||
b >>= fun y -> map (f x y) c
|
|
||||||
|
|
||||||
let sequence_map f l =
|
|
||||||
SequenceMap (f,l)
|
|
||||||
|
|
||||||
let sequence l =
|
|
||||||
let _id x = x in
|
|
||||||
SequenceMap(_id, l)
|
|
||||||
|
|
||||||
let repeat i a =
|
|
||||||
if i <= 0 then Return [] else Repeat (i,a)
|
|
||||||
|
|
||||||
let repeat' i a =
|
|
||||||
if i <= 0 then Return () else RepeatIgnore (i,a)
|
|
||||||
|
|
||||||
(** {2 Finalizers} *)
|
|
||||||
|
|
||||||
let (>>>=) a f =
|
|
||||||
a >>= function
|
|
||||||
| x, finalizer -> WithGuard (finalizer, x >>= f)
|
|
||||||
|
|
||||||
(** {2 Running} *)
|
|
||||||
|
|
||||||
exception IOFailure of string
|
|
||||||
|
|
||||||
let rec _run : type a. a t -> a = function
|
|
||||||
| Return x -> x
|
|
||||||
| Fail msg -> raise (IOFailure msg)
|
|
||||||
| Map (f, a) -> f (_run a)
|
|
||||||
| Bind (f, a) -> _run (f (_run a))
|
|
||||||
| WithGuard (g, a) ->
|
|
||||||
begin try
|
|
||||||
let res = _run a in
|
|
||||||
_run g;
|
|
||||||
res
|
|
||||||
with e ->
|
|
||||||
_run g;
|
|
||||||
raise e
|
|
||||||
end
|
|
||||||
| Star (f, a) -> _run f (_run a)
|
|
||||||
| Repeat (i,a) -> _repeat [] i a
|
|
||||||
| RepeatIgnore (i,a) -> _repeat_ignore i a
|
|
||||||
| Wrap f -> f()
|
|
||||||
| SequenceMap (f, l) -> _sequence_map f l []
|
|
||||||
and _repeat : type a. a list -> int -> a t -> a list
|
|
||||||
= fun acc i a -> match i with
|
|
||||||
| 0 -> List.rev acc
|
|
||||||
| _ ->
|
|
||||||
let x = _run a in
|
|
||||||
_repeat (x::acc) (i-1) a
|
|
||||||
and _repeat_ignore : type a. int -> a t -> unit
|
|
||||||
= fun i a -> match i with
|
|
||||||
| 0 -> ()
|
|
||||||
| _ ->
|
|
||||||
let _ = _run a in
|
|
||||||
_repeat_ignore (i-1) a
|
|
||||||
and _sequence_map : type a b. (a -> b t) -> a list -> b list -> b list
|
|
||||||
= fun f l acc -> match l with
|
|
||||||
| [] -> List.rev acc
|
|
||||||
| a::tail ->
|
|
||||||
let x = _run (f a) in
|
|
||||||
_sequence_map f tail (x::acc)
|
|
||||||
|
|
||||||
let _printers =
|
|
||||||
ref [
|
|
||||||
(* default printer *)
|
|
||||||
( function IOFailure msg
|
|
||||||
| Sys_error msg -> Some msg
|
|
||||||
| Exit -> Some "exit"
|
|
||||||
| _ -> None
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
exception PrinterResult of string
|
|
||||||
|
|
||||||
let _print_exn e =
|
|
||||||
try
|
|
||||||
List.iter
|
|
||||||
(fun p -> match p e with
|
|
||||||
| None -> ()
|
|
||||||
| Some msg -> raise (PrinterResult msg)
|
|
||||||
) !_printers;
|
|
||||||
Printexc.to_string e
|
|
||||||
with PrinterResult s -> s
|
|
||||||
|
|
||||||
let run x =
|
|
||||||
try `Ok (_run x)
|
|
||||||
with e -> `Error (_print_exn e)
|
|
||||||
|
|
||||||
exception IO_error of string
|
|
||||||
|
|
||||||
let run_exn x =
|
|
||||||
try _run x
|
|
||||||
with e -> raise (IO_error (_print_exn e))
|
|
||||||
|
|
||||||
let register_printer p = _printers := p :: !_printers
|
|
||||||
|
|
||||||
(** {2 Standard Wrappers} *)
|
|
||||||
|
|
||||||
let _open_in mode flags filename () =
|
|
||||||
open_in_gen flags mode filename
|
|
||||||
let _close_in ic () = close_in ic
|
|
||||||
|
|
||||||
let with_in ?(mode=0o644) ?(flags=[]) filename =
|
|
||||||
Wrap (_open_in mode flags filename)
|
|
||||||
>>= fun ic ->
|
|
||||||
Return (Return ic, Wrap (_close_in ic))
|
|
||||||
|
|
||||||
let _read ic s i len () = input ic s i len
|
|
||||||
let read ic s i len = Wrap (_read ic s i len)
|
|
||||||
|
|
||||||
let _read_line ic () =
|
|
||||||
try Some (Pervasives.input_line ic)
|
|
||||||
with End_of_file -> None
|
|
||||||
let read_line ic = Wrap(_read_line ic)
|
|
||||||
|
|
||||||
let rec _read_lines ic acc =
|
|
||||||
read_line ic
|
|
||||||
>>= function
|
|
||||||
| None -> return (List.rev acc)
|
|
||||||
| Some l -> _read_lines ic (l::acc)
|
|
||||||
|
|
||||||
let read_lines ic = _read_lines ic []
|
|
||||||
|
|
||||||
let read_all ic = Wrap(fun () -> CCIO.read_all ic)
|
|
||||||
|
|
||||||
let _open_out mode flags filename () =
|
|
||||||
open_out_gen flags mode filename
|
|
||||||
let _close_out oc () = close_out oc
|
|
||||||
|
|
||||||
let with_out ?(mode=0o644) ?(flags=[]) filename =
|
|
||||||
Wrap(_open_out mode (Open_wronly::flags) filename)
|
|
||||||
>>= fun oc ->
|
|
||||||
Return(Return oc, Wrap(_close_out oc))
|
|
||||||
|
|
||||||
let with_out_a ?mode ?(flags=[]) filename =
|
|
||||||
with_out ?mode ~flags:(Open_creat::Open_append::flags) filename
|
|
||||||
|
|
||||||
#if OCAML_MAJOR >= 4 && OCAML_MINOR >= 2
|
|
||||||
|
|
||||||
let output_str_ = Pervasives.output_substring
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
let output_str_ = Pervasives.output
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
let _write oc s i len () = output_str_ oc s i len
|
|
||||||
let write oc s i len = Wrap (_write oc s i len)
|
|
||||||
|
|
||||||
let _write_str oc s () = output_str_ oc s 0 (String.length s)
|
|
||||||
let write_str oc s = Wrap (_write_str oc s)
|
|
||||||
|
|
||||||
let _write_line oc l () =
|
|
||||||
output_string oc l;
|
|
||||||
output_char oc '\n'
|
|
||||||
let write_line oc l = Wrap (_write_line oc l)
|
|
||||||
|
|
||||||
let _write_buf oc buf () = Buffer.output_buffer oc buf
|
|
||||||
let write_buf oc buf = Wrap (_write_buf oc buf)
|
|
||||||
|
|
||||||
let flush oc = Wrap (fun () -> Pervasives.flush oc)
|
|
||||||
|
|
||||||
(** {2 Seq} *)
|
|
||||||
|
|
||||||
module Seq = struct
|
|
||||||
type 'a step_result =
|
|
||||||
| Yield of 'a
|
|
||||||
| Stop
|
|
||||||
|
|
||||||
type 'a gen = unit -> 'a step_result io
|
|
||||||
|
|
||||||
type 'a t = 'a gen
|
|
||||||
|
|
||||||
let _stop () = return Stop
|
|
||||||
let _yield x = return (Yield x)
|
|
||||||
|
|
||||||
let map_pure f gen () =
|
|
||||||
gen() >>= function
|
|
||||||
| Stop -> _stop ()
|
|
||||||
| Yield x -> _yield (f x)
|
|
||||||
|
|
||||||
let map f g () =
|
|
||||||
g() >>= function
|
|
||||||
| Stop -> _stop ()
|
|
||||||
| Yield x -> f x >>= _yield
|
|
||||||
|
|
||||||
let rec filter_map f g () =
|
|
||||||
g() >>= function
|
|
||||||
| Stop -> _stop()
|
|
||||||
| Yield x ->
|
|
||||||
match f x with
|
|
||||||
| None -> filter_map f g()
|
|
||||||
| Some y -> _yield y
|
|
||||||
|
|
||||||
let rec filter f g () =
|
|
||||||
g() >>= function
|
|
||||||
| Stop -> _stop()
|
|
||||||
| Yield x ->
|
|
||||||
if f x then _yield x else filter f g()
|
|
||||||
|
|
||||||
let rec flat_map f g () =
|
|
||||||
g() >>= function
|
|
||||||
| Stop -> _stop ()
|
|
||||||
| Yield x ->
|
|
||||||
f x >>= fun g' -> _flat_map_aux f g g' ()
|
|
||||||
and _flat_map_aux f g g' () =
|
|
||||||
g'() >>= function
|
|
||||||
| Stop -> flat_map f g ()
|
|
||||||
| Yield x -> _yield x
|
|
||||||
|
|
||||||
let general_iter f acc g =
|
|
||||||
let acc = ref acc in
|
|
||||||
let rec _next () =
|
|
||||||
g() >>= function
|
|
||||||
| Stop -> _stop()
|
|
||||||
| Yield x ->
|
|
||||||
f !acc x >>= function
|
|
||||||
| `Stop -> _stop()
|
|
||||||
| `Continue (acc', ret) ->
|
|
||||||
acc := acc';
|
|
||||||
match ret with
|
|
||||||
| None -> _next()
|
|
||||||
| Some y -> _yield y
|
|
||||||
in
|
|
||||||
_next
|
|
||||||
|
|
||||||
let take n seq =
|
|
||||||
general_iter
|
|
||||||
(fun n x -> if n<=0
|
|
||||||
then return `Stop
|
|
||||||
else return (`Continue (n-1, Some x))
|
|
||||||
) n seq
|
|
||||||
|
|
||||||
let drop n seq =
|
|
||||||
general_iter
|
|
||||||
(fun n x -> if n<=0
|
|
||||||
then return (`Continue (n, Some x))
|
|
||||||
else return (`Continue (n-1, None))
|
|
||||||
) n seq
|
|
||||||
|
|
||||||
let take_while p seq =
|
|
||||||
general_iter
|
|
||||||
(fun () x ->
|
|
||||||
p x >|= function
|
|
||||||
| true -> `Continue ((), Some x)
|
|
||||||
| false -> `Stop
|
|
||||||
) () seq
|
|
||||||
|
|
||||||
let drop_while p seq =
|
|
||||||
general_iter
|
|
||||||
(fun dropping x ->
|
|
||||||
if dropping
|
|
||||||
then p x >|= function
|
|
||||||
| true -> `Continue (true, None)
|
|
||||||
| false -> `Continue (false, Some x)
|
|
||||||
else return (`Continue (false, Some x))
|
|
||||||
) true seq
|
|
||||||
|
|
||||||
(* apply all actions from [l] to [x] *)
|
|
||||||
let rec _apply_all_to x l = match l with
|
|
||||||
| [] -> return ()
|
|
||||||
| f::tail -> f x >>= fun () -> _apply_all_to x tail
|
|
||||||
|
|
||||||
let _tee funs g () =
|
|
||||||
g() >>= function
|
|
||||||
| Stop -> _stop()
|
|
||||||
| Yield x ->
|
|
||||||
_apply_all_to x funs >>= fun () ->
|
|
||||||
_yield x
|
|
||||||
|
|
||||||
let tee funs g = match funs with
|
|
||||||
| [] -> g
|
|
||||||
| _::_ -> _tee funs g
|
|
||||||
|
|
||||||
(** {6 Consume} *)
|
|
||||||
|
|
||||||
let rec fold_pure f acc g =
|
|
||||||
g() >>= function
|
|
||||||
| Stop -> return acc
|
|
||||||
| Yield x -> fold_pure f (f acc x) g
|
|
||||||
|
|
||||||
let length g = fold_pure (fun acc _ -> acc+1) 0 g
|
|
||||||
|
|
||||||
let rec fold f acc g =
|
|
||||||
g() >>= function
|
|
||||||
| Stop -> return acc
|
|
||||||
| Yield x ->
|
|
||||||
f acc x >>= fun acc' -> fold f acc' g
|
|
||||||
|
|
||||||
let rec iter f g =
|
|
||||||
g() >>= function
|
|
||||||
| Stop -> return ()
|
|
||||||
| Yield x -> f x >>= fun _ -> iter f g
|
|
||||||
|
|
||||||
let of_fun g = g
|
|
||||||
|
|
||||||
let empty () = _stop()
|
|
||||||
|
|
||||||
let singleton x =
|
|
||||||
let first = ref true in
|
|
||||||
fun () ->
|
|
||||||
if !first then (first := false; _yield x) else _stop()
|
|
||||||
|
|
||||||
let cons x g =
|
|
||||||
let first = ref true in
|
|
||||||
fun () ->
|
|
||||||
if !first then (first := false; _yield x) else g()
|
|
||||||
|
|
||||||
let of_list l =
|
|
||||||
let l = ref l in
|
|
||||||
fun () -> match !l with
|
|
||||||
| [] -> _stop()
|
|
||||||
| x::tail -> l:= tail; _yield x
|
|
||||||
|
|
||||||
let of_array a =
|
|
||||||
let i = ref 0 in
|
|
||||||
fun () ->
|
|
||||||
if !i = Array.length a
|
|
||||||
then _stop()
|
|
||||||
else (
|
|
||||||
let x = a.(!i) in
|
|
||||||
incr i;
|
|
||||||
_yield x
|
|
||||||
)
|
|
||||||
|
|
||||||
(* TODO: wrapper around with_in? using bind ~finalize:... ? *)
|
|
||||||
|
|
||||||
let chunks ~size ic =
|
|
||||||
let buf = Buffer.create size in
|
|
||||||
let eof = ref false in
|
|
||||||
let next() =
|
|
||||||
if !eof then _stop()
|
|
||||||
else try
|
|
||||||
Buffer.add_channel buf ic size;
|
|
||||||
let s = Buffer.contents buf in
|
|
||||||
Buffer.clear buf;
|
|
||||||
_yield s
|
|
||||||
with End_of_file ->
|
|
||||||
let s = Buffer.contents buf in
|
|
||||||
eof := true;
|
|
||||||
if s="" then _stop() else _yield s
|
|
||||||
in
|
|
||||||
next
|
|
||||||
|
|
||||||
let lines ic () =
|
|
||||||
try _yield (input_line ic)
|
|
||||||
with End_of_file -> _stop()
|
|
||||||
|
|
||||||
let words _g =
|
|
||||||
failwith "words: not implemented yet"
|
|
||||||
(* TODO: state machine that goes:
|
|
||||||
- 0: read input chunk
|
|
||||||
- switch to "search for ' '", and yield word
|
|
||||||
- goto 0 if no ' ' found
|
|
||||||
- yield leftover when g returns Stop
|
|
||||||
let buf = Buffer.create 32 in
|
|
||||||
let next() =
|
|
||||||
g() >>= function
|
|
||||||
| Stop -> _stop
|
|
||||||
| Yield s ->
|
|
||||||
Buffer.add_string buf s;
|
|
||||||
search_
|
|
||||||
in
|
|
||||||
next
|
|
||||||
*)
|
|
||||||
|
|
||||||
let output ?sep oc seq =
|
|
||||||
let first = ref true in
|
|
||||||
iter
|
|
||||||
(fun s ->
|
|
||||||
(* print separator *)
|
|
||||||
( if !first
|
|
||||||
then (first:=false; return ())
|
|
||||||
else match sep with
|
|
||||||
| None -> return ()
|
|
||||||
| Some sep -> write_str oc sep
|
|
||||||
) >>= fun () ->
|
|
||||||
write_str oc s
|
|
||||||
) seq
|
|
||||||
>>= fun () -> flush oc
|
|
||||||
end
|
|
||||||
|
|
||||||
(** {6 File and file names} *)
|
|
||||||
|
|
||||||
module File = struct
|
|
||||||
type t = string
|
|
||||||
|
|
||||||
let to_string f = f
|
|
||||||
|
|
||||||
let make f =
|
|
||||||
if Filename.is_relative f
|
|
||||||
then Filename.concat (Sys.getcwd()) f
|
|
||||||
else f
|
|
||||||
|
|
||||||
let exists f = Wrap (fun () -> Sys.file_exists f)
|
|
||||||
|
|
||||||
let is_directory f = Wrap (fun () -> Sys.is_directory f)
|
|
||||||
|
|
||||||
let remove f = Wrap (fun () -> Sys.remove f)
|
|
||||||
|
|
||||||
let _read_dir d () =
|
|
||||||
if Sys.is_directory d
|
|
||||||
then
|
|
||||||
let arr = Sys.readdir d in
|
|
||||||
Seq.map_pure make (Seq.of_array arr)
|
|
||||||
else Seq.empty
|
|
||||||
|
|
||||||
let rec _walk d () =
|
|
||||||
if Sys.is_directory d
|
|
||||||
then
|
|
||||||
let arr = Sys.readdir d in
|
|
||||||
let tail = Seq.of_array arr in
|
|
||||||
let tail = Seq.flat_map
|
|
||||||
(fun s -> return (_walk (Filename.concat d s) ()))
|
|
||||||
tail
|
|
||||||
in Seq.cons (`Dir,d) tail
|
|
||||||
else Seq.singleton (`File, d)
|
|
||||||
|
|
||||||
let walk t = Wrap (_walk t)
|
|
||||||
|
|
||||||
let read_dir ?(recurse=false) d =
|
|
||||||
if recurse
|
|
||||||
then walk d
|
|
||||||
>|= Seq.filter_map
|
|
||||||
(function
|
|
||||||
| `File, f -> Some f
|
|
||||||
| `Dir, _ -> None
|
|
||||||
)
|
|
||||||
else Wrap (_read_dir d)
|
|
||||||
|
|
||||||
let rec _read_dir_rec d () =
|
|
||||||
if Sys.is_directory d
|
|
||||||
then
|
|
||||||
let arr = Sys.readdir d in
|
|
||||||
let arr = Seq.of_array arr in
|
|
||||||
let arr = Seq.map_pure (fun s -> Filename.concat d s) arr in
|
|
||||||
Seq.flat_map
|
|
||||||
(fun s ->
|
|
||||||
if Sys.is_directory s
|
|
||||||
then return (_read_dir_rec s ())
|
|
||||||
else return (Seq.singleton s)
|
|
||||||
) arr
|
|
||||||
else Seq.empty
|
|
||||||
end
|
|
||||||
|
|
||||||
(** {2 Raw} *)
|
|
||||||
|
|
||||||
module Raw = struct
|
|
||||||
let wrap f = Wrap f
|
|
||||||
end
|
|
||||||
|
|
@ -1,322 +0,0 @@
|
||||||
(*
|
|
||||||
copyright (c) 2013-2014, simon cruanes
|
|
||||||
all rights reserved.
|
|
||||||
|
|
||||||
redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer. redistributions in binary
|
|
||||||
form must reproduce the above copyright notice, this list of conditions and the
|
|
||||||
following disclaimer in the documentation and/or other materials provided with
|
|
||||||
the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*)
|
|
||||||
|
|
||||||
(** {1 IO Monad}
|
|
||||||
|
|
||||||
A simple abstraction over blocking IO, with strict evaluation. This is in
|
|
||||||
no way an alternative to Lwt/Async if you need concurrency.
|
|
||||||
|
|
||||||
@since 0.3.3
|
|
||||||
*)
|
|
||||||
|
|
||||||
(**
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
- obtain the list of lines of a file:
|
|
||||||
|
|
||||||
{[
|
|
||||||
let l = CCIO.((with_in "/tmp/some_file" >>>= read_lines) |> run_exn);;
|
|
||||||
]}
|
|
||||||
|
|
||||||
- transfer one file into another:
|
|
||||||
|
|
||||||
{[
|
|
||||||
# let a = CCIO.(
|
|
||||||
with_in "input" >>>= fun ic ->
|
|
||||||
with_out ~flags:[Open_creat] "output" >>>= fun oc ->
|
|
||||||
Seq.chunks 512 ic
|
|
||||||
|> Seq.output oc
|
|
||||||
) ;;
|
|
||||||
|
|
||||||
# run a;;
|
|
||||||
]}
|
|
||||||
*)
|
|
||||||
|
|
||||||
type 'a t
|
|
||||||
type 'a io = 'a t
|
|
||||||
|
|
||||||
type 'a with_finalizer
|
|
||||||
(** A value of type ['a with_finalizer] is similar to a value ['a t] but
|
|
||||||
also contains a finalizer that must be run to cleanup.
|
|
||||||
See {!(>>>=)} to get rid of it. *)
|
|
||||||
|
|
||||||
type 'a or_error = [ `Ok of 'a | `Error of string ]
|
|
||||||
|
|
||||||
val (>>=) : 'a t -> ('a -> 'b t) -> 'b t
|
|
||||||
(** Wait for the result of an action, then use a function to build a
|
|
||||||
new action and execute it *)
|
|
||||||
|
|
||||||
val return : 'a -> 'a t
|
|
||||||
(** Just return a value *)
|
|
||||||
|
|
||||||
val repeat : int -> 'a t -> 'a list t
|
|
||||||
(** Repeat an IO action as many times as required *)
|
|
||||||
|
|
||||||
val repeat' : int -> 'a t -> unit t
|
|
||||||
(** Same as {!repeat}, but ignores the result *)
|
|
||||||
|
|
||||||
val map : ('a -> 'b) -> 'a t -> 'b t
|
|
||||||
(** Map values *)
|
|
||||||
|
|
||||||
val (>|=) : 'a t -> ('a -> 'b) -> 'b t
|
|
||||||
|
|
||||||
val bind : ?finalize:(unit t) -> ('a -> 'b t) -> 'a t -> 'b t
|
|
||||||
(** [bind f a] runs the action [a] and applies [f] to its result
|
|
||||||
to obtain a new action. It then behaves exactly like this new
|
|
||||||
action.
|
|
||||||
@param finalize an optional action that is always run after evaluating
|
|
||||||
the whole action *)
|
|
||||||
|
|
||||||
val pure : 'a -> 'a t
|
|
||||||
val (<*>) : ('a -> 'b) t -> 'a t -> 'b t
|
|
||||||
|
|
||||||
val lift : ('a -> 'b) -> 'a t -> 'b t
|
|
||||||
(** Synonym to {!map} *)
|
|
||||||
|
|
||||||
val lift2 : ('a -> 'b -> 'c) -> 'a t -> 'b t -> 'c t
|
|
||||||
val lift3 : ('a -> 'b -> 'c -> 'd) -> 'a t -> 'b t -> 'c t -> 'd t
|
|
||||||
|
|
||||||
val sequence : 'a t list -> 'a list t
|
|
||||||
(** Runs operations one by one and gather their results *)
|
|
||||||
|
|
||||||
val sequence_map : ('a -> 'b t) -> 'a list -> 'b list t
|
|
||||||
(** Generalization of {!sequence} *)
|
|
||||||
|
|
||||||
val fail : string -> 'a t
|
|
||||||
(** [fail msg] fails with the given message. Running the IO value will
|
|
||||||
return an [`Error] variant *)
|
|
||||||
|
|
||||||
(** {2 Finalizers} *)
|
|
||||||
|
|
||||||
val (>>>=) : 'a with_finalizer -> ('a -> 'b t) -> 'b t
|
|
||||||
(** Same as {!(>>=)}, but taking the finalizer into account. Once this
|
|
||||||
IO value is done executing, the finalizer is executed and the resource,
|
|
||||||
fred. *)
|
|
||||||
|
|
||||||
(** {2 Running} *)
|
|
||||||
|
|
||||||
val run : 'a t -> 'a or_error
|
|
||||||
(** Run an IO action.
|
|
||||||
@return either [`Ok x] when [x] is the successful result of the
|
|
||||||
computation, or some [`Error "message"] *)
|
|
||||||
|
|
||||||
exception IO_error of string
|
|
||||||
|
|
||||||
val run_exn : 'a t -> 'a
|
|
||||||
(** Unsafe version of {!run}. It assumes non-failure.
|
|
||||||
@raise IO_error if the execution didn't go well *)
|
|
||||||
|
|
||||||
val register_printer : (exn -> string option) -> unit
|
|
||||||
(** [register_printer p] register [p] as a possible failure printer.
|
|
||||||
If [run a] raises an exception [e], [p e] is evaluated. If [p e = Some msg]
|
|
||||||
then the error message will be [msg], otherwise other printers will
|
|
||||||
be tried *)
|
|
||||||
|
|
||||||
(** {2 Standard Wrappers} *)
|
|
||||||
|
|
||||||
(** {6 Input} *)
|
|
||||||
|
|
||||||
val with_in : ?mode:int -> ?flags:open_flag list ->
|
|
||||||
string -> in_channel with_finalizer
|
|
||||||
(** Open an input file with the given optional flag list.
|
|
||||||
It yields a [in_channel] with a finalizer attached. See {!(>>>=)} to
|
|
||||||
use it. *)
|
|
||||||
|
|
||||||
val read : in_channel -> Bytes.t -> int -> int -> int t
|
|
||||||
(** Read a chunk into the given string *)
|
|
||||||
|
|
||||||
val read_line : in_channel -> string option t
|
|
||||||
(** Read a line from the channel. Returns [None] if the input is terminated. *)
|
|
||||||
|
|
||||||
val read_lines : in_channel -> string list t
|
|
||||||
(** Read all lines eagerly *)
|
|
||||||
|
|
||||||
val read_all : in_channel -> string t
|
|
||||||
(** Read the whole channel into a buffer, then converted into a string *)
|
|
||||||
|
|
||||||
(** {6 Output} *)
|
|
||||||
|
|
||||||
val with_out : ?mode:int -> ?flags:open_flag list ->
|
|
||||||
string -> out_channel with_finalizer
|
|
||||||
(** Same as {!with_in} but for an output channel *)
|
|
||||||
|
|
||||||
val with_out_a : ?mode:int -> ?flags:open_flag list ->
|
|
||||||
string -> out_channel with_finalizer
|
|
||||||
(** Similar to {!with_out} but with the [Open_append] and [Open_creat]
|
|
||||||
flags activated *)
|
|
||||||
|
|
||||||
val write : out_channel -> string -> int -> int -> unit t
|
|
||||||
|
|
||||||
val write_str : out_channel -> string -> unit t
|
|
||||||
|
|
||||||
val write_buf : out_channel -> Buffer.t -> unit t
|
|
||||||
|
|
||||||
val write_line : out_channel -> string -> unit t
|
|
||||||
|
|
||||||
val flush : out_channel -> unit t
|
|
||||||
|
|
||||||
(* TODO: printf/fprintf wrappers *)
|
|
||||||
|
|
||||||
(** {2 Streams}
|
|
||||||
|
|
||||||
Iterators on chunks of bytes, or lines, or any other value using combinators.
|
|
||||||
Those iterators are usable only once, because their source might
|
|
||||||
be usable only once (think of a socket) *)
|
|
||||||
|
|
||||||
module Seq : sig
|
|
||||||
type 'a t
|
|
||||||
(** An IO stream of values of type 'a, consumable (iterable only once) *)
|
|
||||||
|
|
||||||
val map : ('a -> 'b io) -> 'a t -> 'b t
|
|
||||||
(** Map values with actions *)
|
|
||||||
|
|
||||||
val map_pure : ('a -> 'b) -> 'a t -> 'b t
|
|
||||||
(** Map values with a pure function *)
|
|
||||||
|
|
||||||
val filter_map : ('a -> 'b option) -> 'a t -> 'b t
|
|
||||||
|
|
||||||
val filter : ('a -> bool) -> 'a t -> 'a t
|
|
||||||
|
|
||||||
val flat_map : ('a -> 'b t io) -> 'a t -> 'b t
|
|
||||||
(** Map each value to a sub sequence of values *)
|
|
||||||
|
|
||||||
val take : int -> 'a t -> 'a t
|
|
||||||
|
|
||||||
val drop : int -> 'a t -> 'a t
|
|
||||||
|
|
||||||
val take_while : ('a -> bool io) -> 'a t -> 'a t
|
|
||||||
|
|
||||||
val drop_while : ('a -> bool io) -> 'a t -> 'a t
|
|
||||||
|
|
||||||
val general_iter : ('b -> 'a -> [`Stop | `Continue of ('b * 'c option)] io) ->
|
|
||||||
'b -> 'a t -> 'c t
|
|
||||||
(** [general_iter f acc seq] performs a [filter_map] over [seq],
|
|
||||||
using [f]. [f] is given a state and the current value, and
|
|
||||||
can either return [`Stop] to indicate it stops traversing,
|
|
||||||
or [`Continue (st, c)] where [st] is the new state and
|
|
||||||
[c] an optional output value.
|
|
||||||
The result is the stream of values output by [f] *)
|
|
||||||
|
|
||||||
val tee : ('a -> unit io) list -> 'a t -> 'a t
|
|
||||||
(** [tee funs seq] behaves like [seq], but each element is given to
|
|
||||||
every function [f] in [funs]. This function [f] returns an action that
|
|
||||||
is eagerly executed. *)
|
|
||||||
|
|
||||||
(** {6 Consume} *)
|
|
||||||
|
|
||||||
val iter : ('a -> _ io) -> 'a t -> unit io
|
|
||||||
(** Iterate on the stream, with an action for each element *)
|
|
||||||
|
|
||||||
val length : _ t -> int io
|
|
||||||
(** Length of the stream *)
|
|
||||||
|
|
||||||
val fold : ('b -> 'a -> 'b io) -> 'b -> 'a t -> 'b io
|
|
||||||
(** [fold f acc seq] folds over [seq], consuming it. Every call to [f]
|
|
||||||
has the right to return an IO value. *)
|
|
||||||
|
|
||||||
val fold_pure : ('b -> 'a -> 'b) -> 'b -> 'a t -> 'b io
|
|
||||||
(** [fold f acc seq] folds over [seq], consuming it. [f] is pure. *)
|
|
||||||
|
|
||||||
(** {6 Standard Wrappers} *)
|
|
||||||
|
|
||||||
type 'a step_result =
|
|
||||||
| Yield of 'a
|
|
||||||
| Stop
|
|
||||||
|
|
||||||
type 'a gen = unit -> 'a step_result io
|
|
||||||
|
|
||||||
val of_fun : 'a gen -> 'a t
|
|
||||||
(** Create a stream from a function that yields an element or stops *)
|
|
||||||
|
|
||||||
val empty : 'a t
|
|
||||||
val singleton : 'a -> 'a t
|
|
||||||
val cons : 'a -> 'a t -> 'a t
|
|
||||||
val of_list : 'a list -> 'a t
|
|
||||||
val of_array : 'a array -> 'a t
|
|
||||||
|
|
||||||
val chunks : size:int -> in_channel -> string t
|
|
||||||
(** Read the channel's content into chunks of size [size] *)
|
|
||||||
|
|
||||||
val lines : in_channel -> string t
|
|
||||||
(** Lines of an input channel *)
|
|
||||||
|
|
||||||
val words : string t -> string t
|
|
||||||
(** Split strings into words at " " boundaries.
|
|
||||||
{b NOT IMPLEMENTED} *)
|
|
||||||
|
|
||||||
val output : ?sep:string -> out_channel -> string t -> unit io
|
|
||||||
(** [output oc seq] outputs every value of [seq] into [oc], separated
|
|
||||||
with the optional argument [sep] (default: None).
|
|
||||||
It blocks until all values of [seq] are produced and written to [oc]. *)
|
|
||||||
end
|
|
||||||
|
|
||||||
(** {6 File and file names}
|
|
||||||
|
|
||||||
How to list recursively files in a directory:
|
|
||||||
{[
|
|
||||||
CCIO.(
|
|
||||||
File.read_dir ~recurse:true (File.make "/tmp")
|
|
||||||
>>= Seq.output ~sep:"\n" stdout
|
|
||||||
) |> CCIO.run_exn ;;
|
|
||||||
|
|
||||||
]}
|
|
||||||
|
|
||||||
See {!File.walk} if you also need to list directories.
|
|
||||||
*)
|
|
||||||
|
|
||||||
module File : sig
|
|
||||||
type t = string
|
|
||||||
(** A file is always represented by its absolute path *)
|
|
||||||
|
|
||||||
val to_string : t -> string
|
|
||||||
|
|
||||||
val make : string -> t
|
|
||||||
(** Build a file representation from a path (absolute or relative) *)
|
|
||||||
|
|
||||||
val exists : t -> bool io
|
|
||||||
|
|
||||||
val is_directory : t -> bool io
|
|
||||||
|
|
||||||
val remove : t -> unit io
|
|
||||||
|
|
||||||
val read_dir : ?recurse:bool -> t -> t Seq.t io
|
|
||||||
(** [read_dir d] returns a sequence of files and directory contained
|
|
||||||
in the directory [d] (or an empty stream if [d] is not a directory)
|
|
||||||
@param recurse if true (default [false]), sub-directories are also
|
|
||||||
explored *)
|
|
||||||
|
|
||||||
val walk : t -> ([`File | `Dir] * t) Seq.t io
|
|
||||||
(** Similar to {!read_dir} (with [recurse=true]), this function walks
|
|
||||||
a directory recursively and yields either files or directories.
|
|
||||||
Is a file anything that doesn't satisfy {!is_directory} (including
|
|
||||||
symlinks, etc.) *)
|
|
||||||
end
|
|
||||||
|
|
||||||
(** {2 Low level access} *)
|
|
||||||
module Raw : sig
|
|
||||||
val wrap : (unit -> 'a) -> 'a t
|
|
||||||
(** [wrap f] is the IO action that, when executed, returns [f ()].
|
|
||||||
[f] should be callable as many times as required *)
|
|
||||||
end
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
(*
|
|
||||||
copyright (c) 2013-2015, simon cruanes
|
|
||||||
all rights reserved.
|
|
||||||
|
|
||||||
redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer. redistributions in binary
|
|
||||||
form must reproduce the above copyright notice, this list of conditions and the
|
|
||||||
following disclaimer in the documentation and/or other materials provided with
|
|
||||||
the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*)
|
|
||||||
|
|
||||||
module Batch = CCBatch
|
|
||||||
module Cat = CCCat
|
|
||||||
module Linq = CCLinq
|
|
||||||
module MonadIO = CCMonadIO
|
|
||||||
Loading…
Add table
Reference in a new issue