Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kleisli Composition Operator and Apply_or Added #455

Merged
merged 7 commits into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/core/CCFun.mli
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,13 @@ module Monad (X : sig
type t
end) : sig
type 'a t = X.t -> 'a
(** Definition of a monad in continuation-passing style. *)

val return : 'a -> 'a t
(** Monadic [return]. *)

val ( >|= ) : 'a t -> ('a -> 'b) -> 'b t
(** Monadic [map]. *)

val ( >>= ) : 'a t -> ('a -> 'b t) -> 'b t
(** Monadic [bind]. *)
Expand Down
17 changes: 17 additions & 0 deletions src/core/CCOption.ml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ let[@inline] bind o f = flat_map f o
let ( >>= ) = bind
let pure x = Some x

let k_compose f g =
(fun x -> f x |> flat_map g)
let ( >=> ) = k_compose
let ( <=< ) f g = (>=>) g f

let ( <*> ) f x =
match f, x with
| None, _ | _, None -> None
Expand Down Expand Up @@ -111,6 +116,13 @@ let get_or ~default x =
| None -> default
| Some y -> y

let apply_or f x =
Copy link
Owner

@c-cube c-cube Jul 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't that fold? edit: oh, no, same default value…

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was motivated mostly by seeing places where CCString functions were being stringed together, combined with repeated CCOption's get_or to reset back to the original string if the string functions failed. Particularly the operator form would concisely shrink down that sort of pipeline to just the string functions.

match f x with
| None -> x
| Some y -> y

let ( |?> ) x f = apply_or f x

let value x ~default =
match x with
| None -> default
Expand Down Expand Up @@ -181,6 +193,7 @@ module Infix = struct
let ( <*> ) = ( <*> )
let ( <$> ) = map
let ( <+> ) = ( <+> )
let ( |?> ) = ( |?> )
let ( let+ ) = ( >|= )
let ( let* ) = ( >>= )

Expand All @@ -190,6 +203,10 @@ module Infix = struct
| _ -> None

let ( and* ) = ( and+ )

let ( >=> ) = ( >=> )

let ( <=< ) = ( <=< )
end

include Infix
Expand Down
18 changes: 18 additions & 0 deletions src/core/CCOption.mli
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ val bind : 'a t -> ('a -> 'b t) -> 'b t
Monadic bind.
@since 3.0 *)

val k_compose : ('a -> 'b t) -> ('b -> 'c t) -> ('a -> 'c t)
(** Kleisli composition. Monadic equivalent of CCFun.compose *)

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]. *)

Expand Down Expand Up @@ -92,6 +95,12 @@ val get_or : default:'a -> 'a t -> 'a
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".
*)

val value : 'a t -> default:'a -> 'a
(** [value o ~default] is similar to the Stdlib's [Option.value] and to {!get_or}.
@since 2.8 *)
Expand Down Expand Up @@ -175,10 +184,19 @@ module Infix : sig
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] *)

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]. *)
c-cube marked this conversation as resolved.
Show resolved Hide resolved

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

include module type of Infix
Expand Down
17 changes: 17 additions & 0 deletions src/core/CCResult.ml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,13 @@ let get_or e ~default =
| Ok x -> x
| Error _ -> default

let apply_or f x =
match f x with
| Error _ -> x
| Ok y -> y

let ( |?> ) x f = apply_or f x

let get_lazy f e =
match e with
| Ok x -> x
Expand All @@ -125,6 +132,12 @@ let flat_map f e =
| Ok x -> f x
| Error s -> Error s

let k_compose f g =
(fun x -> flat_map g @@ f x)

let ( >=> ) = k_compose
let ( <=< ) f g = ( >=> ) g f

let equal ~err eq a b =
match a, b with
| Ok x, Ok y -> eq x y
Expand Down Expand Up @@ -265,6 +278,7 @@ module Infix = struct
let ( >|= ) e f = map f e
let ( >>= ) e f = flat_map f e
let ( <*> ) = ( <*> )
let ( |?> ) = ( |?> )
let ( let+ ) = ( >|= )
let ( let* ) = ( >>= )

Expand All @@ -275,6 +289,9 @@ module Infix = struct
| _, Error e -> Error e

let ( and* ) = ( and+ )

let ( >=> ) = ( >=> )
let ( <=< ) = ( <=< )
end

include Infix
Expand Down
18 changes: 18 additions & 0 deletions src/core/CCResult.mli
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,12 @@ val get_exn : ('a, _) t -> 'a
val get_or : ('a, _) t -> default:'a -> 'a
(** [get_or e ~default] returns [x] if [e = Ok x], [default] otherwise. *)

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".
*)

val get_or_failwith : ('a, string) t -> 'a
(** [get_or_failwith e] returns [x] if [e = Ok x], fails otherwise.
@raise Failure with [msg] if [e = Error msg].
Expand All @@ -113,6 +119,10 @@ val catch : ('a, 'err) t -> ok:('a -> 'b) -> err:('err -> 'b) -> 'b
the value of [e]. *)

val flat_map : ('a -> ('b, 'err) t) -> ('a, 'err) t -> ('b, 'err) t

val k_compose : ('a -> ('b, 'err) t) -> ('b -> ('c, 'err) t) -> ('a -> ('c, 'err) t)
(** Kleisli composition. Monadic equivalent of CCFun.compose *)

val equal : err:'err equal -> 'a equal -> ('a, 'err) t equal
val compare : err:'err ord -> 'a ord -> ('a, 'err) t ord

Expand Down Expand Up @@ -188,6 +198,8 @@ module Infix : sig
[Ok (a b)]. Otherwise, it fails, and the error of [a] is chosen
over the error of [b] if both fail. *)

val ( |?> ) : 'a -> ('a -> ('a, _) t) -> 'a

val ( let+ ) : ('a, 'e) t -> ('a -> 'b) -> ('b, 'e) t
(** @since 2.8 *)

Expand All @@ -199,6 +211,12 @@ module Infix : sig

val ( and* ) : ('a, 'e) t -> ('b, 'e) t -> ('a * 'b, 'e) t
(** @since 2.8 *)

val ( >=> ) : ('a -> ('b, 'err) t) -> ('b -> ('c, 'err) t) -> ('a -> ('c, 'err) t)
(** Monadic [k_compose]. *)
c-cube marked this conversation as resolved.
Show resolved Hide resolved

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

include module type of Infix
Expand Down
Loading