Skip to content

Commit

Permalink
Improve tests for optional applicative
Browse files Browse the repository at this point in the history
  • Loading branch information
vt4a2h committed Jan 5, 2024
1 parent 980d3f5 commit 4df0e5c
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 14 deletions.
10 changes: 6 additions & 4 deletions src/include/fl/utils/ap_optional.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ constexpr bool is_optional<std::optional<T>> = true;
template<class T>
concept IsOptional = is_optional<std::remove_cvref_t<T>>;

template<class T>
concept NotOptional = !is_optional<std::remove_cvref_t<T>>;

template<class V>
constexpr auto pure(V &&v)
{
Expand All @@ -40,17 +43,16 @@ constexpr auto pure(V &&v)

} // namespace details

template <class F, class ...Args>
template <class F, details::NotOptional ...Args>
constexpr auto ap(F &&f, Args &&...args) noexcept(noexcept(std::invoke(std::forward<F>(f), std::forward<Args>(args)...)))
requires (std::is_invocable_v<F, Args...>)
requires std::is_invocable_v<F, Args...>
{
return std::make_optional(std::invoke(std::forward<F>(f), std::forward<Args>(args)...));
}

template <class F, details::IsOptional ...Args>
constexpr auto ap(F &&f, Args &&...args) noexcept(noexcept(ap(std::forward<F>(f), *std::forward<Args>(args)...)))
-> decltype(ap(std::forward<F>(f), *std::forward<Args>(args)...))
requires (!std::is_invocable_v<F, Args...>)
{
if ((... && args)) {
return ap(std::forward<F>(f), *std::forward<Args>(args)...);
Expand All @@ -63,7 +65,7 @@ template <class F, class ...Args>
constexpr auto ap(F &&f, Args &&...args) noexcept(noexcept(ap(std::forward<F>(f), *details::pure(args)...)))
requires (!std::is_invocable_v<F, Args...> && !(... && details::IsOptional<Args>))
{
return ap(std::forward<F>(f), *details::pure(args)...);
return ap(std::forward<F>(f), details::pure(args)...);
}

} // namespace fl
48 changes: 38 additions & 10 deletions test/test_utils_ap_optional.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

#include <fl/utils/ap_optional.hpp>

#include <fmt/std.h>

TEST_CASE("Applicative for optional")
{
SECTION("Regular args and function") {
Expand All @@ -24,25 +26,51 @@ TEST_CASE("Applicative for optional")

SECTION("Optional non-invocable") {
const auto add = [](int a, const std::string &b) { return a + std::stoi(b); };
const auto a = std::make_optional<int>(1);
const auto b = std::make_optional<std::string>("2");

REQUIRE(fl::ap(add, a, b) == 3);
const auto [arg1, arg2, expected] = GENERATE(table<std::optional<int>, std::optional<std::string>, std::optional<int>>({
{std::make_optional<int>(1), std::make_optional<std::string>("2"), std::make_optional<int>(3)},
{std::make_optional<int>(1), std::nullopt, std::nullopt},
{std::nullopt, std::make_optional<std::string>("2"), std::nullopt},
}));

const auto actual = fl::ap(add, arg1, arg2);

INFO(fmt::format("{} + {} = {}\n\texpected {}", arg1, arg2, actual, expected));
REQUIRE(actual == expected);
}

SECTION("Optional invocable") {
const auto add = [](std::optional<int> a, const std::optional<std::string> &b) { return *a + std::stoi(*b); };
const auto a = std::make_optional<int>(1);
const auto b = std::make_optional<std::string>("2");
const auto add = [](std::optional<int> a, const std::optional<std::string> &b) -> std::optional<int> {
if (!a || ! b) {
return std::nullopt;
} else {
return *a + std::stoi(*b);
}
};

REQUIRE(fl::ap(add, a, b) == 3);
const auto [arg1, arg2, expected] = GENERATE(table<std::optional<int>, std::optional<std::string>, std::optional<int>>({
{std::make_optional<int>(1), std::make_optional<std::string>("2"), std::make_optional<int>(3)},
{std::make_optional<int>(1), std::nullopt, std::nullopt},
{std::nullopt, std::make_optional<std::string>("2"), std::nullopt},
}));

const auto actual = fl::ap(add, arg1, arg2);

INFO(fmt::format("{} + {} = {}\n\texpected {}", arg1, arg2, actual, expected));
REQUIRE(actual == expected);
}

SECTION("Optional partial ap") {
const auto add = [](int a, const std::string &b) { return a + std::stoi(b); };
const auto a = std::make_optional<int>(1);
const auto b = std::string{"2"};

REQUIRE(fl::ap(add, a, b) == 3);
const auto [arg1, arg2, expected] = GENERATE(table<std::optional<int>, std::string, std::optional<int>>({
{std::make_optional<int>(1), "2", std::make_optional<int>(3)},
{std::nullopt, "2", std::nullopt},
}));

const auto actual = fl::ap(add, arg1, arg2);

INFO(fmt::format("{} + {} = {}\n\texpected {}", arg1, arg2, actual, expected));
REQUIRE(actual == expected);
}
}

0 comments on commit 4df0e5c

Please sign in to comment.