1 // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors.
2 // https://github.com/Dobiasd/FunctionalPlus
3 // Distributed under the Boost Software License, Version 1.0.
4 // (See accompanying file LICENSE_1_0.txt or copy at
5 //  http://www.boost.org/LICENSE_1_0.txt)
6 
7 #pragma once
8 
9 #include <fplus/function_traits.hpp>
10 #include <fplus/maybe.hpp>
11 
12 #include <fplus/internal/asserts/functions.hpp>
13 #include <fplus/internal/composition.hpp>
14 #include <fplus/internal/invoke.hpp>
15 
16 #include <cassert>
17 #include <functional>
18 #include <memory>
19 
20 namespace fplus
21 {
22 
23 template <typename Ok, typename Error>
24 class result;
25 
26 template <typename Ok, typename Error>
27 result<Ok, Error> ok(const Ok& val);
28 
29 template <typename Ok, typename Error>
30 result<Ok, Error> error(const Error& error);
31 
32 // Can hold a value of type Ok or an error of type Error.
33 template <typename Ok, typename Error>
34 class result
35 {
36 public:
is_ok() const37     bool is_ok() const { return static_cast<bool>(ptr_ok_); }
is_error() const38     bool is_error() const { return static_cast<bool>(ptr_error_); }
unsafe_get_ok() const39     const Ok& unsafe_get_ok() const {
40         check_either_or_invariant(); assert(is_ok()); return *ptr_ok_;
41     }
unsafe_get_error() const42     const Error& unsafe_get_error() const {
43         check_either_or_invariant(); assert(is_error()); return *ptr_error_;
44     }
45     typedef Ok ok_t;
46     typedef Error error_t;
47 
result(const result<Ok,Error> & other)48     result(const result<Ok, Error>& other) :
49         ptr_ok_(other.is_ok() ? ptr_ok(new Ok(other.unsafe_get_ok())) : ptr_ok()),
50         ptr_error_(other.is_error() ? ptr_error(new Error(other.unsafe_get_error())) : ptr_error())
51     {
52         check_either_or_invariant();
53     }
operator =(const result<Ok,Error> & other)54     result<Ok, Error>& operator = (const result<Ok, Error>& other)
55     {
56         ptr_ok_ = other.is_ok() ? ptr_ok(new Ok(other.unsafe_get_ok())) : ptr_ok();
57         ptr_error_ = other.is_error() ? ptr_error(new Error(other.unsafe_get_error())) : ptr_error();
58         return *this;
59     }
60 private:
check_either_or_invariant() const61     void check_either_or_invariant() const
62     {
63         assert(is_ok() != is_error());
64     }
result()65     result() : ptr_ok_(ptr_ok()), ptr_error_(ptr_error()) {}
66     typedef std::unique_ptr<Ok> ptr_ok;
67     typedef std::unique_ptr<Error> ptr_error;
68     friend result<Ok, Error> ok<Ok, Error>(const Ok& ok);
69     friend result<Ok, Error> error<Ok, Error>(const Error& error);
70     ptr_ok ptr_ok_;
71     ptr_error ptr_error_;
72 };
73 
74 // API search type: is_ok : Result a b -> Bool
75 // fwd bind count: 0
76 // Is not error?
77 template <typename Ok, typename Error>
is_ok(const result<Ok,Error> & result)78 bool is_ok(const result<Ok, Error>& result)
79 {
80     return result.is_ok();
81 }
82 
83 // API search type: is_error : Result a b -> Bool
84 // fwd bind count: 0
85 // Is not OK?
86 template <typename Ok, typename Error>
is_error(const result<Ok,Error> & result)87 bool is_error(const result<Ok, Error>& result)
88 {
89     return !is_ok(result);
90 }
91 
92 // API search type: unsafe_get_ok : Result a b -> a
93 // fwd bind count: 0
94 // Crashes if result is error!
95 template <typename Ok, typename Error>
96 Ok unsafe_get_ok(const result<Ok, Error>& result)
97 {
98     return result.unsafe_get_ok();
99 }
100 
101 // API search type: unsafe_get_error : Result a b -> b
102 // fwd bind count: 0
103 // Crashes if result is ok!
104 template <typename Ok, typename Error>
unsafe_get_error(const result<Ok,Error> & result)105 Error unsafe_get_error(const result<Ok, Error>& result)
106 {
107     return result.unsafe_get_error();
108 }
109 
110 // API search type: ok_with_default : (a, Result a b) -> a
111 // fwd bind count: 1
112 // Get the value from a result or the default in case it is error.
113 template <typename Ok, typename Error>
114 Ok ok_with_default(const Ok& defaultValue, const result<Ok, Error>& result)
115 {
116     if (is_ok(result))
117         return unsafe_get_ok(result);
118     return defaultValue;
119 }
120 
121 // API search type: ok : a -> Result a b
122 // fwd bind count: 0
123 // Wrap a value in a result as a Ok.
124 template <typename Ok, typename Error>
ok(const Ok & val)125 result<Ok, Error> ok(const Ok& val)
126 {
127     result<Ok, Error> x;
128     x.ptr_ok_.reset(new Ok(val));
129     return x;
130 }
131 
132 // API search type: error : b -> Result a b
133 // fwd bind count: 0
134 // Construct an error of a certain result type.
135 template <typename Ok, typename Error>
error(const Error & error)136 result<Ok, Error> error(const Error& error)
137 {
138     result<Ok, Error> x;
139     x.ptr_error_.reset(new Error(error));
140     return x;
141 }
142 
143 // API search type: to_maybe : Result a b -> Maybe a
144 // fwd bind count: 0
145 // Convert ok to just, error to nothing.
146 template <typename Ok, typename Error>
to_maybe(const result<Ok,Error> & result)147 maybe<Ok> to_maybe(const result<Ok, Error>& result)
148 {
149     if (is_ok(result))
150         return just<Ok>(unsafe_get_ok(result));
151     else
152         return nothing<Ok>();
153 }
154 
155 // API search type: from_maybe : (b, Maybe a) -> Result a b
156 // fwd bind count: 1
157 // Convert just to ok, nothing to error.
158 template <typename Error, typename Ok>
from_maybe(const Error & err,const maybe<Ok> & maybe)159 result<Ok, Error> from_maybe(const Error& err, const maybe<Ok>& maybe)
160 {
161     if (is_just(maybe))
162         return ok<Ok, Error>(unsafe_get_just(maybe));
163     else
164         return error<Ok, Error>(err);
165 }
166 
167 // API search type: throw_on_error : (e, Result a b) -> a
168 // fwd bind count: 1
169 // Throws the given exception in case of error.
170 // Return ok value if ok.
171 template <typename E, typename Ok, typename Error>
172 Ok throw_on_error(const E& e, const result<Ok, Error>& result)
173 {
174     if (is_error(result))
175         throw e;
176     return unsafe_get_ok(result);
177 }
178 
179 // API search type: throw_type_on_error : Result a b -> a
180 // Throws the given exception type constructed with error value if error.
181 // Return ok value if ok.
182 template <typename E, typename Ok, typename Error>
183 Ok throw_type_on_error(const result<Ok, Error>& result)
184 {
185     if (is_error(result))
186         throw E(unsafe_get_error(result));
187     return unsafe_get_ok(result);
188 }
189 
190 // True if ok values are the same or if errors are the same.
191 template <typename Ok, typename Error>
operator ==(const result<Ok,Error> & x,const result<Ok,Error> & y)192 bool operator == (const result<Ok, Error>& x, const result<Ok, Error>& y)
193 {
194     if (is_ok(x) && is_ok(y))
195         return unsafe_get_ok(x) == unsafe_get_ok(y);
196     if (is_error(x) && is_error(y))
197         return unsafe_get_error(x) == unsafe_get_error(y);
198     return false;
199 }
200 
201 // False if ok values are the same or if both errors are the same.
202 template <typename Ok, typename Error>
operator !=(const result<Ok,Error> & x,const result<Ok,Error> & y)203 bool operator != (const result<Ok, Error>& x, const result<Ok, Error>& y)
204 {
205     return !(x == y);
206 }
207 
208 // API search type: lift_result : ((a -> b), Result a c) -> Result b c
209 // fwd bind count: 1
210 // Lifts a function into the result functor.
211 // A function that for example was able to convert and int into a string,
212 // now can convert a result<int, Err> into a result<string, Err>.
213 // An error stays the same error, regardless of the conversion.
214 template <typename Error, typename F, typename A>
lift_result(F f,const result<A,Error> & r)215 auto lift_result(F f, const result<A, Error>& r)
216 {
217     internal::trigger_static_asserts<internal::unary_function_tag, F, A>();
218 
219     using B = std::decay_t<internal::invoke_result_t<F, A>>;
220 
221     if (is_ok(r))
222         return ok<B, Error>(internal::invoke(f, unsafe_get_ok(r)));
223     return error<B, Error>(unsafe_get_error(r));
224 }
225 
226 // API search type: lift_result_both : ((a -> c), (b -> d), Result a b) -> Result c d
227 // fwd bind count: 2
228 // Lifts two functions into the result functor.
229 template <typename F, typename G, typename A, typename B>
lift_result_both(F f,G g,const result<A,B> & r)230 auto lift_result_both(F f, G g, const result<A, B>& r)
231 {
232     internal::trigger_static_asserts<internal::unary_function_tag, F, A>();
233     internal::trigger_static_asserts<internal::unary_function_tag, G, B>();
234 
235     using C = std::decay_t<internal::invoke_result_t<F, A>>;
236     using D = std::decay_t<internal::invoke_result_t<G, B>>;
237 
238     if (is_ok(r))
239         return ok<C, D>(internal::invoke(f, unsafe_get_ok(r)));
240     return error<C, D>(internal::invoke(g, unsafe_get_error(r)));
241 }
242 
243 // API search type: unify_result : ((a -> c), (b -> c), Result a b) -> c
244 // fwd bind count: 2
245 // Extracts the value (Ok or Error) from a Result
246 // as defined by the two given functions.
247 template <typename F, typename G, typename A, typename B>
unify_result(F f,G g,const result<A,B> & r)248 auto unify_result(F f, G g, const result<A, B>& r)
249 {
250     internal::trigger_static_asserts<internal::unary_function_tag, F, A>();
251     internal::trigger_static_asserts<internal::unary_function_tag, G, B>();
252     static_assert(std::is_same<internal::invoke_result_t<F, A>,
253                                internal::invoke_result_t<G, B>>::value,
254                   "Both functions must return the same type.");
255     if (is_ok(r))
256         return internal::invoke(f, unsafe_get_ok(r));
257     return internal::invoke(g, unsafe_get_error(r));
258 }
259 
260 // API search type: join_result : Result (Result a b) b -> Result a b
261 // Flattens a nested result.
262 // join_result(Ok Ok x) == Ok x
263 // join_result(Ok Error e) == Error e
264 // join_result(Error e) == Error e
265 template <typename OK, typename Error>
join_result(const result<result<OK,Error>,Error> & r)266 result<OK, Error> join_result(const result<result<OK, Error>, Error>& r)
267 {
268     if (is_ok(r))
269         return unsafe_get_ok(r);
270     else
271         return error<OK, Error>(r.unsafe_get_error());
272 }
273 
274 // API search type: and_then_result : ((a -> Result c b), (Result a b)) -> Result c b
275 // fwd bind count: 1
276 // Monadic bind.
277 // Returns the error if the result is an error.
278 // Otherwise return the result of applying
279 // the function to the ok value of the result.
280 template <typename Ok, typename Error, typename F>
and_then_result(F f,const result<Ok,Error> & r)281 auto and_then_result(F f, const result<Ok, Error>& r)
282 {
283     internal::trigger_static_asserts<internal::unary_function_tag, F, Ok>();
284 
285     using FOut = std::decay_t<internal::invoke_result_t<F, Ok>>;
286     static_assert(std::is_same<Error, typename FOut::error_t>::value,
287                   "Error type must stay the same.");
288     if (is_ok(r))
289         return internal::invoke(f, unsafe_get_ok(r));
290     else
291         return error<typename FOut::ok_t, typename FOut::error_t>(r.unsafe_get_error());
292 }
293 
294 // API search type: compose_result : ((a -> Result b c), (b -> Result d c)) -> (a -> Result d c)
295 // Left-to-right Kleisli composition of monads.
296 // It is possible to compose a variadic number of callables.
297 // The first callable can take a variadic number of parameters.
298 template <typename... Callables>
compose_result(Callables &&...callables)299 auto compose_result(Callables&&... callables)
300 {
301     auto bind_result = [](auto f, auto g) {
302         return [f = std::move(f), g = std::move(g)](auto&&... args)
303         {
304             internal::trigger_static_asserts<internal::check_arity_tag,
305                                                  decltype(f),
306                                                  decltype(args)...>();
307 #if defined(_MSC_VER) && _MSC_VER >= 1920 // in VS2019, compilation with /permissive- breaks with 'using' syntax below
308             struct FOut : std::decay_t<
309                 internal::invoke_result_t<decltype(f), decltype(args)...>> {};
310 #else
311             using FOut = std::decay_t<
312                 internal::invoke_result_t<decltype(f), decltype(args)...>>;
313 #endif
314 
315             internal::trigger_static_asserts<internal::unary_function_tag,
316                                                  decltype(g),
317                                                  typename FOut::ok_t>();
318 #if defined(_MSC_VER) && _MSC_VER >= 1920 // in VS2019, compilation with /permissive- breaks with 'using' syntax below
319             struct GOut : std::decay_t<
320                 internal::invoke_result_t<decltype(g), typename FOut::ok_t>> {};
321 #else
322             using GOut = std::decay_t<
323                 internal::invoke_result_t<decltype(g), typename FOut::ok_t>>;
324 #endif
325             static_assert(std::is_same<typename FOut::error_t,
326                                        typename GOut::error_t>::value,
327                           "Error type must stay the same.");
328 
329             auto resultB =
330                 internal::invoke(f, std::forward<decltype(args)>(args)...);
331             if (is_ok(resultB))
332                 return internal::invoke(g, unsafe_get_ok(resultB));
333             return error<typename GOut::ok_t, typename GOut::error_t>(
334                 unsafe_get_error(resultB));
335         };
336     };
337     return internal::compose_binary_lift(bind_result,
338                                        std::forward<Callables>(callables)...);
339 }
340 } // namespace fplus
341