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