123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580(**
Sugar module signatures.
*)(**
Signatures for the dependencies used in Sugar's module builders.
*)moduleParams=struct(**
A generic signature describing a monad.
*)moduletypeMonad=sigtype'at(** A parametric type representing any OCaml value. *)valreturn:'a->'at(** Creates a constant value in this monad. *)val(>>=):'at->('a->'bt)->'bt(** Waits for the conclusion of the monad in the left,
and then, apply the unwrapped value to the function in the right.
*)end(**
Conventional module type to define the errors inside a project.
This is how Sugar understands the error handling layer of a project.
Like:
{[
module MyError = struct
type error = Not_found | Invalid_arg of string
end
]}
This module might be used to create blocking or asynchronous error handling
layers, using the Sugar functors, like:
{[
module MyResult = Sugar.Result.Make (MyError)
module MyResult2 = Sugar.Promise.Make (Lwt) (MyError)
module MyResult2 = MyResult.For (Lwt)
]}
*)moduletypeError=sigtypet(**
This type describes the errors of your project. It's one of the main requirements
to create a result monad.
If you don't want to specify your errors upfront, you can still use something like [unit] or
[string] as error type.
*)end(**
This signature describes an [Error] module that has some control over unexpected exceptions.
If you want to handle unexpected exceptions as they appear, you should probably define
an error case with the type [exn], like in the code below:
{[
module Error = struct
type t =
| Because_reasons
| Unexpected of exn
let panic e = Unexpected e
end
]}
*)moduletypeStrict_error=sigincludeErrorvalpanic:exn->t(**
When an exception is detected, this module can either terminate the process with a proper
message or chose convert the error to the type {!t}.
*)end(**
A monad that provides some awareness about unexpected exceptions.
This module is related to {{!Sugar.S.Params.Strict_error} Strict_error}.
*)moduletypeStrict_monad=sigincludeMonadvalcatch:(unit->'at)->(exn->'at)->'at(**
Checks if the monad returned by the thunk raised an exception, and applies
the given error handler if necessary.
This function has intentionally the
exact signature as [Lwt.catch]. This means the [Lwt] module is already a [Strict_monad]:
{[
let _ =
(module Lwt: Sugar.Params.Strict_monad)
]}
*)endendopenParamsmoduletypePromise=sigtypeerror(** Error definition imported from your project *)type'avalue=('a,error)Result.result(** An alias for [Pervasives.result] that can only work with errors of your project. *)type'amonad(**
This type is an alias to the underlining monad used to create your [Promise] module.
In other words, it points to the main type of your threading library ([Lwt.t] for Lwt,
or [Async.Std.Deferred.t] for Async).
*)type'aresult='avaluemonad(**
This type describes a result monad inside a project.
For example, if the concrete module is built on top of Lwt,
a value of type [unit result] will be translated as [(unit, error) Pervasives.result Lwt.t].
*)val(>>=):'aresult->('a->'bresult)->'bresult(**
This combinator is also called [bind].
It can be used to chain sequential operations with the current monad.
If the computation in the left failed, the operator will propagate the error,
skipping the function completely.
*)valbind:'aresult->('a->'bresult)->'bresult(**
Similar to {{!Sugar__S.Result.bind} S.Result.bind}
*)valbind_unless:'aresult->(error->'aresult)->'aresult(**
Similar to {{!Sugar__S.Result.bind_unless} S.Result.bind_unless}
*)valmap:'aresult->('a->'b)->'bresult(**
Similar to {{!Sugar__S.Result.map} S.Result.map}
*)valreturn:'a->'aresult(**
Similar to {{!Sugar__S.Result.return} S.Result.return}
*)valthrow:error->'aresult(**
Similar to {{!Sugar__S.Result.throw} S.Result.throw}
*)moduleInfix:sigval(>>|):'aresult->('a->'b)->'bresult(**
{{!Sugar.S.Result.Infix.(>>|)} More...}
*)val(<$>):('a->'b)->'aresult->'bresult(** Applicative combinator for map *)val(<*>):('a->'b)result->'aresult->'bresult(** Applicative combinator for parallel execution of function and operand *)val(>>>=):'amonad->('a->'bmonad)->'bmonad(** An alias for UserMonad.(>>=)
This combinator provides direct access to the monad used to create
this module. *)val(>---------):'aresult->(error->'aresult)->'aresult(**
"Broom" combinator
This is an alias for the function {!bind_unless}. It provides syntatic
sugar to create error handlers in a more intuitive way.
There's a secret message behind the form of this combinator.
It has the same number of characters sufficient for the whole block
in the next line. For example:
{[
let program1 () =
do_something ()
>---------
( fun e ->
return ()
)
let program2 () =
do_something ()
>---------
( function
e -> return ()
)
]}
*)val(>>):unitresult->'bresultLazy.t->'bresult(**
{{!Sugar.S.Result.Infix.(>>)} More...}
*)val(>>>):'aresult->'bresultLazy.t->'bresult(**
{{!Sugar.S.Result.Infix.(>>>)} More...}
*)endvalunwrap:'avaluemonad->'amonad(**
Unwraps the successful value as a normal value in the threading monad.
If the value is not successful, it will raise an [Invalid_arg] exception.
*)valunwrap_or:(error->'amonad)->'avaluemonad->'amonad(**
Unwraps the successful value as a value in the threading monad.
Different from [unwrap], you can assign an error handler to be
executed if the computation failed.
*)valexpect:'avaluemonad->string->'amonad(**
Extracts a successful value from an computation, or raises and [Invalid_arg]
exception with a customized error message.
*)end(**
This interface specifies an error handling layer for monadic computations.
Sugar value modules work with any monad.
{[
module MyMonad = struct
type 'a monad = 'a Lwt.t
let return = Lwt.return
let (>>=) = Lwt.bind
end
module MyResult = Sugar.Promise.Make (MyMonad) (MyError)
]}
*)moduletypeStrict_promise=sigincludePromise(**
Disable exception handling. Open this module with you don't want to catch exceptions.
*)moduleNoExceptions:Promisewithtypeerror:=errorandtype'amonad:='amonadend(**
Common definitions for the default result monad.
*)moduletypeResult_partials=sigtypeerror(** Error definition from your project *)type'aresult=('a,error)Result.result(** An alias for the result type in the stdlib *)valbind:'aresult->('a->'bresult)->'bresult(** Apply the binding only if the computation was successful.
You can use the operator {{!(>>=)} >>=} instead of this function for syntatic sugar *)valbind_unless:'aresult->(error->'aresult)->'aresult(** Apply the binding only if the computation failed.
Notice that an error handler must be provided, and this handler
must throw an error or provide an equivalent for the result type of the
previous computation.
You can use the operator {{!Infix.(>---------)} >---------} instead of this function for syntatic sugar *)valmap:'aresult->('a->'b)->'bresult(**
Apply a function to the result of a successful computation. This function
makes it ease to work with non error aware functions.
Example:
{[
open Sugar.Option
let twenty =
map (Some 10) (fun n -> n + n)
]}
You could also use the combinator {{!Infix.(>>|)} >>|} for syntatic sugar. *)valreturn:'a->'aresult(** Return a value in a successful computation.
This function should be used with its counterpart, [throw] *)valthrow:error->'aresult(**
Return an error as the result of a computation.
Like the [return] function, [throw] helps you hide the internals of your
result type and keep a clean code.
If you are still at the beginning of your project, and don't have your
errors defined yet, this function still is a great help. For example,
the code bellow have the same usage as the function [failwith], but is a lot
safer.
{[
module MyResult = Sugar.MakeResult (struct error = string end)
open MyResult
let run (): int result =
if true then
return 10
else
throw "something bad happend"
]}
You could also not describe your errors at all for some time, and
use the {!Sugar.Option} module to create error aware computations, like:
{[
open Sugar.Option
let run (): string result =
if true then
return "hello world"
else
throw ()
]} *)moduleInfix:sigval(>---------):'aresult->(error->'aresult)->'aresult(**
Combinator used to introduce an error handler block to "clean errors".
There's a secret message behind the form of this combinator.
It has the same number of characters sufficient for the whole block
in the next line. For example:
{[
let program1 () =
do_something ()
>---------
( fun e ->
return ()
)
let program2 () =
do_something ()
>---------
( function
e -> return ()
)
]}
So beyond the clean aesthetics similar to markdown, we are
implying that a developer should never handle errors in an open
anonymous function. *)val(>>|):'aresult->('a->'b)->'bresult(**
Combinator for map with semantic similar to bind
As its name sugests, this is an alias for the function {{!map} map}.
Its intended use is to help integrate with functions that are not error
aware.
For example, considere the function [let double x = x + x] in the code
fragments bellow:
{[
open Sugar.Option
let twenty =
match Some 10 with
| None -> None
| Some n -> Some (double n)
let using_bind_combinator =
Some 10
>>=
( fun n ->
return (double n)
)
let using_map_combinator =
Some 10
>>| double
]}
*)val(<$>):('a->'b)->'aresult->'bresultval(<*>):('a->'b)result->'aresult->'bresultval(>>>):'aresult->'bresultLazy.t->'bresult(**
Ignore operator.
This is a type of semicolon combinator that can be used to ignore any
result. It is supposed to be used with the keyword lazy as in [>>>lazy].
It can be used to replace the creation of an anonymous function to discard
the previous value, like:
{[
(* instead of writing *)
do_something ()
>>=
( fun _ ->
do_something_else ()
)
(* you can write *)
( do_something () ) >>>lazy
( do_something_else () )
]}
For more information, look at {!(>>)}.
*)val(>>):unitresult->'bresultLazy.t->'bresult(**
A sequential semicolon combinator.
This operator is supposed to be used with the keyword lazy, as [>>lazy].
To reduce cognitive load, it's interesting to treat [>>lazy] as one word.
To chain monadic expressions that are not related to each other.
Instead of writing code like:
{[
puts "hello"
>>=
( fun () ->
puts "monadic"
)
>>=
( fun () ->
puts "world"
)
]}
You can use these form:
{[
( puts "hello" ) >>lazy
( puts "monadic" ) >>lazy
( puts "world" )
]}
Or this alternative form that has exactly the structure as the example above:
{[
( puts "hello" )>>lazy(
puts "monadic" )>>lazy(
puts "world"
)
]}
Notice that the expression on the left must be of type [unit result]. If you wish to
ignore other type of [result]s, use {{!(>>>)} >>>lazy}
*)endval(>>=):'aresult->('a->'bresult)->'bresult(**
Bind combinator
If the computation in the left is successful, the operator will
Take the inner value and feed it to the function in the right. This is an
alias for the function [bind].
If the computation in the left failed, the operator will propagate the error,
skipping the function completely.
*)valunwrap:'aresult->'a(**
Unwraps the successful result as a normal value in the threading monad.
If the value is not successful, it will raise an Invalid_arg exception.
*)valunwrap_or:(error->'a)->'aresult->'a(**
Unwraps the successful result as a value in the threading monad.
Different from [unwrap], you can assign an error handler to be
executed if the computation failed. Example:
{[
let run () =
get_data ()
|> unwrap_or (fun _ -> "default")
]}
*)valexpect:'aresult->string->'a(**
Extracts a successful value from an computation, or raises and Invalid_arg
exception with the defined parameter.
*)end(**
The signature for the default result monad.
*)moduletypeResult=sigincludeResult_partials(**
Create a new result module based on the current one, but wrapped around a monad.
*)moduleFor:functor(UserMonad:Monad)->Promisewithtypeerror:=errorandtype'amonad:='aUserMonad.tend(**
The signature for a result monad that has some awareness about unexpected exceptions.
*)moduletypeStrict_result=sigincludeResult_partials(**
Create a new result module based on the current one, but wrapped around a monad.
*)moduleFor:functor(UserMonad:Params.Strict_monad)->Strict_promisewithtypeerror:=errorandtype'amonad:='aUserMonad.t(**
Disable exception handling
*)moduleNoExceptions:Resultwithtypeerror:=errorend