mirror of
https://github.com/c-cube/ocaml-containers.git
synced 2025-12-07 11:45:31 -05:00
CCIO: explicit finalizer system, to use a >>>= operator rather than callbacks.
bugfix in Seq.chunks
This commit is contained in:
parent
b88461d834
commit
d03ea3dc54
2 changed files with 94 additions and 33 deletions
75
core/CCIO.ml
75
core/CCIO.ml
|
|
@ -31,22 +31,22 @@ type _ t =
|
||||||
| Fail : string -> 'a t
|
| Fail : string -> 'a t
|
||||||
| Map : ('a -> 'b) * 'a t -> 'b t
|
| Map : ('a -> 'b) * 'a t -> 'b t
|
||||||
| Bind : ('a -> 'b t) * 'a t -> 'b t
|
| Bind : ('a -> 'b t) * 'a t -> 'b t
|
||||||
| BindWith : unit t * ('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
|
| Star : ('a -> 'b) t * 'a t -> 'b t
|
||||||
| Repeat : int * 'a t -> 'a list t
|
| Repeat : int * 'a t -> 'a list t
|
||||||
| RepeatIgnore : int * 'a t -> unit t
|
| RepeatIgnore : int * 'a t -> unit t
|
||||||
| Wrap : (unit -> 'a) -> 'a t
|
| Wrap : (unit -> 'a) -> 'a t
|
||||||
| WrapJoin : (unit -> 'a t) -> 'a t
|
|
||||||
| SequenceMap : ('a -> 'b t) * 'a list -> 'b list t
|
| SequenceMap : ('a -> 'b t) * 'a list -> 'b list t
|
||||||
|
|
||||||
type 'a io = 'a 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 ]
|
type 'a or_error = [ `Ok of 'a | `Error of string ]
|
||||||
|
|
||||||
let (>>=) x f = Bind(f,x)
|
let (>>=) x f = Bind(f,x)
|
||||||
|
|
||||||
let bind ?finalize f a = match finalize with
|
let bind ?finalize f a = match finalize with
|
||||||
| None -> Bind(f,a)
|
| None -> Bind(f,a)
|
||||||
| Some b -> BindWith (b,f,a)
|
| Some b -> WithGuard (b, Bind (f,a))
|
||||||
|
|
||||||
let map f x = Map(f, x)
|
let map f x = Map(f, x)
|
||||||
|
|
||||||
|
|
@ -81,6 +81,14 @@ let repeat i a =
|
||||||
let repeat' i a =
|
let repeat' i a =
|
||||||
if i <= 0 then Return () else RepeatIgnore (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
|
exception IOFailure of string
|
||||||
|
|
||||||
let rec _run : type a. a t -> a = function
|
let rec _run : type a. a t -> a = function
|
||||||
|
|
@ -88,20 +96,19 @@ let rec _run : type a. a t -> a = function
|
||||||
| Fail msg -> raise (IOFailure msg)
|
| Fail msg -> raise (IOFailure msg)
|
||||||
| Map (f, a) -> f (_run a)
|
| Map (f, a) -> f (_run a)
|
||||||
| Bind (f, a) -> _run (f (_run a))
|
| Bind (f, a) -> _run (f (_run a))
|
||||||
| BindWith (finalize, f, a) ->
|
| WithGuard (g, a) ->
|
||||||
begin try
|
begin try
|
||||||
let res = _run (f (_run a)) in
|
let res = _run a in
|
||||||
_run finalize;
|
_run g;
|
||||||
res
|
res
|
||||||
with e ->
|
with e ->
|
||||||
_run finalize;
|
_run g;
|
||||||
raise e
|
raise e
|
||||||
end
|
end
|
||||||
| Star (f, a) -> _run f (_run a)
|
| Star (f, a) -> _run f (_run a)
|
||||||
| Repeat (i,a) -> _repeat [] i a
|
| Repeat (i,a) -> _repeat [] i a
|
||||||
| RepeatIgnore (i,a) -> _repeat_ignore i a
|
| RepeatIgnore (i,a) -> _repeat_ignore i a
|
||||||
| Wrap f -> f()
|
| Wrap f -> f()
|
||||||
| WrapJoin f -> _run (f())
|
|
||||||
| SequenceMap (f, l) -> _sequence_map f l []
|
| SequenceMap (f, l) -> _sequence_map f l []
|
||||||
and _repeat : type a. a list -> int -> a t -> a list
|
and _repeat : type a. a list -> int -> a t -> a list
|
||||||
= fun acc i a -> match i with
|
= fun acc i a -> match i with
|
||||||
|
|
@ -158,16 +165,14 @@ let register_printer p = _printers := p :: !_printers
|
||||||
|
|
||||||
(** {2 Standard Wrappers} *)
|
(** {2 Standard Wrappers} *)
|
||||||
|
|
||||||
let _with_in flags filename f () =
|
let _open_in mode flags filename () =
|
||||||
let ic = open_in_gen flags 0x644 filename in
|
open_in_gen flags mode filename
|
||||||
try
|
let _close_in ic () = close_in ic
|
||||||
f ic
|
|
||||||
with e ->
|
|
||||||
close_in ic;
|
|
||||||
raise e
|
|
||||||
|
|
||||||
let with_in ?(flags=[]) filename f =
|
let with_in ?(mode=0o644) ?(flags=[]) filename =
|
||||||
WrapJoin (_with_in flags filename f)
|
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 () = input ic s i len
|
||||||
let read ic s i len = Wrap (_read ic s i len)
|
let read ic s i len = Wrap (_read ic s i len)
|
||||||
|
|
@ -196,16 +201,17 @@ let _read_all ic () =
|
||||||
|
|
||||||
let read_all ic = Wrap(_read_all ic)
|
let read_all ic = Wrap(_read_all ic)
|
||||||
|
|
||||||
let _with_out flags filename f () =
|
let _open_out mode flags filename () =
|
||||||
let oc = open_out_gen flags 0x644 filename in
|
open_out_gen flags mode filename
|
||||||
try
|
let _close_out oc () = close_out oc
|
||||||
f oc
|
|
||||||
with e ->
|
|
||||||
close_out oc;
|
|
||||||
raise e
|
|
||||||
|
|
||||||
let with_out ?(flags=[]) filename f =
|
let with_out ?(mode=0o644) ?(flags=[]) filename =
|
||||||
WrapJoin (_with_out flags filename f)
|
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
|
||||||
|
|
||||||
let _write oc s i len () = output oc s i len
|
let _write oc s i len () = output oc s i len
|
||||||
let write oc s i len = Wrap (_write oc s i len)
|
let write oc s i len = Wrap (_write oc s i len)
|
||||||
|
|
@ -323,13 +329,18 @@ module Seq = struct
|
||||||
|
|
||||||
let chunks ~size ic =
|
let chunks ~size ic =
|
||||||
let buf = Buffer.create size in
|
let buf = Buffer.create size in
|
||||||
|
let eof = ref false in
|
||||||
let next() =
|
let next() =
|
||||||
try
|
if !eof then _stop()
|
||||||
|
else try
|
||||||
Buffer.add_channel buf ic size;
|
Buffer.add_channel buf ic size;
|
||||||
let s = Buffer.contents buf in
|
let s = Buffer.contents buf in
|
||||||
Buffer.clear buf;
|
Buffer.clear buf;
|
||||||
_yield s
|
_yield s
|
||||||
with End_of_file -> _stop()
|
with End_of_file ->
|
||||||
|
let s = Buffer.contents buf in
|
||||||
|
eof := true;
|
||||||
|
if s="" then _stop() else _yield s
|
||||||
in
|
in
|
||||||
next
|
next
|
||||||
|
|
||||||
|
|
@ -355,16 +366,20 @@ module Seq = struct
|
||||||
next
|
next
|
||||||
*)
|
*)
|
||||||
|
|
||||||
let output ?(sep="\n") oc seq =
|
let output ?sep oc seq =
|
||||||
let first = ref true in
|
let first = ref true in
|
||||||
iter
|
iter
|
||||||
(fun s ->
|
(fun s ->
|
||||||
|
(* print separator *)
|
||||||
( if !first
|
( if !first
|
||||||
then (first:=false; return ())
|
then (first:=false; return ())
|
||||||
else write_str oc sep
|
else match sep with
|
||||||
|
| None -> return ()
|
||||||
|
| Some sep -> write_str oc sep
|
||||||
) >>= fun () ->
|
) >>= fun () ->
|
||||||
write_str oc s
|
write_str oc s
|
||||||
) seq
|
) seq
|
||||||
|
>>= fun () -> flush oc
|
||||||
end
|
end
|
||||||
|
|
||||||
(** {2 Raw} *)
|
(** {2 Raw} *)
|
||||||
|
|
|
||||||
|
|
@ -29,11 +29,37 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
A simple abstraction over blocking IO, with strict evaluation. This is in
|
A simple abstraction over blocking IO, with strict evaluation. This is in
|
||||||
no way an alternative to Lwt/Async if you need concurrency.
|
no way an alternative to Lwt/Async if you need concurrency.
|
||||||
|
|
||||||
@since NEXT_RELEASE *)
|
@since NEXT_RELEASE
|
||||||
|
|
||||||
|
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 t
|
||||||
type 'a io = '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 ]
|
type 'a or_error = [ `Ok of 'a | `Error of string ]
|
||||||
|
|
||||||
val (>>=) : 'a t -> ('a -> 'b t) -> 'b t
|
val (>>=) : 'a t -> ('a -> 'b t) -> 'b t
|
||||||
|
|
@ -80,6 +106,15 @@ val fail : string -> 'a t
|
||||||
(** [fail msg] fails with the given message. Running the IO value will
|
(** [fail msg] fails with the given message. Running the IO value will
|
||||||
return an [`Error] variant *)
|
return an [`Error] variant *)
|
||||||
|
|
||||||
|
(** {2 Finalizers} *)
|
||||||
|
|
||||||
|
val (>>>=) : 'a with_finalizer -> ('a -> 'b t) -> 'b t
|
||||||
|
(** Alternative to {!(>>=)} that also takes a [unit t] value, that is a
|
||||||
|
finalizer. This action will run in any case (even failure).
|
||||||
|
Other than the finalizer, this behaves like {!(>>=)} *)
|
||||||
|
|
||||||
|
(** {2 Running} *)
|
||||||
|
|
||||||
val run : 'a t -> 'a or_error
|
val run : 'a t -> 'a or_error
|
||||||
(** Run an IO action.
|
(** Run an IO action.
|
||||||
@return either [`Ok x] when [x] is the successful result of the
|
@return either [`Ok x] when [x] is the successful result of the
|
||||||
|
|
@ -101,7 +136,11 @@ val register_printer : (exn -> string option) -> unit
|
||||||
|
|
||||||
(** {6 Input} *)
|
(** {6 Input} *)
|
||||||
|
|
||||||
val with_in : ?flags:open_flag list -> string -> (in_channel -> 'a t) -> 'a t
|
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 -> string -> int -> int -> int t
|
val read : in_channel -> string -> int -> int -> int t
|
||||||
(** Read a chunk into the given string *)
|
(** Read a chunk into the given string *)
|
||||||
|
|
@ -117,7 +156,14 @@ val read_all : in_channel -> string t
|
||||||
|
|
||||||
(** {6 Output} *)
|
(** {6 Output} *)
|
||||||
|
|
||||||
val with_out : ?flags:open_flag list -> string -> (out_channel -> 'a t) -> 'a t
|
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 : out_channel -> string -> int -> int -> unit t
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue