From b46db0bdc02fda5aa5b5dfac5fcc12c9948507da Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Thu, 7 Aug 2014 11:53:30 -0400 Subject: [PATCH] Add the Monoid type class --- example/monoid/plus.cpp | 17 ++++ example/monoid/zero.cpp | 17 ++++ include/boost/hana.hpp | 1 + include/boost/hana/detail/integral_fwd.hpp | 1 - include/boost/hana/detail/minimal/monoid.hpp | 68 +++++++++++++ include/boost/hana/foldable/mcd.hpp | 14 +-- include/boost/hana/integral.hpp | 30 ++++++ include/boost/hana/monoid.hpp | 17 ++++ include/boost/hana/monoid/laws.hpp | 41 ++++++++ include/boost/hana/monoid/mcd.hpp | 17 ++++ include/boost/hana/monoid/monoid.hpp | 100 +++++++++++++++++++ include/boost/hana/range.hpp | 3 +- test/integral/monoid.cpp | 24 +++++ test/list/instance/list/zip_with.cpp | 18 ++-- test/monoid/builtin_instance.cpp | 50 ++++++++++ test/monoid/laws.cpp | 28 ++++++ test/monoid/plus.cpp | 23 +++++ test/monoid/zero.cpp | 23 +++++ test/sandbox/detail/is_valid.cpp | 10 +- 19 files changed, 480 insertions(+), 22 deletions(-) create mode 100644 example/monoid/plus.cpp create mode 100644 example/monoid/zero.cpp create mode 100644 include/boost/hana/detail/minimal/monoid.hpp create mode 100644 include/boost/hana/monoid.hpp create mode 100644 include/boost/hana/monoid/laws.hpp create mode 100644 include/boost/hana/monoid/mcd.hpp create mode 100644 include/boost/hana/monoid/monoid.hpp create mode 100644 test/integral/monoid.cpp create mode 100644 test/monoid/builtin_instance.cpp create mode 100644 test/monoid/laws.cpp create mode 100644 test/monoid/plus.cpp create mode 100644 test/monoid/zero.cpp diff --git a/example/monoid/plus.cpp b/example/monoid/plus.cpp new file mode 100644 index 000000000..eb8bda854 --- /dev/null +++ b/example/monoid/plus.cpp @@ -0,0 +1,17 @@ +/* +@copyright Louis Dionne 2014 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#include +#include +using namespace boost::hana; + + +int main() { + //! [main] + BOOST_HANA_CONSTANT_ASSERT(plus(int_<3>, int_<5>) == int_<8>); + BOOST_HANA_CONSTEXPR_ASSERT(plus(1, 2) == 3); + //! [main] +} diff --git a/example/monoid/zero.cpp b/example/monoid/zero.cpp new file mode 100644 index 000000000..b1c321cd2 --- /dev/null +++ b/example/monoid/zero.cpp @@ -0,0 +1,17 @@ +/* +@copyright Louis Dionne 2014 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#include +#include +using namespace boost::hana; + + +int main() { + //! [main] + BOOST_HANA_CONSTANT_ASSERT(zero == int_<0>); + BOOST_HANA_CONSTEXPR_ASSERT(zero == 0l); + //! [main] +} diff --git a/include/boost/hana.hpp b/include/boost/hana.hpp index a6b81f92e..ea4991e83 100644 --- a/include/boost/hana.hpp +++ b/include/boost/hana.hpp @@ -1145,6 +1145,7 @@ I hope you enjoy using the library as much as I enjoyed writing it! #include #include #include +#include #include #include #include diff --git a/include/boost/hana/detail/integral_fwd.hpp b/include/boost/hana/detail/integral_fwd.hpp index 71ce5af89..626b0d970 100644 --- a/include/boost/hana/detail/integral_fwd.hpp +++ b/include/boost/hana/detail/integral_fwd.hpp @@ -85,7 +85,6 @@ namespace boost { namespace hana { // Arithmetic BOOST_HANA_INTEGRAL_UNARY_OP(+) BOOST_HANA_INTEGRAL_UNARY_OP(-) - BOOST_HANA_INTEGRAL_BINARY_OP(+) BOOST_HANA_INTEGRAL_BINARY_OP(-) BOOST_HANA_INTEGRAL_BINARY_OP(*) BOOST_HANA_INTEGRAL_BINARY_OP(/) diff --git a/include/boost/hana/detail/minimal/monoid.hpp b/include/boost/hana/detail/minimal/monoid.hpp new file mode 100644 index 000000000..67b50e80f --- /dev/null +++ b/include/boost/hana/detail/minimal/monoid.hpp @@ -0,0 +1,68 @@ +/*! +@file +Defines `boost::hana::detail::minimal::Monoid`. + +@copyright Louis Dionne 2014 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#ifndef BOOST_HANA_DETAIL_MINIMAL_MONOID_HPP +#define BOOST_HANA_DETAIL_MINIMAL_MONOID_HPP + +#include +#include + + +namespace boost { namespace hana { +namespace detail { namespace minimal { + template + struct Monoid { }; + + template + struct monoid_impl { + int value; + using hana_datatype = Monoid; + }; + + template + struct make_monoid_impl { + constexpr auto operator()(int i) const + { return monoid_impl{i}; } + }; + + template + constexpr make_monoid_impl monoid{}; +}} // end namespace detail::minimal + +// Provided for convenience in the unit tests. +template +struct Comparable::instance< + detail::minimal::Monoid, detail::minimal::Monoid +> : Comparable::equal_mcd +{ + static constexpr bool equal_impl( + detail::minimal::monoid_impl x, + detail::minimal::monoid_impl y + ) + { return x.value == y.value; } +}; + +template <> +struct Monoid::instance< + detail::minimal::Monoid, + detail::minimal::Monoid +> : Monoid::mcd +{ + static constexpr auto plus_impl( + detail::minimal::monoid_impl x, + detail::minimal::monoid_impl y + ) + { return detail::minimal::monoid(x.value + y.value); } + + static constexpr auto zero_impl() + { return detail::minimal::monoid(0); } +}; +}} // end namespace boost::hana + +#endif // !BOOST_HANA_DETAIL_MINIMAL_MONOID_HPP diff --git a/include/boost/hana/foldable/mcd.hpp b/include/boost/hana/foldable/mcd.hpp index 74e95c005..78c2d4480 100644 --- a/include/boost/hana/foldable/mcd.hpp +++ b/include/boost/hana/foldable/mcd.hpp @@ -15,6 +15,7 @@ Distributed under the Boost Software License, Version 1.0. #include #include #include +#include #include @@ -74,11 +75,12 @@ namespace boost { namespace hana { }); } - template - static constexpr auto sum_impl(Foldable_ foldable) { - return foldl(foldable, int_<0>, [](auto x, auto y) { - return x + y; - }); + //! @todo + //! The base case can't be `int_<0>`, it should be the identity of + //! a given `Monoid`? + template + static constexpr auto sum_impl(Xs xs) { + return foldl(xs, int_<0>, plus); } template @@ -91,7 +93,7 @@ namespace boost { namespace hana { template static constexpr auto count_impl(Foldable_ foldable, Pred pred) { return foldl(foldable, size_t<0>, [=](auto counter, auto x) { - return if_(pred(x), counter + size_t<1>, counter); + return if_(pred(x), plus(counter, size_t<1>), counter); }); } diff --git a/include/boost/hana/integral.hpp b/include/boost/hana/integral.hpp index 0b87e0862..9f3332049 100644 --- a/include/boost/hana/integral.hpp +++ b/include/boost/hana/integral.hpp @@ -16,6 +16,7 @@ Distributed under the Boost Software License, Version 1.0. #include #include #include +#include #include @@ -106,6 +107,35 @@ namespace boost { namespace hana { { return bool_; } }; + //! Additive `Monoid` of `Integral`s. + template <> + struct Monoid::instance : Monoid::mcd { + template + static constexpr auto plus_impl(X x, Y y) + { return integral; } + + static constexpr auto zero_impl() + { return int_<0>; } + }; + + template + struct Monoid::instance{}>> + : Monoid::mcd + { + template + static constexpr auto plus_impl(X x, Y y) + { return value(x) + y; } + }; + + template + struct Monoid::instance{}>> + : Monoid::mcd + { + template + static constexpr auto plus_impl(X x, Y y) + { return x + value(y); } + }; + namespace integral_detail { constexpr int to_int(char c) { return static_cast(c) - 48; } diff --git a/include/boost/hana/monoid.hpp b/include/boost/hana/monoid.hpp new file mode 100644 index 000000000..7c8ae32b9 --- /dev/null +++ b/include/boost/hana/monoid.hpp @@ -0,0 +1,17 @@ +/*! +@file +Defines `boost::hana::Monoid`. + +@copyright Louis Dionne 2014 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#ifndef BOOST_HANA_MONOID_HPP +#define BOOST_HANA_MONOID_HPP + +#include +#include +#include + +#endif // !BOOST_HANA_MONOID_HPP diff --git a/include/boost/hana/monoid/laws.hpp b/include/boost/hana/monoid/laws.hpp new file mode 100644 index 000000000..2a7a3750e --- /dev/null +++ b/include/boost/hana/monoid/laws.hpp @@ -0,0 +1,41 @@ +/*! +@file +Defines `boost::hana::Monoid::laws`. + +@copyright Louis Dionne 2014 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#ifndef BOOST_HANA_MONOID_LAWS_HPP +#define BOOST_HANA_MONOID_LAWS_HPP + +#include + +#include +#include +#include +#include + + +namespace boost { namespace hana { + struct Monoid::laws { + template + static constexpr auto check(MonoidObjects objs) { + return all(objs, [=](auto x) { + return all(objs, [=](auto y) { + return all(objs, [=](auto z) { + using M = datatype_t; + return and_( + equal(plus(zero, x), x), + equal(plus(x, zero), x), + equal(plus(x, plus(y, z)), plus(plus(x, y), z)) + ); + }); + }); + }); + } + }; +}} // end namespace boost::hana + +#endif // !BOOST_HANA_MONOID_LAWS_HPP diff --git a/include/boost/hana/monoid/mcd.hpp b/include/boost/hana/monoid/mcd.hpp new file mode 100644 index 000000000..44b2bcb6b --- /dev/null +++ b/include/boost/hana/monoid/mcd.hpp @@ -0,0 +1,17 @@ +/*! +@file +Defines `boost::hana::Monoid::mcd`. + +@copyright Louis Dionne 2014 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#ifndef BOOST_HANA_MONOID_MCD_HPP +#define BOOST_HANA_MONOID_MCD_HPP + +// The mcd is defined in the forward declaration header because it is +// required by the instance for builtins. +#include + +#endif // !BOOST_HANA_MONOID_MCD_HPP diff --git a/include/boost/hana/monoid/monoid.hpp b/include/boost/hana/monoid/monoid.hpp new file mode 100644 index 000000000..e03ee462d --- /dev/null +++ b/include/boost/hana/monoid/monoid.hpp @@ -0,0 +1,100 @@ +/*! +@file +Forward declares `boost::hana::Monoid`. + +@copyright Louis Dionne 2014 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#ifndef BOOST_HANA_MONOID_MONOID_HPP +#define BOOST_HANA_MONOID_MONOID_HPP + +#include +#include + + +namespace boost { namespace hana { + //! @ingroup group-typeclasses + //! The `Monoid` type class is used for data types with an associative + //! binary operation that has an identity. + //! + //! The method names refer to the monoid of numbers under addition, but + //! there are many other instances such as sequences under concatenation. + //! Some datatypes can be viewed as a monoid in more than one way, e.g. + //! both addition and multiplication on numbers. + //! + //! ### Laws + //! For all objects `x`, `y` and `z` whose data type `M` is a `Monoid`, + //! the following laws must be satisfied: + //! @code + //! plus(zero, x) == x // left zero + //! plus(x, zero) == x // right zero + //! plus(x, plus(y, z)) == plus(plus(x, y), z) // associativity + //! @endcode + struct Monoid { + BOOST_HANA_BINARY_TYPECLASS(Monoid); + + //! Minimal complete definition : `zero` and `plus` + struct mcd { }; + + struct laws; + }; + + //! Associative operation on a `Monoid`. + //! @relates Monoid + //! + //! ### Example + //! @snippet example/monoid/plus.cpp main + BOOST_HANA_CONSTEXPR_LAMBDA auto plus = [](auto x, auto y) { + return Monoid::instance< + datatype_t, datatype_t + >::plus_impl(x, y); + }; + + //! Identity of `plus`. + //! @relates Monoid + //! + //! Since `Monoid` is a binary type class and `zero` is a nullary method, + //! `zero` is dispatched to the type class instance for `M` and `M`, + //! i.e. `Monoid::instance`. + //! + //! @tparam M + //! The data type (a `Monoid`) of the returned identity. + //! + //! ### Example + //! @snippet example/monoid/zero.cpp main + template + constexpr auto zero = Monoid::instance::zero_impl(); + + //! Instance of `Monoid` for objects with numeric data types. + //! + //! Any two objects whose data types can be added with the usual + //! `operator+` and for which a valid conversion from `int` exists + //! (for both data types) naturally form an additive `Monoid`, with + //! `0` being the identity and the usual `operator+` being the + //! associative operation. + template + struct Monoid::instance(0), + static_cast(0), + *(X*)0 + *(Y*)0 + ))>> : Monoid::mcd { + static constexpr auto plus_impl(X x, Y y) + { return x + y; } + + // Will never be used with two different `X` and `Y` anyway. + static constexpr auto zero_impl() + { return static_cast(0); } + }; + + namespace operators { + //! Equivalent to `plus`. + //! @relates boost::hana::Monoid + template + constexpr auto operator+(C1 c1, C2 c2) + { return plus(c1, c2); } + } +}} // end namespace boost::hana + +#endif // !BOOST_HANA_MONOID_MONOID_HPP diff --git a/include/boost/hana/range.hpp b/include/boost/hana/range.hpp index d28337d97..f42a54b75 100644 --- a/include/boost/hana/range.hpp +++ b/include/boost/hana/range.hpp @@ -16,6 +16,7 @@ Distributed under the Boost Software License, Version 1.0. #include #include #include +#include #include @@ -114,7 +115,7 @@ namespace boost { namespace hana { template static constexpr auto drop_impl(N n, R r) { auto size = r.to - r.from; - return range(if_(greater(n, size), r.to, r.from + n), r.to); + return range(if_(greater(n, size), r.to, plus(r.from, n)), r.to); } }; diff --git a/test/integral/monoid.cpp b/test/integral/monoid.cpp new file mode 100644 index 000000000..eaceed963 --- /dev/null +++ b/test/integral/monoid.cpp @@ -0,0 +1,24 @@ +/* +@copyright Louis Dionne 2014 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#include + +#include +#include +#include +using namespace boost::hana; + + +int main() { + BOOST_HANA_CONSTANT_ASSERT(zero == int_<0>); + BOOST_HANA_CONSTANT_ASSERT(plus(int_<3>, int_<4>) == int_<3 + 4>); + BOOST_HANA_CONSTEXPR_ASSERT(plus(int_<3>, 4) == 3 + 4); + BOOST_HANA_CONSTEXPR_ASSERT(plus(3, int_<4>) == 3 + 4); + + BOOST_HANA_CONSTEXPR_ASSERT(Monoid::laws::check(list( + int_<1>, short_<2>, long_<3>, ullong<4>, 5, 6ull + ))); +} diff --git a/test/list/instance/list/zip_with.cpp b/test/list/instance/list/zip_with.cpp index 42ce5791b..f4f75cc79 100644 --- a/test/list/instance/list/zip_with.cpp +++ b/test/list/instance/list/zip_with.cpp @@ -11,7 +11,7 @@ Distributed under the Boost Software License, Version 1.0. using namespace boost::hana; -BOOST_HANA_CONSTEXPR_LAMBDA auto plus = [](auto x, auto y) { return x + y; }; +BOOST_HANA_CONSTEXPR_LAMBDA auto add = [](auto x, auto y) { return x + y; }; BOOST_HANA_CONSTEXPR_LAMBDA auto inc = [](auto x) { return x + 1; }; int main() { @@ -21,12 +21,12 @@ int main() { BOOST_HANA_CONSTEXPR_ASSERT(zip_with(inc, list(0, 1, 2)) == list(1, 2, 3)); BOOST_HANA_CONSTANT_ASSERT(zip_with(inc, list(), list()) == list()); - BOOST_HANA_CONSTANT_ASSERT(zip_with(plus, list()) == list()); - BOOST_HANA_CONSTANT_ASSERT(zip_with(plus, list(), list()) == list()); - BOOST_HANA_CONSTANT_ASSERT(zip_with(plus, list(1), list()) == list()); - BOOST_HANA_CONSTANT_ASSERT(zip_with(plus, list(), list(3)) == list()); - BOOST_HANA_CONSTEXPR_ASSERT(zip_with(plus, list(1), list(3)) == list(1 + 3)); - BOOST_HANA_CONSTEXPR_ASSERT(zip_with(plus, list(1, 2), list(3, 4)) == list(1 + 3, 2 + 4)); - BOOST_HANA_CONSTEXPR_ASSERT(zip_with(plus, list(1, 2, 3, 4), list(5, 6, 7)) == list(1 + 5, 2 + 6, 3 + 7)); - BOOST_HANA_CONSTANT_ASSERT(zip_with(plus, list(), list(), list()) == list()); + BOOST_HANA_CONSTANT_ASSERT(zip_with(add, list()) == list()); + BOOST_HANA_CONSTANT_ASSERT(zip_with(add, list(), list()) == list()); + BOOST_HANA_CONSTANT_ASSERT(zip_with(add, list(1), list()) == list()); + BOOST_HANA_CONSTANT_ASSERT(zip_with(add, list(), list(3)) == list()); + BOOST_HANA_CONSTEXPR_ASSERT(zip_with(add, list(1), list(3)) == list(1 + 3)); + BOOST_HANA_CONSTEXPR_ASSERT(zip_with(add, list(1, 2), list(3, 4)) == list(1 + 3, 2 + 4)); + BOOST_HANA_CONSTEXPR_ASSERT(zip_with(add, list(1, 2, 3, 4), list(5, 6, 7)) == list(1 + 5, 2 + 6, 3 + 7)); + BOOST_HANA_CONSTANT_ASSERT(zip_with(add, list(), list(), list()) == list()); } diff --git a/test/monoid/builtin_instance.cpp b/test/monoid/builtin_instance.cpp new file mode 100644 index 000000000..be1dd9450 --- /dev/null +++ b/test/monoid/builtin_instance.cpp @@ -0,0 +1,50 @@ +/* +@copyright Louis Dionne 2014 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#include + +#include +#include +using namespace boost::hana; + + +struct integer { + int value; + constexpr explicit integer(int i) : value{i} { } +}; + +struct integer2 { + int value; + constexpr explicit integer2(int i) : value{i} { } +}; + +constexpr auto operator+(integer a, integer b) +{ return integer{a.value + b.value}; } + +constexpr auto operator+(integer2 a, integer b) +{ return integer{a.value + b.value}; } + +constexpr auto operator+(integer a, integer2 b) +{ return integer{a.value + b.value}; } + +constexpr auto operator+(integer2 a, integer2 b) +{ return integer2{a.value + b.value}; } + +using Integer = datatype_t; +using Integer2 = datatype_t; + +int main() { + // same type + BOOST_HANA_CONSTEXPR_ASSERT(zero.value == 0); + BOOST_HANA_CONSTEXPR_ASSERT(plus(integer{3}, integer{5}).value == 3 + 5); + + BOOST_HANA_CONSTEXPR_ASSERT(zero.value == 0); + BOOST_HANA_CONSTEXPR_ASSERT(plus(integer2{3}, integer2{5}).value == 3 + 5); + + // mixed types + BOOST_HANA_CONSTEXPR_ASSERT(plus(integer{3}, integer2{5}).value == 3 + 5); + BOOST_HANA_CONSTEXPR_ASSERT(plus(integer2{3}, integer{5}).value == 3 + 5); +} diff --git a/test/monoid/laws.cpp b/test/monoid/laws.cpp new file mode 100644 index 000000000..06c750986 --- /dev/null +++ b/test/monoid/laws.cpp @@ -0,0 +1,28 @@ +/* +@copyright Louis Dionne 2014 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#include +#include + +#include +#include +#include +using namespace boost::hana; + + +template +void test() { + constexpr auto mono = detail::minimal::monoid; + BOOST_HANA_CONSTEXPR_ASSERT( + Monoid::laws::check( + list(mono(0), mono(1), mono(2), mono(3)) + ) + ); +} + +int main() { + test(); +} diff --git a/test/monoid/plus.cpp b/test/monoid/plus.cpp new file mode 100644 index 000000000..a48f71f32 --- /dev/null +++ b/test/monoid/plus.cpp @@ -0,0 +1,23 @@ +/* +@copyright Louis Dionne 2014 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#include + +#include +#include +using namespace boost::hana; + + +template +void test() { + constexpr auto mono = detail::minimal::monoid; + BOOST_HANA_CONSTEXPR_ASSERT(plus(mono(2), mono(3)) == mono(5)); + BOOST_HANA_CONSTEXPR_ASSERT(mono(2) + mono(3) == mono(5)); +} + +int main() { + test(); +} diff --git a/test/monoid/zero.cpp b/test/monoid/zero.cpp new file mode 100644 index 000000000..dd086bca4 --- /dev/null +++ b/test/monoid/zero.cpp @@ -0,0 +1,23 @@ +/* +@copyright Louis Dionne 2014 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#include + +#include +#include +using namespace boost::hana; + + +template +void test() { + constexpr auto mono = detail::minimal::monoid; + using M = detail::minimal::Monoid; + BOOST_HANA_CONSTEXPR_ASSERT(zero == mono(0)); +} + +int main() { + test(); +} diff --git a/test/sandbox/detail/is_valid.cpp b/test/sandbox/detail/is_valid.cpp index d34feb51f..50a07f45f 100644 --- a/test/sandbox/detail/is_valid.cpp +++ b/test/sandbox/detail/is_valid.cpp @@ -12,13 +12,13 @@ using namespace boost::hana; constexpr struct { } invalid{}; -auto plus = [](auto x, auto y) -> decltype(x + y) { }; -BOOST_HANA_CONSTANT_ASSERT(detail::is_valid(plus)(1, 2)); -BOOST_HANA_CONSTANT_ASSERT(!detail::is_valid(plus)(1, invalid)); -BOOST_HANA_CONSTANT_ASSERT(!detail::is_valid(plus)(invalid, 1)); +auto add = [](auto x, auto y) -> decltype(x + y) { }; +BOOST_HANA_CONSTANT_ASSERT(detail::is_valid(add)(1, 2)); +BOOST_HANA_CONSTANT_ASSERT(!detail::is_valid(add)(1, invalid)); +BOOST_HANA_CONSTANT_ASSERT(!detail::is_valid(add)(invalid, 1)); auto f = []() -> void { }; BOOST_HANA_CONSTANT_ASSERT(detail::is_valid(f)()); BOOST_HANA_CONSTANT_ASSERT(!detail::is_valid(f)(1)); -int main() { (void)plus; (void)f; } +int main() { (void)add; (void)f; }