Upgrade proper to current HEAD, to build on OTP 18.X
[hope.git] / src / hope_result.erl
1 -module(hope_result).
2
3 -behavior(hope_gen_monad).
4
5 -export_type(
6 [ t/2
7 , exn_class/0
8 , exn_value/1
9 ]).
10
11 -export(
12 % Generic monad interface
13 [ return/1
14 , map/2 % map/2 is alias for map_ok/2
15 , pipe/2
16
17 % Specific to hope_result:t()
18 , map_ok/2
19 , map_error/2
20 , tag_error/2
21 , lift_exn/1
22 , lift_exn/2
23 , lift_map_exn/3
24 ]).
25
26 -type exn_class() ::
27 error
28 | exit
29 | throw
30 .
31
32 -type exn_value(A) ::
33 {exn_class(), A}.
34
35 -type t(A, B) ::
36 {ok, A}
37 | {error, B}
38 .
39
40
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).
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) ->
54 {ok, F(X)};
55 map_ok({error, _}=Error, _) ->
56 Error.
57
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
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 .
79 pipe([], X) ->
80 {ok, X};
81 pipe([F|Fs], X) ->
82 case F(X)
83 of {error, _}=E -> E
84 ; {ok, Y} -> pipe(Fs, Y)
85 end.
86
87 -spec lift_exn(F) -> G
88 when F :: fun((A) -> B)
89 , G :: fun((A) -> t(B, exn_value(any())))
90 .
91 lift_exn(F) when is_function(F, 1) ->
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).
103
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))
109 .
110 lift_map_exn(F, MapOk, MapError) when is_function(F, 1) ->
111 fun(X) ->
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
121 of {ok , _}=Ok -> map_ok (Ok , MapOk)
122 ; {error, _}=Error -> map_error(Error, MapError)
123 end
124 end.
This page took 0.073783 seconds and 4 git commands to generate.