type ('a, 'e) res = [ | `Ok of '| `Error of 'e ]

type 'a m = ('a, exn) res

let return r = `Ok r
  
let fail e = `Error e
  
let error e = `Error e
  
let bind f m = match m with | `Ok a -> f a | (`Error _ as e) -> e
  
let ( >>= ) m f = bind f m
  
let bind_rev = ( >>= )
  
let catch func handler =
  match func () with | (`Ok _ as r) -> r | `Error e -> handler e
  
let wrap1 f a = try `Ok (f a) with | e -> `Error e
  
let wrap2 f a b = try `Ok (f a b) with | e -> `Error e
  
let wrap3 f a b c = try `Ok (f a b c) with | e -> `Error e
  
let wrap4 f a b c d = try `Ok (f a b c d) with | e -> `Error e
  
let wrap5 f a b c d e = try `Ok (f a b c d e) with | e' -> `Error e'
  
let catch_exn func = try func () with | e -> fail e
  
let catch_all f handler = catch (fun () -> catch_exn f) handler
  
let exn_res r = match r with | `Ok x -> x | `Error e -> raise e
  
let map_err f r = match r with | (`Ok _ as r) -> r | `Error e -> `Error (f e)
  
let res_opterr oe = match oe with | None -> `Ok () | Some e -> `Error e
  
let res_optval ov = match ov with | None -> `Error () | Some v -> `Ok v
  
open Am_Ops
  
let res_exn func = catch_exn (return % func)
  
exception Foldres_exit
  
let (foldres_of_fold :
     (('-> '-> 'a) -> '-> '-> 'a) ->
       ('-> '-> ('a, 'e) res) -> '-> '-> ('a, 'e) res) =
  fun fold f init v ->
    let opt_err = ref None in
    let new_f a v =
      match f a v with
      | `Ok new_a -> new_a
      | `Error e -> (opt_err := Some e; raise Foldres_exit)
    in
      try `Ok (fold new_f init v)
      with
      | Foldres_exit ->
          (match !opt_err with | None -> assert false | Some e -> `Error e)
  
let rprintf fmt =
  Printf.ksprintf
    (fun str -> try return & (output_string stdout str) with | e -> `Error e)
    fmt
  
let reprintf fmt =
  Printf.ksprintf
    (fun str ->
       try return & (output_string stderr str; flush stderr)
       with | e -> `Error e)
    fmt
  
let wrap_with1 with1 a f = res_exn & (fun () -> with1 a (exn_res % f))
  
let wrap_with3 with3 a b c f =
  res_exn & (fun () -> with3 a b c (exn_res % f))
  
let list_map_all func lst =
  let rec inner rev_acc lst =
    match lst with
    | [] -> return & (List.rev rev_acc)
    | h :: t ->
        (match func h with
         | `Ok x -> inner (x :: rev_acc) t
         | `Error e -> `Error ((h, e)))
  in inner [] lst
  
let array_map_all func arr =
  let lst = Array.to_list arr
  in
    (list_map_all func lst) >>=
      (fun res_lst -> return & (Array.of_list res_lst))
  
let list_fold_left_all func init lst =
  let rec inner init lst =
    match lst with
    | [] -> return init
    | h :: t ->
        (match func init h with
         | `Ok x -> inner x t
         | `Error e -> `Error ((h, t, init, e)))
  in inner init lst
  
let list_iter_all func lst =
  catch
    (fun () ->
       list_fold_left_all (fun () x -> (func x : (unit, _) res)) () lst)
    (fun (h, t, (), e) -> fail (h, t, e))
  
let repeat n f a =
  let rec inner made a =
    if made >= n
    then `Ok a
    else
      (match f a with
       | `Ok a -> inner (made + 1) a
       | `Error e -> `Error ((e, made)))
  in inner 0 a