mirror of
https://github.com/c-cube/ocaml-containers.git
synced 2025-12-06 11:15:31 -05:00
add CCResult, with dependency on result for retrocompat
This commit is contained in:
parent
e2848675f7
commit
02a9639d02
6 changed files with 435 additions and 2 deletions
1
.merlin
1
.merlin
|
|
@ -28,6 +28,7 @@ B _build/examples
|
|||
B _build/tests
|
||||
PKG oUnit
|
||||
PKG benchmark
|
||||
PKG result
|
||||
PKG threads
|
||||
PKG threads.posix
|
||||
PKG lwt
|
||||
|
|
|
|||
4
_oasis
4
_oasis
|
|
@ -46,8 +46,8 @@ Library "containers"
|
|||
Modules: CCVector, CCPrint, CCError, CCHeap, CCList, CCOpt, CCPair,
|
||||
CCFun, CCHash, CCInt, CCBool, CCFloat, CCArray, CCRef, CCSet,
|
||||
CCOrd, CCRandom, CCString, CCHashtbl, CCMap, CCFormat, CCIO,
|
||||
CCInt64, CCChar, Containers
|
||||
BuildDepends: bytes
|
||||
CCInt64, CCChar, CCResult, Containers
|
||||
BuildDepends: bytes, result
|
||||
# BuildDepends: bytes, bisect_ppx
|
||||
|
||||
Library "containers_io"
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ CCPair
|
|||
CCPrint
|
||||
CCRandom
|
||||
CCRef
|
||||
CCResult
|
||||
CCSet
|
||||
CCString
|
||||
CCVector
|
||||
|
|
|
|||
1
opam
1
opam
|
|
@ -27,6 +27,7 @@ remove: [
|
|||
depends: [
|
||||
"ocamlfind" {build}
|
||||
"base-bytes"
|
||||
"result"
|
||||
"cppo" {build}
|
||||
"oasis" {build}
|
||||
"ocamlbuild" {build}
|
||||
|
|
|
|||
248
src/core/CCResult.ml
Normal file
248
src/core/CCResult.ml
Normal file
|
|
@ -0,0 +1,248 @@
|
|||
|
||||
(* This file is free software, part of containers. See file "license" for more details. *)
|
||||
|
||||
(** {1 Error Monad} *)
|
||||
|
||||
type 'a sequence = ('a -> unit) -> unit
|
||||
type 'a equal = 'a -> 'a -> bool
|
||||
type 'a ord = 'a -> 'a -> int
|
||||
type 'a printer = Buffer.t -> 'a -> unit
|
||||
type 'a formatter = Format.formatter -> 'a -> unit
|
||||
|
||||
(** {2 Basics} *)
|
||||
|
||||
type (+'good, +'bad) t = ('good, 'bad) Result.result =
|
||||
| Ok of 'good
|
||||
| Error of 'bad
|
||||
|
||||
let return x = Ok x
|
||||
|
||||
let fail s = Error s
|
||||
|
||||
let fail_printf format =
|
||||
let buf = Buffer.create 64 in
|
||||
Printf.kbprintf
|
||||
(fun buf -> fail (Buffer.contents buf))
|
||||
buf format
|
||||
|
||||
let fail_fprintf format =
|
||||
let buf = Buffer.create 64 in
|
||||
let out = Format.formatter_of_buffer buf in
|
||||
Format.kfprintf
|
||||
(fun out -> Format.pp_print_flush out (); fail (Buffer.contents buf))
|
||||
out format
|
||||
|
||||
let of_exn e =
|
||||
let msg = Printexc.to_string e in
|
||||
Error msg
|
||||
|
||||
let of_exn_trace e =
|
||||
let res = Printf.sprintf "%s\n%s"
|
||||
(Printexc.to_string e) (Printexc.get_backtrace ())
|
||||
in
|
||||
Error res
|
||||
|
||||
let map f e = match e with
|
||||
| Ok x -> Ok (f x)
|
||||
| Error s -> Error s
|
||||
|
||||
let map_err f e = match e with
|
||||
| Ok _ as res -> res
|
||||
| Error y -> Error (f y)
|
||||
|
||||
let map2 f g e = match e with
|
||||
| Ok x -> Ok (f x)
|
||||
| Error s -> Error (g s)
|
||||
|
||||
let iter f e = match e with
|
||||
| Ok x -> f x
|
||||
| Error _ -> ()
|
||||
|
||||
exception GetOnError
|
||||
|
||||
let get_exn = function
|
||||
| Ok x -> x
|
||||
| Error _ -> raise GetOnError
|
||||
|
||||
let catch e ~ok ~err = match e with
|
||||
| Ok x -> ok x
|
||||
| Error y -> err y
|
||||
|
||||
let flat_map f e = match e with
|
||||
| Ok x -> f x
|
||||
| Error s -> Error s
|
||||
|
||||
let (>|=) e f = map f e
|
||||
|
||||
let (>>=) e f = flat_map f e
|
||||
|
||||
let equal ?(err=Pervasives.(=)) eq a b = match a, b with
|
||||
| Ok x, Ok y -> eq x y
|
||||
| Error s, Error s' -> err s s'
|
||||
| _ -> false
|
||||
|
||||
let compare ?(err=Pervasives.compare) cmp a b = match a, b with
|
||||
| Ok x, Ok y -> cmp x y
|
||||
| Ok _, _ -> 1
|
||||
| _, Ok _ -> -1
|
||||
| Error s, Error s' -> err s s'
|
||||
|
||||
let fold ~ok ~error x = match x with
|
||||
| Ok x -> ok x
|
||||
| Error s -> error s
|
||||
|
||||
(** {2 Wrappers} *)
|
||||
|
||||
let guard f =
|
||||
try Ok (f ())
|
||||
with e -> Error e
|
||||
|
||||
let guard_str f =
|
||||
try Ok (f())
|
||||
with e -> of_exn e
|
||||
|
||||
let guard_str_trace f =
|
||||
try Ok (f())
|
||||
with e -> of_exn_trace e
|
||||
|
||||
let wrap1 f x =
|
||||
try return (f x)
|
||||
with e -> Error e
|
||||
|
||||
let wrap2 f x y =
|
||||
try return (f x y)
|
||||
with e -> Error e
|
||||
|
||||
let wrap3 f x y z =
|
||||
try return (f x y z)
|
||||
with e -> Error e
|
||||
|
||||
(** {2 Applicative} *)
|
||||
|
||||
let pure = return
|
||||
|
||||
let (<*>) f x = match f with
|
||||
| Error s -> fail s
|
||||
| Ok f -> map f x
|
||||
|
||||
let join t = match t with
|
||||
| Ok (Ok o) -> Ok o
|
||||
| Ok (Error e) -> Error e
|
||||
| (Error _) as e -> e
|
||||
|
||||
let both x y = match x,y with
|
||||
| Ok o, Ok o' -> Ok (o, o')
|
||||
| Ok _, Error e -> Error e
|
||||
| Error e, _ -> Error e
|
||||
|
||||
(** {2 Collections} *)
|
||||
|
||||
let map_l f l =
|
||||
let rec map acc l = match l with
|
||||
| [] -> Ok (List.rev acc)
|
||||
| x::l' ->
|
||||
match f x with
|
||||
| Error s -> Error s
|
||||
| Ok y -> map (y::acc) l'
|
||||
in map [] l
|
||||
|
||||
exception LocalExit
|
||||
|
||||
let fold_seq f acc seq =
|
||||
let err = ref None in
|
||||
try
|
||||
let acc = ref acc in
|
||||
seq
|
||||
(fun x -> match f !acc x with
|
||||
| Error s -> err := Some s; raise LocalExit
|
||||
| Ok y -> acc := y);
|
||||
Ok !acc
|
||||
with LocalExit ->
|
||||
match !err with None -> assert false | Some s -> Error s
|
||||
|
||||
let fold_l f acc l = fold_seq f acc (fun k -> List.iter k l)
|
||||
|
||||
(** {2 Misc} *)
|
||||
|
||||
let choose l =
|
||||
let rec find_ = function
|
||||
| [] -> raise Not_found
|
||||
| ((Ok _) as res) :: _ -> res
|
||||
| (Error _) :: l' -> find_ l'
|
||||
in
|
||||
try find_ l
|
||||
with Not_found ->
|
||||
let l' = List.map (function Error s -> s | Ok _ -> assert false) l in
|
||||
Error l'
|
||||
|
||||
let retry n f =
|
||||
let rec retry n acc = match n with
|
||||
| 0 -> fail (List.rev acc)
|
||||
| _ ->
|
||||
match f () with
|
||||
| Ok _ as res -> res
|
||||
| Error e -> retry (n-1) (e::acc)
|
||||
in retry n []
|
||||
|
||||
(** {2 Infix} *)
|
||||
|
||||
module Infix = struct
|
||||
let (>>=) = (>>=)
|
||||
let (>|=) = (>|=)
|
||||
let (<*>) = (<*>)
|
||||
end
|
||||
|
||||
(** {2 Monadic Operations} *)
|
||||
|
||||
module type MONAD = sig
|
||||
type 'a t
|
||||
val return : 'a -> 'a t
|
||||
val (>>=) : 'a t -> ('a -> 'b t) -> 'b t
|
||||
end
|
||||
|
||||
module Traverse(M : MONAD) = struct
|
||||
let (>>=) = M.(>>=)
|
||||
|
||||
let map_m f e = match e with
|
||||
| Error s -> M.return (Error s)
|
||||
| Ok x -> f x >>= fun y -> M.return (Ok y)
|
||||
|
||||
let sequence_m m = map_m (fun x->x) m
|
||||
|
||||
let fold_m f acc e = match e with
|
||||
| Error _ -> M.return acc
|
||||
| Ok x -> f acc x >>= fun y -> M.return y
|
||||
|
||||
let retry_m n f =
|
||||
let rec retry n acc = match n with
|
||||
| 0 -> M.return (fail (List.rev acc))
|
||||
| _ ->
|
||||
f () >>= function
|
||||
| Ok x -> M.return (Ok x)
|
||||
| Error e -> retry (n-1) (e::acc)
|
||||
in retry n []
|
||||
end
|
||||
|
||||
(** {2 Conversions} *)
|
||||
|
||||
let to_opt = function
|
||||
| Ok x -> Some x
|
||||
| Error _ -> None
|
||||
|
||||
let of_opt = function
|
||||
| None -> Error "of_opt"
|
||||
| Some x -> Ok x
|
||||
|
||||
let to_seq e k = match e with
|
||||
| Ok x -> k x
|
||||
| Error _ -> ()
|
||||
|
||||
(** {2 IO} *)
|
||||
|
||||
let pp pp_x buf e = match e with
|
||||
| Ok x -> Printf.bprintf buf "ok(%a)" pp_x x
|
||||
| Error s -> Printf.bprintf buf "error(%s)" s
|
||||
|
||||
let print pp_x fmt e = match e with
|
||||
| Ok x -> Format.fprintf fmt "@[ok(@,%a)@]" pp_x x
|
||||
| Error s -> Format.fprintf fmt "@[error(@,%s)@]" s
|
||||
182
src/core/CCResult.mli
Normal file
182
src/core/CCResult.mli
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
|
||||
(* This file is free software, part of containers. See file "license" for more details. *)
|
||||
|
||||
(** {1 Error Monad}
|
||||
|
||||
Uses the new "result" type from OCaml 4.03.
|
||||
|
||||
@since NEXT_RELEASE *)
|
||||
|
||||
type 'a sequence = ('a -> unit) -> unit
|
||||
type 'a equal = 'a -> 'a -> bool
|
||||
type 'a ord = 'a -> 'a -> int
|
||||
type 'a printer = Buffer.t -> 'a -> unit
|
||||
type 'a formatter = Format.formatter -> 'a -> unit
|
||||
|
||||
(** {2 Basics} *)
|
||||
|
||||
type (+'good, +'bad) t = ('good, 'bad) Result.result =
|
||||
| Ok of 'good
|
||||
| Error of 'bad
|
||||
|
||||
val return : 'a -> ('a, 'err) t
|
||||
(** Successfully return a value *)
|
||||
|
||||
val fail : 'err -> ('a, 'err) t
|
||||
(** Fail with an error *)
|
||||
|
||||
val of_exn : exn -> ('a, string) t
|
||||
(** [of_exn e] uses {!Printexc} to print the exception as a string *)
|
||||
|
||||
val of_exn_trace : exn -> ('a, string) t
|
||||
(** [of_exn_trace e] is similar to [of_exn e], but it adds the stacktrace
|
||||
to the error message.
|
||||
|
||||
Remember to call [Printexc.record_backtrace true] and compile with the
|
||||
debug flag for this to work. *)
|
||||
|
||||
val fail_printf : ('a, Buffer.t, unit, ('a, string) t) format4 -> 'a
|
||||
(** [fail_printf format] uses [format] to obtain an error message
|
||||
and then returns [Error msg] *)
|
||||
|
||||
val fail_fprintf : ('a, Format.formatter, unit, ('a, string) t) format4 -> 'a
|
||||
(** [fail_printf format] uses [format] to obtain an error message
|
||||
and then returns [Error msg] *)
|
||||
|
||||
val map : ('a -> 'b) -> ('a, 'err) t -> ('b, 'err) t
|
||||
(** Map on success *)
|
||||
|
||||
val map_err : ('err1 -> 'err2) -> ('a, 'err1) t -> ('a, 'err2) t
|
||||
(** Map on the error variant *)
|
||||
|
||||
val map2 : ('a -> 'b) -> ('err1 -> 'err2) -> ('a, 'err1) t -> ('b, 'err2) t
|
||||
(** Same as {!map}, but also with a function that can transform
|
||||
the error message in case of failure *)
|
||||
|
||||
val iter : ('a -> unit) -> ('a, _) t -> unit
|
||||
(** Apply the function only in case of Ok *)
|
||||
|
||||
exception GetOnError
|
||||
|
||||
val get_exn : ('a, _) t -> 'a
|
||||
(** Extract the value [x] from [Ok x], fails otherwise.
|
||||
You should be careful with this function, and favor other combinators
|
||||
whenever possible.
|
||||
@raise GetOnError if the value is an error. *)
|
||||
|
||||
val catch : ('a, 'err) t -> ok:('a -> 'b) -> err:('err -> 'b) -> 'b
|
||||
(** [catch e ~ok ~err] calls either [ok] or [err] depending on
|
||||
the value of [e]. *)
|
||||
|
||||
val flat_map : ('a -> ('b, 'err) t) -> ('a, 'err) t -> ('b, 'err) t
|
||||
|
||||
val (>|=) : ('a, 'err) t -> ('a -> 'b) -> ('b, 'err) t
|
||||
|
||||
val (>>=) : ('a, 'err) t -> ('a -> ('b, 'err) t) -> ('b, 'err) t
|
||||
|
||||
val equal : ?err:'err equal -> 'a equal -> ('a, 'err) t equal
|
||||
|
||||
val compare : ?err:'err ord -> 'a ord -> ('a, 'err) t ord
|
||||
|
||||
val fold : ok:('a -> 'b) -> error:('err -> 'b) -> ('a, 'err) t -> 'b
|
||||
(** [fold ~ok ~error e] opens [e] and, if [e = Ok x], returns
|
||||
[ok x], otherwise [e = Error s] and it returns [error s]. *)
|
||||
|
||||
(** {2 Wrappers} *)
|
||||
|
||||
val guard : (unit -> 'a) -> ('a, exn) t
|
||||
(** [guard f] runs [f ()] and returns its result wrapped in [Ok]. If
|
||||
[f ()] raises some exception [e], then it fails with [Error e] *)
|
||||
|
||||
val guard_str : (unit -> 'a) -> ('a, string) t
|
||||
(** Same as {!guard} but uses {!of_exn} to print the exception. *)
|
||||
|
||||
val guard_str_trace : (unit -> 'a) -> ('a, string) t
|
||||
(** Same as {!guard_str} but uses {!of_exn_trace} instead of {!of_exn} so
|
||||
that the stack trace is printed. *)
|
||||
|
||||
val wrap1 : ('a -> 'b) -> 'a -> ('b, exn) t
|
||||
(** Same as {!guard} but gives the function one argument. *)
|
||||
|
||||
val wrap2 : ('a -> 'b -> 'c) -> 'a -> 'b -> ('c, exn) t
|
||||
(** Same as {!guard} but gives the function two arguments. *)
|
||||
|
||||
val wrap3 : ('a -> 'b -> 'c -> 'd) -> 'a -> 'b -> 'c -> ('d, exn) t
|
||||
(** Same as {!guard} but gives the function three arguments. *)
|
||||
|
||||
(** {2 Applicative} *)
|
||||
|
||||
val pure : 'a -> ('a, 'err) t
|
||||
(** Synonym of {!return} *)
|
||||
|
||||
val (<*>) : ('a -> 'b, 'err) t -> ('a, 'err) t -> ('b, 'err) t
|
||||
(** [a <*> b] evaluates [a] and [b], and, in case of success, returns
|
||||
[Ok (a b)]. Otherwise, it fails, and the error of [a] is chosen
|
||||
over the error of [b] if both fail. *)
|
||||
|
||||
val join : (('a, 'err) t, 'err) t -> ('a, 'err) t
|
||||
(** [join t], in case of success, returns [Ok o] from [Ok (Ok o)]. Otherwise,
|
||||
it fails with [Error e] where [e] is the unwrapped error of [t]. *)
|
||||
|
||||
val both : ('a, 'err) t -> ('b, 'err) t -> (('a * 'b), 'err) t
|
||||
(** [both a b], in case of success, returns [Ok (o, o')] with the ok values
|
||||
of [a] and [b]. Otherwise, it fails, and the error of [a] is chosen over the
|
||||
error of [b] if both fail. *)
|
||||
|
||||
(** {2 Infix} *)
|
||||
|
||||
module Infix : sig
|
||||
val (>|=) : ('a, 'err) t -> ('a -> 'b) -> ('b, 'err) t
|
||||
val (>>=) : ('a, 'err) t -> ('a -> ('b, 'err) t) -> ('b, 'err) t
|
||||
val (<*>) : ('a -> 'b, 'err) t -> ('a, 'err) t -> ('b, 'err) t
|
||||
end
|
||||
|
||||
(** {2 Collections} *)
|
||||
|
||||
val map_l : ('a -> ('b, 'err) t) -> 'a list -> ('b list, 'err) t
|
||||
|
||||
val fold_l : ('b -> 'a -> ('b, 'err) t) -> 'b -> 'a list -> ('b, 'err) t
|
||||
|
||||
val fold_seq : ('b -> 'a -> ('b, 'err) t) -> 'b -> 'a sequence -> ('b, 'err) t
|
||||
|
||||
(** {2 Misc} *)
|
||||
|
||||
val choose : ('a, 'err) t list -> ('a, 'err list) t
|
||||
(** [choose l] selects a member of [l] that is a [Ok _] value,
|
||||
or returns [Error l] otherwise, where [l] is the list of errors. *)
|
||||
|
||||
val retry : int -> (unit -> ('a, 'err) t) -> ('a, 'err list) t
|
||||
(** [retry n f] calls [f] at most [n] times, returning the first result
|
||||
of [f ()] that doesn't fail. If [f] fails [n] times, [retry n f] fails
|
||||
with the list of successive errors. *)
|
||||
|
||||
(** {2 Monadic Operations} *)
|
||||
module type MONAD = sig
|
||||
type 'a t
|
||||
val return : 'a -> 'a t
|
||||
val (>>=) : 'a t -> ('a -> 'b t) -> 'b t
|
||||
end
|
||||
|
||||
module Traverse(M : MONAD) : sig
|
||||
val sequence_m : ('a M.t, 'err) t -> ('a, 'err) t M.t
|
||||
|
||||
val fold_m : ('b -> 'a -> 'b M.t) -> 'b -> ('a, 'err) t -> 'b M.t
|
||||
|
||||
val map_m : ('a -> 'b M.t) -> ('a, 'err) t -> ('b, 'err) t M.t
|
||||
|
||||
val retry_m : int -> (unit -> ('a, 'err) t M.t) -> ('a, 'err list) t M.t
|
||||
end
|
||||
|
||||
(** {2 Conversions} *)
|
||||
|
||||
val to_opt : ('a, _) t -> 'a option
|
||||
|
||||
val of_opt : 'a option -> ('a, string) t
|
||||
|
||||
val to_seq : ('a, _) t -> 'a sequence
|
||||
|
||||
(** {2 IO} *)
|
||||
|
||||
val pp : 'a printer -> ('a, string) t printer
|
||||
|
||||
val print : 'a formatter -> ('a, string) t formatter
|
||||
Loading…
Add table
Reference in a new issue