add CCResult, with dependency on result for retrocompat

This commit is contained in:
Simon Cruanes 2016-01-25 21:18:33 +01:00
parent e2848675f7
commit 02a9639d02
6 changed files with 435 additions and 2 deletions

View file

@ -28,6 +28,7 @@ B _build/examples
B _build/tests B _build/tests
PKG oUnit PKG oUnit
PKG benchmark PKG benchmark
PKG result
PKG threads PKG threads
PKG threads.posix PKG threads.posix
PKG lwt PKG lwt

4
_oasis
View file

@ -46,8 +46,8 @@ Library "containers"
Modules: CCVector, CCPrint, CCError, CCHeap, CCList, CCOpt, CCPair, Modules: CCVector, CCPrint, CCError, CCHeap, CCList, CCOpt, CCPair,
CCFun, CCHash, CCInt, CCBool, CCFloat, CCArray, CCRef, CCSet, CCFun, CCHash, CCInt, CCBool, CCFloat, CCArray, CCRef, CCSet,
CCOrd, CCRandom, CCString, CCHashtbl, CCMap, CCFormat, CCIO, CCOrd, CCRandom, CCString, CCHashtbl, CCMap, CCFormat, CCIO,
CCInt64, CCChar, Containers CCInt64, CCChar, CCResult, Containers
BuildDepends: bytes BuildDepends: bytes, result
# BuildDepends: bytes, bisect_ppx # BuildDepends: bytes, bisect_ppx
Library "containers_io" Library "containers_io"

View file

@ -44,6 +44,7 @@ CCPair
CCPrint CCPrint
CCRandom CCRandom
CCRef CCRef
CCResult
CCSet CCSet
CCString CCString
CCVector CCVector

1
opam
View file

@ -27,6 +27,7 @@ remove: [
depends: [ depends: [
"ocamlfind" {build} "ocamlfind" {build}
"base-bytes" "base-bytes"
"result"
"cppo" {build} "cppo" {build}
"oasis" {build} "oasis" {build}
"ocamlbuild" {build} "ocamlbuild" {build}

248
src/core/CCResult.ml Normal file
View 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
View 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