(M : MonadError) :
  sig
    type ('a, 'r) withres = { cons : '-> 'M.m; fin : '-> unit M.m }
    
    val bindres : ('a, 'r) withres -> '-> ('-> 'M.m) -> 'M.m
      
    val with_alt :
      ('a, 'r) withres ->
        ('b, 'r) withres -> (('a * 'b), ((exn option) * 'r)) withres
      
    val with_identity : ('r, 'r) withres
      
    val premap : ('-> 'b) -> ('b, 'r) withres -> ('a, 'r) withres
      
    type dir_abstract
    
    val with_sys_chdir : (string, dir_abstract) withres
      
  end =
  struct
    type ('a, 'r) withres = { cons : '-> 'M.m; fin : '-> unit M.m }
    
    let ( %> ) f g x = g (f x)
      
    let premap : ('-> 'b) -> ('b, 'r) withres -> ('a, 'r) withres =
      fun ab wbr -> { cons = ab %> wbr.cons; fin = wbr.fin; }
      
    let with_identity = { cons = M.return; fin = (fun _r -> M.return ()); }
      
    let ( >>= ) = M.bind_rev
      
    let bindres wr a f =
      (wr.cons a) >>=
        (fun r ->
           M.catch
             (fun () ->
                (f r) >>= (fun z -> (wr.fin r) >>= (fun () -> M.return z)))
             (fun e -> (wr.fin r) >>= (fun () -> M.error e)))
      
    let with_alt wr1 wr2 =
      let fin = ref wr1.fin
      in
        {
          cons =
            (fun (a, b) ->
               M.catch
                 (fun () -> (wr1.cons a) >>= (fun r -> M.return (None, r)))
                 (fun e ->
                    (wr2.cons b) >>=
                      (fun r -> (fin := wr2.fin; M.return ((Some e), r)))));
          fin = (fun (_opt_err, r) -> !fin r);
        }
      
    type dir_abstract = string
    
    let with_sys_chdir : (string, dir_abstract) withres =
      {
        cons =
          (fun new_dir ->
             M.catch
               (fun () ->
                  let old_dir = Sys.getcwd () in
                  let () = Sys.chdir new_dir in M.return old_dir)
               M.error);
        fin =
          (fun old_dir ->
             M.catch (fun () -> M.return (Sys.chdir old_dir)) M.error);
      }
      
  end