Commit | Line | Data |
---|---|---|
6731749b SK |
1 | -module(hope_result). |
2 | ||
b69220d7 | 3 | -behavior(hope_gen_monad). |
2a40de4f | 4 | |
3b156801 SK |
5 | -export_type( |
6 | [ t/2 | |
64617423 SK |
7 | , exn_class/0 |
8 | , exn_value/1 | |
3b156801 | 9 | ]). |
6731749b | 10 | |
3b156801 | 11 | -export( |
1c003d17 | 12 | % Generic monad interface |
4af0774b | 13 | [ return/1 |
1c003d17 SK |
14 | , map/2 % map/2 is alias for map_ok/2 |
15 | , pipe/2 | |
16 | ||
17 | % Specific to hope_result:t() | |
fd4da0e0 | 18 | , map_ok/2 |
a80ca0b2 SK |
19 | , map_error/2 |
20 | , tag_error/2 | |
8fc25ea1 | 21 | , lift_exn/1 |
b79afea0 | 22 | , lift_exn/2 |
64617423 | 23 | , lift_map_exn/3 |
3b156801 | 24 | ]). |
6731749b | 25 | |
64617423 SK |
26 | -type exn_class() :: |
27 | error | |
28 | | exit | |
29 | | throw | |
30 | . | |
31 | ||
32 | -type exn_value(A) :: | |
33 | {exn_class(), A}. | |
6731749b | 34 | |
3b156801 SK |
35 | -type t(A, B) :: |
36 | {ok, A} | |
37 | | {error, B} | |
38 | . | |
6731749b SK |
39 | |
40 | ||
4af0774b SK |
41 | -spec return(A) -> |
42 | {ok, A}. | |
43 | return(X) -> | |
44 | {ok, X}. | |
45 | ||
46 | -spec map(t(A, Error), fun((A) -> (B))) -> | |
47 | t(B, Error). | |
fd4da0e0 SK |
48 | map({_, _}=T, F) -> |
49 | map_ok(T, F). | |
50 | ||
51 | -spec map_ok(t(A, Error), fun((A) -> (B))) -> | |
52 | t(B, Error). | |
53 | map_ok({ok, X}, F) -> | |
4af0774b | 54 | {ok, F(X)}; |
fd4da0e0 | 55 | map_ok({error, _}=Error, _) -> |
4af0774b SK |
56 | Error. |
57 | ||
a80ca0b2 SK |
58 | -spec map_error(t(A, B), fun((B) -> (C))) -> |
59 | t(A, C). | |
60 | map_error({ok, _}=Ok, _) -> | |
61 | Ok; | |
62 | map_error({error, Reason}, F) -> | |
63 | {error, F(Reason)}. | |
64 | ||
65 | -spec tag_error(t(A, Reason), Tag) -> | |
66 | t(A, {Tag, Reason}). | |
67 | tag_error({ok, _}=Ok, _) -> | |
68 | Ok; | |
69 | tag_error({error, Reason}, Tag) -> | |
70 | {error, {Tag, Reason}}. | |
71 | ||
ed9905af SK |
72 | -spec pipe([F], X) -> |
73 | t(Ok, Error) | |
74 | when X :: any() | |
75 | , Ok :: any() | |
76 | , Error :: any() | |
77 | , F :: fun((X) -> t(Ok, Error)) | |
78 | . | |
3efbdbc9 SK |
79 | pipe([], X) -> |
80 | {ok, X}; | |
6731749b SK |
81 | pipe([F|Fs], X) -> |
82 | case F(X) | |
83 | of {error, _}=E -> E | |
84 | ; {ok, Y} -> pipe(Fs, Y) | |
85 | end. | |
8fc25ea1 SK |
86 | |
87 | -spec lift_exn(F) -> G | |
64617423 SK |
88 | when F :: fun((A) -> B) |
89 | , G :: fun((A) -> t(B, exn_value(any()))) | |
8fc25ea1 SK |
90 | . |
91 | lift_exn(F) when is_function(F, 1) -> | |
64617423 SK |
92 | ID = fun hope_fun:id/1, |
93 | lift_map_exn(F, ID, ID). | |
94 | ||
95 | -spec lift_exn(F, ErrorTag) -> G | |
96 | when F :: fun((A) -> B) | |
97 | , G :: fun((A) -> t(B, {ErrorTag, exn_value(any())})) | |
98 | . | |
99 | lift_exn(F, ErrorTag) when is_function(F, 1) -> | |
100 | ID = fun hope_fun:id/1, | |
101 | Tag = fun (Reason) -> {ErrorTag, Reason} end, | |
102 | lift_map_exn(F, ID, Tag). | |
b79afea0 | 103 | |
64617423 SK |
104 | -spec lift_map_exn(F, MapOk, MapError) -> G |
105 | when F :: fun((A) -> B) | |
106 | , MapOk :: fun((B) -> C) | |
107 | , MapError :: fun((exn_value(any())) -> Error) | |
108 | , G :: fun((A) -> t(C, Error)) | |
b79afea0 | 109 | . |
64617423 | 110 | lift_map_exn(F, MapOk, MapError) when is_function(F, 1) -> |
b79afea0 | 111 | fun(X) -> |
64617423 SK |
112 | Result = |
113 | try | |
114 | {ok, F(X)} | |
115 | catch Class:Reason -> | |
116 | {error, {Class, Reason}} | |
117 | end, | |
118 | % Applying maps separately as to not unintentionally catch an exception | |
119 | % raised in a map. | |
120 | case Result | |
fd4da0e0 | 121 | of {ok , _}=Ok -> map_ok (Ok , MapOk) |
64617423 | 122 | ; {error, _}=Error -> map_error(Error, MapError) |
b79afea0 SK |
123 | end |
124 | end. |