Current section

Files

Jump to
tql src tql_either.erl
Raw

src/tql_either.erl

-module(tql_either).
%% API exports
-export([ fold/2
, sequence/1
, is_ok/1
, is_error/1
, from_bool/3
]).
-type either(Term, Error) :: {ok, Term} | {error, Error}.
-export_types([ either/2
]).
%%%---------------------------------------------------------------------
%%% API
%%%---------------------------------------------------------------------
%% @doc Fold over a term with a list of functions.
%%
%% The first function in the list is called with the initial value, and
%% expected to produce either an {@type either(NewValue, Error)} or a bare
%% `NewValue'. If the produced value is an `{ok, NewValue}' tuple, the
%% next function is called with `NewValue' as its input. If the produced
%% value is a bare `NewValue', the next function is called with that
%% value as its input. If the produced value is an error, processing
%% stops and returns that `{error, E}' tuple.
%%
%% Note that this function will always produce a tuple, so if the final
%% function produces a bare value, this will be wrapped in a tuple, too.
-spec fold(term(), [fun((Term) -> Return)]) -> either(Term, Error) when
Term :: term(),
Error :: term(),
Return :: either(Term, Error) | Term.
fold(Init, Fs) when is_list(Fs) ->
Result = lists:foldl(fun fold_handle/2, Init, Fs),
fold_create(Result).
%% @doc Combine a list of result tuples.
%%
%% This will result in either an `{error, E}' if any of the supplied
%% tuples is an error, or `{ok, [V]}' with all the ok-values sequenced
%% into a list.
-spec sequence([either(Term, Error)]) -> either([Term], Error) when
Term :: term(),
Error :: term().
sequence(Eithers) ->
lists:foldr(fun
({ok, Success}, {ok, Successes}) ->
{ok, [Success | Successes]};
({ok, _Success}, {error, Failure}) ->
{error, Failure};
({error, Failure}, _) ->
{error, Failure}
end, {ok, []}, Eithers).
%% @doc Check whether the given result tuple is of the form `{ok, V}'.
-spec is_ok(either(Term, Error)) -> boolean() when
Term :: term(),
Error :: term().
is_ok({ok, _}) ->
true;
is_ok({error, _}) ->
false.
%% @doc Check whether the given result tuple is of the form `{error, E}'.
-spec is_error(either(Term, Error)) -> boolean() when
Term :: term(),
Error :: term().
is_error({ok, _}) ->
false;
is_error({error, _}) ->
true.
%% @doc Convert a boolean to the form `{ok, Term}` or `{error, Error}`.
-spec from_bool(Term, Error, boolean()) -> either(Term, Error) when
Term :: term(),
Error :: term().
from_bool(Term, _Error, true) ->
{ok, Term};
from_bool(_Term, Error, false) ->
{error, Error}.
%%%-----------------------------------------------------------------------------
%%% Internal functions
%%%-----------------------------------------------------------------------------
-spec fold_handle(fun((Term) -> Return), Return) -> Return when
Term :: term(),
Error :: term(),
Return :: either(Term, Error) | Term.
fold_handle(F, {ok, Value}) ->
F(Value);
fold_handle(_, {error, Reason}) ->
{error, Reason};
fold_handle(F, Value) ->
F(Value).
-spec fold_create(either(Term, Error) | Term) -> either(Term, Error) when
Term :: term(),
Error :: term().
fold_create({error, Reason}) ->
{error, Reason};
fold_create({ok, Value}) ->
{ok, Value};
fold_create(Value) ->
{ok, Value}.
%% Local variables:
%% mode: erlang
%% erlang-indent-level: 2
%% indent-tabs-mode: nil
%% fill-column: 72
%% coding: latin-1
%% End: