Define explicit map_ok as implementation of map
[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 [ return/1
13 , map/2 % map/2 is alias for map_ok/2
14 , map_ok/2
15 , map_error/2
16 , tag_error/2
17 , pipe/2
18 , lift_exn/1
19 , lift_exn/2
20 , lift_map_exn/3
21 ]).
22
23 -type exn_class() ::
24 error
25 | exit
26 | throw
27 .
28
29 -type exn_value(A) ::
30 {exn_class(), A}.
31
32 -type t(A, B) ::
33 {ok, A}
34 | {error, B}
35 .
36
37
38 -spec return(A) ->
39 {ok, A}.
40 return(X) ->
41 {ok, X}.
42
43 -spec map(t(A, Error), fun((A) -> (B))) ->
44 t(B, Error).
45 map({_, _}=T, F) ->
46 map_ok(T, F).
47
48 -spec map_ok(t(A, Error), fun((A) -> (B))) ->
49 t(B, Error).
50 map_ok({ok, X}, F) ->
51 {ok, F(X)};
52 map_ok({error, _}=Error, _) ->
53 Error.
54
55 -spec map_error(t(A, B), fun((B) -> (C))) ->
56 t(A, C).
57 map_error({ok, _}=Ok, _) ->
58 Ok;
59 map_error({error, Reason}, F) ->
60 {error, F(Reason)}.
61
62 -spec tag_error(t(A, Reason), Tag) ->
63 t(A, {Tag, Reason}).
64 tag_error({ok, _}=Ok, _) ->
65 Ok;
66 tag_error({error, Reason}, Tag) ->
67 {error, {Tag, Reason}}.
68
69 -spec pipe([F], X) ->
70 t(Ok, Error)
71 when X :: any()
72 , Ok :: any()
73 , Error :: any()
74 , F :: fun((X) -> t(Ok, Error))
75 .
76 pipe([], X) ->
77 {ok, X};
78 pipe([F|Fs], X) ->
79 case F(X)
80 of {error, _}=E -> E
81 ; {ok, Y} -> pipe(Fs, Y)
82 end.
83
84 -spec lift_exn(F) -> G
85 when F :: fun((A) -> B)
86 , G :: fun((A) -> t(B, exn_value(any())))
87 .
88 lift_exn(F) when is_function(F, 1) ->
89 ID = fun hope_fun:id/1,
90 lift_map_exn(F, ID, ID).
91
92 -spec lift_exn(F, ErrorTag) -> G
93 when F :: fun((A) -> B)
94 , G :: fun((A) -> t(B, {ErrorTag, exn_value(any())}))
95 .
96 lift_exn(F, ErrorTag) when is_function(F, 1) ->
97 ID = fun hope_fun:id/1,
98 Tag = fun (Reason) -> {ErrorTag, Reason} end,
99 lift_map_exn(F, ID, Tag).
100
101 -spec lift_map_exn(F, MapOk, MapError) -> G
102 when F :: fun((A) -> B)
103 , MapOk :: fun((B) -> C)
104 , MapError :: fun((exn_value(any())) -> Error)
105 , G :: fun((A) -> t(C, Error))
106 .
107 lift_map_exn(F, MapOk, MapError) when is_function(F, 1) ->
108 fun(X) ->
109 Result =
110 try
111 {ok, F(X)}
112 catch Class:Reason ->
113 {error, {Class, Reason}}
114 end,
115 % Applying maps separately as to not unintentionally catch an exception
116 % raised in a map.
117 case Result
118 of {ok , _}=Ok -> map_ok (Ok , MapOk)
119 ; {error, _}=Error -> map_error(Error, MapError)
120 end
121 end.
This page took 0.055141 seconds and 4 git commands to generate.