Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/fplus/curry_instances.autogenerated_defines
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ fplus_curry_define_fn_1(just_with_default)
fplus_curry_define_fn_1(throw_on_nothing)
fplus_curry_define_fn_0(just)
fplus_curry_define_fn_1(as_just_if)
fplus_curry_define_fn_1(just_if)
fplus_curry_define_fn_0(maybe_to_seq)
fplus_curry_define_fn_0(singleton_seq_as_maybe)
fplus_curry_define_fn_1(lift_maybe)
Expand Down
2 changes: 2 additions & 0 deletions include/fplus/fwd_instances.autogenerated_defines
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ fplus_fwd_define_fn_1(just_with_default)
fplus_fwd_define_fn_1(throw_on_nothing)
fplus_fwd_define_fn_0(just)
fplus_fwd_define_fn_1(as_just_if)
fplus_fwd_define_fn_1(just_if)
fplus_fwd_define_fn_0(maybe_to_seq)
fplus_fwd_define_fn_0(singleton_seq_as_maybe)
fplus_fwd_define_fn_1(lift_maybe)
Expand Down Expand Up @@ -491,6 +492,7 @@ fplus_fwd_flip_define_fn_1(xor_bools)
fplus_fwd_flip_define_fn_1(just_with_default)
fplus_fwd_flip_define_fn_1(throw_on_nothing)
fplus_fwd_flip_define_fn_1(as_just_if)
fplus_fwd_flip_define_fn_1(just_if)
fplus_fwd_flip_define_fn_1(lift_maybe)
fplus_fwd_flip_define_fn_1(and_then_maybe)
fplus_fwd_flip_define_fn_1(elem_at_idx)
Expand Down
225 changes: 155 additions & 70 deletions include/fplus/maybe.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,27 @@ namespace fplus
{

// Can hold a value of type T or nothing.
template <typename T>
class maybe;

namespace internal
{
template <typename>
struct is_maybe : std::false_type
{
};

template <typename T>
struct is_maybe<maybe<T>> : std::true_type
{
};
}

template <typename T>
class maybe
{
public:
using value_type = T;
bool is_just() const { return is_present_; }
bool is_nothing() const { return !is_just(); }
const T& unsafe_get_just() const
Expand Down Expand Up @@ -95,6 +112,124 @@ class maybe
}
return *this;
}

T get_with_default(const T& defaultValue) const
{
if (is_just())
return unsafe_get_just();
return defaultValue;
}

template <typename E>
T get_or_throw(const E& e) const
{
if (is_nothing())
throw e;
return unsafe_get_just();
}

template <typename Pred>
maybe<T> just_if(Pred pred) const
{
if (is_just() && pred(unsafe_get_just()))
return *this;
return maybe<T>{};
}

template <typename ContainerOut = std::vector<T>>
ContainerOut to_seq() const
{
if (is_just())
return ContainerOut(1, unsafe_get_just());
return {};
}

template <typename F>
auto lift(F f) const
{
internal::trigger_static_asserts<internal::check_arity_tag, F, T>();

using B = std::decay_t<internal::invoke_result_t<F, T>>;
if (is_just())
return maybe<B>(internal::invoke(f, unsafe_get_just()));
return maybe<B>{};
}

template <typename Default, typename F>
auto lift_def(const Default& def, F f) const
{
internal::trigger_static_asserts<internal::check_arity_tag, F, T>();

using B = std::decay_t<internal::invoke_result_t<F, T>>;
static_assert(
std::is_convertible<Default, B>::value,
"Default value must be convertible to Function's return type");
if (is_just())
return B(internal::invoke(f, unsafe_get_just()));
return B(def);
}

template <typename F, typename B>
auto lift_2(F f, const maybe<B>& m_b) const
{
internal::trigger_static_asserts<internal::check_arity_tag, F, T, B>();

using FOut = std::decay_t<internal::invoke_result_t<F, T, B>>;
if (is_just() && m_b.is_just())
{
return maybe<FOut>(
internal::invoke(f, unsafe_get_just(), m_b.unsafe_get_just()));
}
return maybe<FOut>{};
}

template <typename F, typename B, typename Default>
auto lift_2_def(const Default& def,
F f,
const maybe<B>& m_b) const
{
internal::trigger_static_asserts<internal::check_arity_tag, F, T, B>();

using C = std::decay_t<internal::invoke_result_t<F, T, B>>;
static_assert(
std::is_convertible<Default, C>::value,
"Default value must be convertible to Function's return type");
if (is_just() && m_b.is_just())
return C(internal::invoke(f, unsafe_get_just(), m_b.unsafe_get_just()));
return C(def);
}

auto join() const
{
static_assert(
internal::is_maybe<T>::value,
"Cannot join when value type is not also a maybe"
);
if (is_just())
return unsafe_get_just();
else
return maybe<typename T::value_type>{};
}

auto flatten() const
{
return join();
}

template <typename F>
auto and_then(F f) const
{
internal::trigger_static_asserts<internal::check_arity_tag, F, T>();
using FOut = std::decay_t<internal::invoke_result_t<F, T>>;
static_assert(internal::is_maybe<FOut>::value,
"Function must return a maybe<> type");
if (is_just())
return internal::invoke(f, unsafe_get_just());
else
return maybe<typename FOut::type>{};
}


private:
void destruct_content()
{
Expand All @@ -108,19 +243,6 @@ class maybe
typename std::aligned_storage<sizeof(T), alignof(T)>::type value_;
};

namespace internal
{
template <typename>
struct is_maybe : std::false_type
{
};

template <typename T>
struct is_maybe<maybe<T>> : std::true_type
{
};
}

// API search type: is_just : Maybe a -> Bool
// fwd bind count: 0
// Is not nothing?
Expand Down Expand Up @@ -154,9 +276,7 @@ T unsafe_get_just(const maybe<T>& maybe)
template <typename T>
T just_with_default(const T& defaultValue, const maybe<T>& maybe)
{
if (is_just(maybe))
return unsafe_get_just(maybe);
return defaultValue;
return maybe.get_with_default(defaultValue);
}

// API search type: throw_on_nothing : (e, Maybe a) -> a
Expand All @@ -165,9 +285,7 @@ T just_with_default(const T& defaultValue, const maybe<T>& maybe)
template <typename E, typename T>
T throw_on_nothing(const E& e, const maybe<T>& maybe)
{
if (is_nothing(maybe))
throw e;
return unsafe_get_just(maybe);
return maybe.get_or_throw(e);
}

// API search type: just : a -> Maybe a
Expand All @@ -193,6 +311,16 @@ maybe<T> as_just_if(Pred pred, const T& val)
return {};
}

// API search type: just_if : ((a -> bool), Maybe a) -> Maybe a
// fwd bind count: 1
// Retain the Just value fulfilling a predicate.
// A nothing is returned if predicate is false or the maybe is already nothing.
template <typename Pred, typename T>
maybe<T> just_if(Pred pred, const maybe<T>& maybe)
{
return maybe.just_if(pred);
}

// API search type: maybe_to_seq : Maybe a -> [a]
// fwd bind count: 0
// Converts a maybe to a sequence.
Expand All @@ -201,9 +329,7 @@ maybe<T> as_just_if(Pred pred, const T& val)
template <typename T, typename ContainerOut = std::vector<T>>
ContainerOut maybe_to_seq(const maybe<T>& maybe)
{
if (is_just(maybe))
return ContainerOut(1, unsafe_get_just(maybe));
return {};
return maybe.to_seq();
}

// API search type: singleton_seq_as_maybe : [a] -> Maybe a
Expand Down Expand Up @@ -254,12 +380,7 @@ bool operator != (const maybe<T>& x, const maybe<T>& y)
template <typename F, typename A>
auto lift_maybe(F f, const maybe<A>& m)
{
internal::trigger_static_asserts<internal::check_arity_tag, F, A>();

using B = std::decay_t<internal::invoke_result_t<F, A>>;
if (is_just(m))
return just<B>(internal::invoke(f, unsafe_get_just(m)));
return nothing<B>();
return m.lift(f);
}

// API search type: lift_maybe_def : (b, (a -> b), Maybe a) -> b
Expand All @@ -272,15 +393,7 @@ auto lift_maybe(F f, const maybe<A>& m)
template <typename F, typename A, typename Default>
auto lift_maybe_def(const Default& def, F f, const maybe<A>& m)
{
internal::trigger_static_asserts<internal::check_arity_tag, F, A>();

using B = std::decay_t<internal::invoke_result_t<F, A>>;
static_assert(
std::is_convertible<Default, B>::value,
"Default value must be convertible to Function's return type");
if (is_just(m))
return B(internal::invoke(f, unsafe_get_just(m)));
return B(def);
return m.lift_def(def, f);
}

// API search type: lift_maybe_2 : (((a, b) -> c), Maybe a, Maybe b) -> Maybe c
Expand All @@ -291,15 +404,7 @@ auto lift_maybe_def(const Default& def, F f, const maybe<A>& m)
template <typename F, typename A, typename B>
auto lift_maybe_2(F f, const maybe<A>& m_a, const maybe<B>& m_b)
{
internal::trigger_static_asserts<internal::check_arity_tag, F, A, B>();

using FOut = std::decay_t<internal::invoke_result_t<F, A, B>>;
if (is_just(m_a) && is_just(m_b))
{
return just<FOut>(
internal::invoke(f, unsafe_get_just(m_a), unsafe_get_just(m_b)));
}
return nothing<FOut>();
return m_a.lift_2(f, m_b);
}

// API search type: lift_maybe_2_def : (c, ((a, b) -> c), Maybe a, Maybe b) -> c
Expand All @@ -316,15 +421,7 @@ auto lift_maybe_2_def(const Default& def,
const maybe<A>& m_a,
const maybe<B>& m_b)
{
internal::trigger_static_asserts<internal::check_arity_tag, F, A, B>();

using C = std::decay_t<internal::invoke_result_t<F, A, B>>;
static_assert(
std::is_convertible<Default, C>::value,
"Default value must be convertible to Function's return type");
if (is_just(m_a) && is_just(m_b))
return C(internal::invoke(f, unsafe_get_just(m_a), unsafe_get_just(m_b)));
return C(def);
return m_a.lift_2_def(def, f, m_b);
}

// API search type: join_maybe : Maybe Maybe a -> Maybe a
Expand All @@ -335,10 +432,7 @@ auto lift_maybe_2_def(const Default& def,
template <typename A>
maybe<A> join_maybe(const maybe<maybe<A>>& m)
{
if (is_just(m))
return unsafe_get_just(m);
else
return nothing<A>();
return m.join();
}

// API search type: and_then_maybe : ((a -> Maybe b), (Maybe a)) -> Maybe b
Expand All @@ -350,14 +444,7 @@ maybe<A> join_maybe(const maybe<maybe<A>>& m)
template <typename T, typename F>
auto and_then_maybe(F f, const maybe<T>& m)
{
internal::trigger_static_asserts<internal::check_arity_tag, F, T>();
using FOut = std::decay_t<internal::invoke_result_t<F, T>>;
static_assert(internal::is_maybe<FOut>::value,
"Function must return a maybe<> type");
if (is_just(m))
return internal::invoke(f, unsafe_get_just(m));
else
return nothing<typename FOut::type>();
return m.and_then(f);
}

// API search type: compose_maybe : ((a -> Maybe b), (b -> Maybe c)) -> (a -> Maybe c)
Expand Down Expand Up @@ -402,9 +489,7 @@ auto compose_maybe(Callables&&... callables)
template <typename T>
maybe<T> flatten_maybe(const maybe<maybe<T>>& maybe_maybe)
{
if (is_nothing(maybe_maybe))
return nothing<T>();
return unsafe_get_just(maybe_maybe);
return maybe_maybe.flatten();
}

} // namespace fplus
Loading