beamai_result.erl
1 %%%------------------------------------------------------------------- 2 %%% @doc 错误处理的 Result 单子 3 %%% 4 %%% 提供用于链式组合可能失败的计算的单子操作。 5 %%% 所有函数都使用 {ok, Value} | {error, Reason} 元组格式。 6 %%% 7 %%% @end 8 %%%------------------------------------------------------------------- 9 -module(beamai_result). 10 11 -export([ok/1, error/1, is_ok/1, is_error/1]). 12 -export([map/2, flat_map/2, bind/2]). 13 -export([pipe/2, pipe_while/2]). 14 -export([unwrap/1, unwrap_or/2, unwrap_error/1]). 15 -export([from_boolean/2, from_maybe/2]). 16 -export([collect/1, partition/1]). 17 -export([tap/2, tap_error/2]). 18 19 -type result(T) :: {ok, T} | {error, term()}. 20 -type result(T, E) :: {ok, T} | {error, E}. 21 22 -export_type([result/1, result/2]). 23 24 %%==================================================================== 25 %% 构造函数 26 %%==================================================================== 27 28 -spec ok(T) -> {ok, T}. 29 ok(Value) -> {ok, Value}. 30 31 -spec error(E) -> {error, E}. 32 error(Reason) -> {error, Reason}. 33 34 %%==================================================================== 35 %% 谓词函数 36 %%==================================================================== 37 38 -spec is_ok(result(term())) -> boolean(). 39 is_ok({ok, _}) -> true; 40 is_ok(_) -> false. 41 42 -spec is_error(result(term())) -> boolean(). 43 is_error({error, _}) -> true; 44 is_error(_) -> false. 45 46 %%==================================================================== 47 %% 转换函数 48 %%==================================================================== 49 50 %% @doc 如果是 ok 则应用函数,否则透传错误 51 -spec map(result(A), fun((A) -> B)) -> result(B). 52 map({ok, Value}, Fun) -> {ok, Fun(Value)}; 53 map({error, _} = E, _Fun) -> E. 54 55 %% @doc 应用返回 result 的函数,并展平结果 56 -spec flat_map(result(A), fun((A) -> result(B))) -> result(B). 57 flat_map({ok, Value}, Fun) -> Fun(Value); 58 flat_map({error, _} = E, _Fun) -> E. 59 60 %% @doc flat_map 的别名(Haskell 命名风格) 61 -spec bind(result(A), fun((A) -> result(B))) -> result(B). 62 bind(Result, Fun) -> flat_map(Result, Fun). 63 64 %%==================================================================== 65 %% 管道操作 66 %%==================================================================== 67 68 %% @doc 将值通过函数列表传递,遇到第一个错误时停止 69 -spec pipe(A, [fun((A) -> result(B)) | fun((A) -> B)]) -> result(B). 70 pipe(Value, []) -> 71 {ok, Value}; 72 pipe(Value, [Fun | Rest]) -> 73 case apply_fun(Fun, Value) of 74 {ok, NewValue} -> pipe(NewValue, Rest); 75 {error, _} = E -> E 76 end. 77 78 %% @doc 当谓词返回 true 时继续传递 79 -spec pipe_while(A, [{fun((A) -> boolean()), fun((A) -> result(A))}]) -> result(A). 80 pipe_while(Value, []) -> 81 {ok, Value}; 82 pipe_while(Value, [{Pred, Fun} | Rest]) -> 83 case Pred(Value) of 84 true -> 85 case apply_fun(Fun, Value) of 86 {ok, NewValue} -> pipe_while(NewValue, Rest); 87 {error, _} = E -> E 88 end; 89 false -> 90 {ok, Value} 91 end. 92 93 %%==================================================================== 94 %% 解包操作 95 %%==================================================================== 96 97 %% @doc 获取值,如果是错误则抛出异常 98 -spec unwrap(result(T)) -> T. 99 unwrap({ok, Value}) -> Value; 100 unwrap({error, Reason}) -> erlang:error({unwrap_error, Reason}). 101 102 %% @doc 获取值,如果是错误则返回默认值 103 -spec unwrap_or(result(T), T) -> T. 104 unwrap_or({ok, Value}, _Default) -> Value; 105 unwrap_or({error, _}, Default) -> Default. 106 107 %% @doc 获取错误原因 108 -spec unwrap_error(result(term(), E)) -> E. 109 unwrap_error({error, Reason}) -> Reason; 110 unwrap_error({ok, _}) -> erlang:error(not_an_error). 111 112 %%==================================================================== 113 %% 类型转换 114 %%==================================================================== 115 116 %% @doc 将布尔值转换为 result 117 -spec from_boolean(boolean(), E) -> result(ok, E). 118 from_boolean(true, _Error) -> {ok, ok}; 119 from_boolean(false, Error) -> {error, Error}. 120 121 %% @doc 将可能为 undefined 的值转换为 result 122 -spec from_maybe(T | undefined, E) -> result(T, E). 123 from_maybe(undefined, Error) -> {error, Error}; 124 from_maybe(Value, _Error) -> {ok, Value}. 125 126 %%==================================================================== 127 %% 集合操作 128 %%==================================================================== 129 130 %% @doc 将 result 列表收集为列表的 result 131 -spec collect([result(T)]) -> result([T]). 132 collect(Results) -> 133 collect(Results, []). 134 135 collect([], Acc) -> 136 {ok, lists:reverse(Acc)}; 137 collect([{ok, V} | Rest], Acc) -> 138 collect(Rest, [V | Acc]); 139 collect([{error, _} = E | _], _Acc) -> 140 E. 141 142 %% @doc 将结果列表分区为成功和错误两部分 143 -spec partition([result(T, E)]) -> {[T], [E]}. 144 partition(Results) -> 145 partition(Results, [], []). 146 147 partition([], Oks, Errors) -> 148 {lists:reverse(Oks), lists:reverse(Errors)}; 149 partition([{ok, V} | Rest], Oks, Errors) -> 150 partition(Rest, [V | Oks], Errors); 151 partition([{error, E} | Rest], Oks, Errors) -> 152 partition(Rest, Oks, [E | Errors]). 153 154 %%==================================================================== 155 %% 副作用操作 156 %%==================================================================== 157 158 %% @doc 对 ok 值执行副作用函数,返回原始结果 159 -spec tap(result(T), fun((T) -> term())) -> result(T). 160 tap({ok, Value} = R, Fun) -> 161 Fun(Value), 162 R; 163 tap({error, _} = E, _Fun) -> 164 E. 165 166 %% @doc 对错误执行副作用函数,返回原始结果 167 -spec tap_error(result(T, E), fun((E) -> term())) -> result(T, E). 168 tap_error({error, Reason} = E, Fun) -> 169 Fun(Reason), 170 E; 171 tap_error({ok, _} = R, _Fun) -> 172 R. 173 174 %%==================================================================== 175 %% 内部函数 176 %%==================================================================== 177 178 apply_fun(Fun, Value) -> 179 case Fun(Value) of 180 {ok, _} = R -> R; 181 {error, _} = E -> E; 182 Other -> {ok, Other} 183 end.