(* This file is free software, part of containers. See file "license" for more details. *)

(** Basic operations on the option type.

   This module replaces `CCOpt`.
   @since 3.6 *)

include module type of Option
(** @inline *)

val map_or : default:'b -> ('a -> 'b) -> 'a t -> 'b
(** [map_or ~default f o] is [f x] if [o = Some x], [default] otherwise.
    @since 0.16 *)

val map_lazy : (unit -> 'b) -> ('a -> 'b) -> 'a t -> 'b
(** [map_lazy default_fn f o] is [f x] if [o = Some x], [default_fn ()] otherwise.
    @since 1.2 *)

val return : 'a -> 'a t
(** [return x] is a monadic return, that is [return x = Some x]. *)

val flat_map : ('a -> 'b t) -> 'a t -> 'b t
(** [flat_map f o] is equivalent to {!map} followed by {!flatten}.
    Flip version of {!(>>=)}. *)

val flat_map_l : ('a -> 'b list) -> 'a t -> 'b list
(** [flat_map_l f o] is [[]] if [o] is [None], or [f x] if [o] is [Some x].
    @since 3.12 *)

val k_compose : ('a -> 'b t) -> ('b -> 'c t) -> 'a -> 'c t
(** Kleisli composition. Monadic equivalent of {!CCFun.compose}
      @since 3.13.1 *)

val map2 : ('a -> 'b -> 'c) -> 'a t -> 'b t -> 'c t
(** [map2 f o1 o2] maps ['a option] and ['b option] to a ['c option] using [f]. *)

val fold : ('a -> 'b -> 'a) -> 'a -> 'b t -> 'a
(** [fold f init o] is [f init x] if [o] is [Some x], or [init] if [o] is [None].
    Fold on 0 or 1 element. *)

val filter : ('a -> bool) -> 'a t -> 'a t
(** [filter f o] returns [Some x] if [o] is [Some x] and [f x] is [true],
    or [None] if [f x] is [false] or if [o] is [None].
    Filter on 0 or 1 element.
    @since 0.5 *)

val if_ : ('a -> bool) -> 'a -> 'a option
(** [if_ f x] is [Some x] if [f x], [None] otherwise.
    @since 0.17 *)

val exists : ('a -> bool) -> 'a t -> bool
(** [exists f o] returns [true] iff there exists an element for which
    the provided function [f] evaluates to [true].
    @since 0.17 *)

val for_all : ('a -> bool) -> 'a t -> bool
(** [for_all f o] returns [true] iff the provided function [f] evaluates to [true] for all elements.
    @since 0.17 *)

val get_or : default:'a -> 'a t -> 'a
(** [get_or ~default o] extracts the value from [o], or
    returns [default] if [o] is [None].
    @since 0.18 *)

val apply_or : ('a -> 'a t) -> 'a -> 'a
(** [apply_or f x] returns the original [x] if [f] fails, or unwraps [f x] if it succeeds.
    Useful for piping preprocessing functions together (such as string processing),
    turning functions like "remove" into "remove_if_it_exists".
    @since 3.13.1 *)

val get_exn : 'a t -> 'a
[@@ocaml.deprecated "use CCOption.get_exn_or instead"]
(** [get_exn o] returns [x] if [o] is [Some x] or fails if [o] is [None].
    @raise Invalid_argument if the option is [None].
    @deprecated use {!get_exn_or} instead
*)

val get_exn_or : string -> 'a t -> 'a
(** [get_exn_or msg o] returns [x] if [o] is [Some x]
    or fails with [Invalid_argument msg] if [o] is [None].
    @raise Invalid_argument if the option is [None].
    @since 3.4 *)

val get_lazy : (unit -> 'a) -> 'a t -> 'a
(** [get_lazy default_fn o] unwraps [o], but if [o] is [None] it returns [default_fn ()] instead.
    @since 0.6.1 *)

val sequence_l : 'a t list -> 'a list t
(** [sequence_l [x1; x2; …; xn]] returns [Some [y1; y2; …; yn]] if
    every [xi] is [Some yi]. Otherwise, if the list contains at least
    one [None], the result is [None]. *)

val wrap : ?handler:(exn -> bool) -> ('a -> 'b) -> 'a -> 'b option
(** [wrap ?handler f x] calls [f x] and returns [Some y] if [f x = y]. If [f x] raises
    any exception, the result is [None]. This can be useful to wrap functions
    such as [Map.S.find].
    @param handler the exception handler, which returns [true] if the
        exception is to be caught. *)

val wrap2 : ?handler:(exn -> bool) -> ('a -> 'b -> 'c) -> 'a -> 'b -> 'c option
(** [wrap2 ?handler f x y] is similar to {!wrap} but for binary functions. *)

(** {2 Applicative} *)

val pure : 'a -> 'a t
(** [pure x] is an alias to {!return}. *)

(** {2 Alternatives} *)

val or_ : else_:'a t -> 'a t -> 'a t
(** [or_ ~else_ o] is [o] if [o] is [Some _], [else_] if [o] is [None].
    @since 1.2 *)

val or_lazy : else_:(unit -> 'a t) -> 'a t -> 'a t
(** [or_lazy ~else_ o] is [o] if [o] is [Some _], [else_ ()] if [o] is [None].
    @since 1.2 *)

val choice : 'a t list -> 'a t
(** [choice lo] returns the first non-[None] element of the list [lo], or [None]. *)

val flatten : 'a t t -> 'a t
(** [flatten oo] transforms [Some x] into [x].
    @since 2.2 *)

val return_if : bool -> 'a -> 'a t
(** [return_if b x] applies [Some] or [None] depending on the boolean [b].
    More precisely, [return_if false x] is [None],
    and [return_if true x] is [Some x].
    @since 2.2 *)

(** {2 Infix Operators}
    @since 0.16 *)

module Infix : sig
  val ( >|= ) : 'a t -> ('a -> 'b) -> 'b t
  (** [o >|= f] is [map f o]. *)

  val ( >>= ) : 'a t -> ('a -> 'b t) -> 'b t
  (** [o >>= f] is the monadic bind. *)

  val ( <*> ) : ('a -> 'b) t -> 'a t -> 'b t
  (** [f <*> o] returns [Some (f x)] if [o] is [Some x] and [None] if [o] is [None]. *)

  val ( <$> ) : ('a -> 'b) -> 'a t -> 'b t
  (** [f <$> o] is like [map f o].  *)

  val ( <+> ) : 'a t -> 'a t -> 'a t
  (** [o1 <+> o2] is [o1] if [o1] is [Some _], [o2] if [o1] is [None]. *)

  val ( |?> ) : 'a -> ('a -> 'a t) -> 'a
  (** [x |?> f] is [apply_or f x]
      @since 3.13.1 *)

  val ( let+ ) : 'a t -> ('a -> 'b) -> 'b t
  val ( and+ ) : 'a t -> 'b t -> ('a * 'b) t
  val ( let* ) : 'a t -> ('a -> 'b t) -> 'b t
  val ( and* ) : 'a t -> 'b t -> ('a * 'b) t

  val ( >=> ) : ('a -> 'b t) -> ('b -> 'c t) -> 'a -> 'c t
  (** Monadic [k_compose].
      @since 3.13.1 *)

  val ( <=< ) : ('b -> 'c t) -> ('a -> 'b t) -> 'a -> 'c t
  (** Reverse monadic [k_compose].
      @since 3.13.1 *)
end

include module type of Infix

(** {2 Conversion and IO} *)

val of_list : 'a list -> 'a t
(** [of_list l] returns [Some x] (x being the head of the list l), or [None] if [l] is the empty list. *)

val to_result : 'e -> 'a t -> ('a, 'e) result
(** [to_result e o] returns [Ok x] if [o] is [Some x], or [Error e] if [o] is [None].
    @since 1.2 *)

val to_result_lazy : (unit -> 'e) -> 'a t -> ('a, 'e) result
(** [to_result_lazy f o] returns [Ok x] if [o] is [Some x] or [Error f] if [o] is [None].
    @since 1.2 *)

val of_result : ('a, _) result -> 'a t
(** [of_result result] returns an option from a [result].
    @since 1.2 *)

type 'a iter = ('a -> unit) -> unit
type 'a gen = unit -> 'a option
type 'a printer = Format.formatter -> 'a -> unit
type 'a random_gen = Random.State.t -> 'a

val random : 'a random_gen -> 'a t random_gen

val choice_iter : 'a t iter -> 'a t
(** [choice_iter iter] is similar to {!choice}, but works on [iter].
    It returns the first [Some x] occurring in [iter], or [None] otherwise.
    @since 3.0 *)

val choice_seq : 'a t Seq.t -> 'a t
(** [choice_seq seq] works on [Seq.t].
    It returns the first [Some x] occurring in [seq], or [None] otherwise.
    @since 3.0 *)

val to_gen : 'a t -> 'a gen
(** [to_gen o] is [o] as a [gen]. [Some x] is the singleton [gen] containing [x]
    and [None] is the empty [gen]. *)

val to_iter : 'a t -> 'a iter
(** [to_iter o] returns an internal iterator, like in the library [Iter].
    @since 2.8 *)

val pp : 'a printer -> 'a t printer
(** [pp ppf o] pretty-prints option [o] using [ppf]. *)
