mirror of
https://github.com/BlackMATov/kari.hpp.git
synced 2025-12-16 16:07:08 +07:00
142
README.md
142
README.md
@@ -52,14 +52,16 @@ target_link_libraries(your_project_target kari.hpp)
|
|||||||
### Basic currying
|
### Basic currying
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
|
using namespace kari_hpp;
|
||||||
|
|
||||||
auto foo = [](int v1, int v2, int v3) {
|
auto foo = [](int v1, int v2, int v3) {
|
||||||
return v1 + v2 + v3;
|
return v1 + v2 + v3;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto c0 = kari::curry(foo); // currying of `foo` function
|
auto c0 = curry(foo); // currying of `foo` function
|
||||||
auto c1 = c0(10); // apply to first argument
|
auto c1 = c0(10); // apply to first argument
|
||||||
auto c2 = c1(20); // apply to second argument
|
auto c2 = c1(20); // apply to second argument
|
||||||
auto rr = c2(12); // apply to third argument and call the `foo` function
|
auto rr = c2(12); // apply to third argument and call the `foo` function
|
||||||
|
|
||||||
// output: 42
|
// output: 42
|
||||||
std::cout << rr << std::endl;
|
std::cout << rr << std::endl;
|
||||||
@@ -68,13 +70,14 @@ std::cout << rr << std::endl;
|
|||||||
### Partial application of curried functions
|
### Partial application of curried functions
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
auto foo = [](int v1, int v2, int v3, int v4) {
|
using namespace kari_hpp;
|
||||||
|
|
||||||
|
curry_t c0 = [](int v1, int v2, int v3, int v4) {
|
||||||
return v1 + v2 + v3 + v4;
|
return v1 + v2 + v3 + v4;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto c0 = kari::curry(foo); // currying
|
auto c1 = c0(15, 20); // partial application of two arguments
|
||||||
auto c1 = c0(15, 20); // partial application of two arguments
|
auto rr = c1(2, 5); // partial application and call `(15,20,2,5)`
|
||||||
auto rr = c1(2, 5); // partial application and call `foo(15,20,2,5)`
|
|
||||||
|
|
||||||
// output: 42
|
// output: 42
|
||||||
std::cout << rr << std::endl;
|
std::cout << rr << std::endl;
|
||||||
@@ -83,17 +86,19 @@ std::cout << rr << std::endl;
|
|||||||
### Calling nested curried functions
|
### Calling nested curried functions
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
auto boo = [](int v1, int v2) {
|
using namespace kari_hpp;
|
||||||
return v1 + v2;
|
|
||||||
|
curry_t boo = [](int a, int b) {
|
||||||
|
return a + b;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto foo = [boo](int v1, int v2) {
|
curry_t foo = [boo](int a, int b) {
|
||||||
return kari::curry(boo, v1 + v2);
|
return boo(a + b);
|
||||||
};
|
};
|
||||||
|
|
||||||
auto c0 = kari::curry(foo)(38,3,1);
|
auto c0 = foo(38,3,1);
|
||||||
auto c1 = kari::curry(foo)(38,3)(1);
|
auto c1 = foo(38,3)(1);
|
||||||
auto c2 = kari::curry(foo)(38)(3,1);
|
auto c2 = foo(38)(3,1);
|
||||||
|
|
||||||
// output: 42,42,42
|
// output: 42,42,42
|
||||||
std::cout << c0 << "," << c1 << "," << c2 << std::endl;
|
std::cout << c0 << "," << c1 << "," << c2 << std::endl;
|
||||||
@@ -102,19 +107,21 @@ std::cout << c0 << "," << c1 << "," << c2 << std::endl;
|
|||||||
### Binding member functions and member objects
|
### Binding member functions and member objects
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
struct Foo {
|
using namespace kari_hpp;
|
||||||
|
|
||||||
|
struct foo_t {
|
||||||
int v = 40;
|
int v = 40;
|
||||||
int addV(int add) {
|
int add_v(int add) {
|
||||||
v += add;
|
v += add;
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
} foo;
|
} foo;
|
||||||
|
|
||||||
auto c0 = kari::curry(&Foo::addV);
|
auto c0 = curry(&foo_t::add_v);
|
||||||
auto c1 = kari::curry(&Foo::v);
|
auto c1 = curry(&foo_t::v);
|
||||||
|
|
||||||
auto r0 = c0(std::ref(foo))(2);
|
auto r0 = c0(std::ref(foo))(2);
|
||||||
auto r1 = c1(foo);
|
auto r1 = c1(std::cref(foo));
|
||||||
|
|
||||||
// output: 42,42
|
// output: 42,42
|
||||||
std::cout << r0 << "," << r1 << std::endl;
|
std::cout << r0 << "," << r1 << std::endl;
|
||||||
@@ -123,15 +130,9 @@ std::cout << r0 << "," << r1 << std::endl;
|
|||||||
## API
|
## API
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
namespace kari {
|
namespace kari_hpp {
|
||||||
template < typename F, typename... Args >
|
template < typename F, typename... Args >
|
||||||
constexpr decltype(auto) curry(F&& f, Args&&... args) const;
|
constexpr auto curry(F&& f, Args&&... args);
|
||||||
|
|
||||||
template < typename F, typename... Args >
|
|
||||||
constexpr decltype(auto) curryV(F&& f, Args&&... args) const;
|
|
||||||
|
|
||||||
template < std::size_t N, typename F, typename... Args >
|
|
||||||
constexpr decltype(auto) curryN(F&& f, Args&&... args) const;
|
|
||||||
|
|
||||||
template < typename F >
|
template < typename F >
|
||||||
struct is_curried;
|
struct is_curried;
|
||||||
@@ -139,59 +140,34 @@ namespace kari {
|
|||||||
template < typename F >
|
template < typename F >
|
||||||
inline constexpr bool is_curried_v = is_curried<F>::value;
|
inline constexpr bool is_curried_v = is_curried<F>::value;
|
||||||
|
|
||||||
template < std::size_t N, typename F, typename... Args >
|
template < typename F, typename... Args >
|
||||||
struct curry_t {
|
class curry_t {
|
||||||
|
constexpr curry_t(F f);
|
||||||
|
|
||||||
template < typename... As >
|
template < typename... As >
|
||||||
constexpr decltype(auto) operator()(As&&... as) const;
|
constexpr auto operator()(As&&... as) const;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### `kari::curry(F&& f, Args&&... args)`
|
### `kari_hpp::curry(F&& f, Args&&... args)`
|
||||||
|
|
||||||
Returns a curried function **`f`** or copy the function result with **`args`** arguments.
|
Returns a curried function **`f`** or copy the function result with **`args`** arguments.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### `kari::curryV(F&& f, Args&&... args)`
|
### `kari_hpp::is_curried<F>, kari_hpp::is_curried_v<F>`
|
||||||
|
|
||||||
Allows carrying variadic functions.
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
auto c0 = kari::curryV(std::printf, "%d + %d = %d");
|
|
||||||
auto c1 = c0(37, 5);
|
|
||||||
auto c2 = c1(42);
|
|
||||||
|
|
||||||
// force calling carried variadic function
|
|
||||||
c2(); // output: 37 + 5 = 42
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### `kari::curryN(F&& f, Args&&... args)`
|
|
||||||
|
|
||||||
Allows carrying variadic functions for **`N`** arguments.
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
char buffer[256] = {'\0'};
|
|
||||||
auto c = kari::curryN<3>(std::snprintf, buffer, 256, "%d + %d = %d");
|
|
||||||
c(37, 5, 42);
|
|
||||||
std::cout << buffer << std::endl; // output: 37 + 5 = 42
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### `kari::is_curried<F>, kari::is_curried_v<F>`
|
|
||||||
|
|
||||||
Checks whether F is a curried function type.
|
Checks whether F is a curried function type.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
auto l = [](int v1, int v2){
|
using namespace kari_hpp;
|
||||||
return v1 + v2;
|
|
||||||
|
constexpr curry_t c = [](int a, int b){
|
||||||
|
return a + b;
|
||||||
};
|
};
|
||||||
auto c = curry(l);
|
|
||||||
|
|
||||||
// output: is `l` curried? no
|
// output: is `l` curried? no
|
||||||
std::cout
|
std::cout
|
||||||
@@ -208,18 +184,19 @@ std::cout
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### `kari::curry_t::operator()(As&&... as)`
|
### `kari_hpp::curry_t::operator()(As&&... as)`
|
||||||
|
|
||||||
Calling operator of curried function for partial application or full application. Returns a new curried function with added new arguments or copy of the function result.
|
Calling operator of curried function for partial application or full application. Returns a new curried function with added new arguments or copy of the function result.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
int foo(int v1, int v2, int v3, int v4) {
|
using namespace kari_hpp;
|
||||||
return v1 + v2 + v3 + v4;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto c0 = kari::curry(foo); // currying
|
curry_t c0 = [](int a, int b, int c, int d) {
|
||||||
auto c1 = c0(15, 20); // partial application
|
return a + b + c + d;
|
||||||
auto rr = c2(2, 5); // function call - foo(15,20,2,5)
|
};
|
||||||
|
|
||||||
|
auto c1 = c0(15, 20); // partial application
|
||||||
|
auto rr = c2(2, 5); // function call - foo(15,20,2,5)
|
||||||
|
|
||||||
// output: 42
|
// output: 42
|
||||||
std::cout << rr << std::endl;
|
std::cout << rr << std::endl;
|
||||||
@@ -232,17 +209,17 @@ std::cout << rr << std::endl;
|
|||||||
### Section of operators
|
### Section of operators
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
using namespace kari::underscore;
|
using namespace kari_hpp::ext::underscore;
|
||||||
std::vector<int> v{1,2,3,4};
|
std::vector v{1, 2, 3, 4};
|
||||||
|
|
||||||
// result: 10
|
// result: 10
|
||||||
std::accumulate(v.begin(), v.end(), 0, _+_);
|
std::accumulate(v.begin(), v.end(), 0, _ + _);
|
||||||
|
|
||||||
// v = 2, 3, 6, 8
|
// v = {2, 4, 6, 8}
|
||||||
std::transform(v.begin(), v.end(), v.begin(), _*2);
|
std::transform(v.begin(), v.end(), v.begin(), _ * 2);
|
||||||
|
|
||||||
// v = -2,-3,-6,-8
|
// v = {-2, -4, -6, -8}
|
||||||
std::transform(v.begin(), v.end(), v.begin(), -_);
|
std::transform(v.begin(), v.end(), v.begin(), - _);
|
||||||
```
|
```
|
||||||
|
|
||||||
### Function composition
|
### Function composition
|
||||||
@@ -250,7 +227,8 @@ std::transform(v.begin(), v.end(), v.begin(), -_);
|
|||||||
#### Pipe operator
|
#### Pipe operator
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
using namespace kari::underscore;
|
using namespace kari_hpp::ext;
|
||||||
|
using namespace kari_hpp::ext::underscore;
|
||||||
|
|
||||||
auto r0 = (_*2) | (_+2) | 4; // (4 * 2) + 2 = 10
|
auto r0 = (_*2) | (_+2) | 4; // (4 * 2) + 2 = 10
|
||||||
auto r1 = 4 | (_*2) | (_+2); // (4 * 2 + 2) = 10
|
auto r1 = 4 | (_*2) | (_+2); // (4 * 2 + 2) = 10
|
||||||
@@ -262,7 +240,8 @@ std::cout << r0, << "," << r1 << std::endl;
|
|||||||
#### Compose operator
|
#### Compose operator
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
using namespace kari::underscore;
|
using namespace kari_hpp::ext;
|
||||||
|
using namespace kari_hpp::ext::underscore;
|
||||||
|
|
||||||
auto r0 = (_*2) * (_+2) * 4; // (4 + 2) * 2 = 12
|
auto r0 = (_*2) * (_+2) * 4; // (4 + 2) * 2 = 12
|
||||||
auto r1 = 4 * (_*2) * (_+2); // (4 * 2 + 2) = 10
|
auto r1 = 4 * (_*2) * (_+2); // (4 * 2 + 2) = 10
|
||||||
@@ -274,7 +253,8 @@ std::cout << r0, << "," << r1 << std::endl;
|
|||||||
### Point-free style for Haskell maniacs
|
### Point-free style for Haskell maniacs
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
using namespace kari::underscore;
|
using namespace kari_hpp::ext;
|
||||||
|
using namespace kari_hpp::ext::underscore;
|
||||||
|
|
||||||
// (. (+2)) (*2) $ 10 == 24 // haskell analog
|
// (. (+2)) (*2) $ 10 == 24 // haskell analog
|
||||||
auto r0 = (_*(_+2))(_*2) * 10;
|
auto r0 = (_*(_+2))(_*2) * 10;
|
||||||
|
|||||||
@@ -6,114 +6,68 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <tuple>
|
|
||||||
#include <limits>
|
|
||||||
#include <utility>
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <tuple>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#define KARI_HPP_NOEXCEPT_RETURN(...) \
|
namespace kari_hpp
|
||||||
noexcept(noexcept(__VA_ARGS__)) { return __VA_ARGS__; }
|
|
||||||
|
|
||||||
#define KARI_HPP_NOEXCEPT_DECLTYPE_RETURN(...) \
|
|
||||||
noexcept(noexcept(__VA_ARGS__)) -> decltype (__VA_ARGS__) { return __VA_ARGS__; }
|
|
||||||
|
|
||||||
namespace kari
|
|
||||||
{
|
{
|
||||||
template < std::size_t N, typename F, typename... Args >
|
template < typename F, typename... As >
|
||||||
struct curry_t;
|
class curry_t;
|
||||||
|
|
||||||
namespace detail
|
namespace impl
|
||||||
{
|
{
|
||||||
template
|
template < typename F >
|
||||||
<
|
struct is_curried_impl
|
||||||
std::size_t N, typename F, typename... Args,
|
: std::false_type {};
|
||||||
typename std::enable_if_t<
|
|
||||||
(N == 0) &&
|
|
||||||
std::is_invocable_v<std::decay_t<F>, Args...>
|
|
||||||
, int> = 0
|
|
||||||
>
|
|
||||||
constexpr auto make_curry(F&& f, std::tuple<Args...>&& args)
|
|
||||||
KARI_HPP_NOEXCEPT_RETURN(
|
|
||||||
std::apply(std::forward<F>(f), std::move(args)))
|
|
||||||
|
|
||||||
template
|
template < typename F, typename... As >
|
||||||
<
|
struct is_curried_impl<curry_t<F, As...>>
|
||||||
std::size_t N, typename F, typename... Args,
|
: std::true_type {};
|
||||||
typename std::enable_if_t<
|
|
||||||
(N > 0) ||
|
|
||||||
!std::is_invocable_v<std::decay_t<F>, Args...>
|
|
||||||
, int> = 0
|
|
||||||
>
|
|
||||||
constexpr auto make_curry(F&& f, std::tuple<Args...>&& args)
|
|
||||||
KARI_HPP_NOEXCEPT_DECLTYPE_RETURN(
|
|
||||||
curry_t<
|
|
||||||
N,
|
|
||||||
std::decay_t<F>,
|
|
||||||
Args...
|
|
||||||
>(std::forward<F>(f), std::move(args)))
|
|
||||||
|
|
||||||
template < std::size_t N, typename F >
|
|
||||||
constexpr auto make_curry(F&& f)
|
|
||||||
KARI_HPP_NOEXCEPT_DECLTYPE_RETURN(
|
|
||||||
make_curry<N>(std::forward<F>(f), std::make_tuple()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template < std::size_t N, typename F, typename... Args >
|
template < typename F >
|
||||||
struct curry_t final {
|
struct is_curried
|
||||||
template < typename U >
|
: impl::is_curried_impl<std::remove_cv_t<F>> {};
|
||||||
constexpr curry_t(U&& u, std::tuple<Args...>&& args)
|
|
||||||
noexcept(noexcept(F(std::forward<U>(u))) && noexcept(std::tuple<Args...>(std::move(args))))
|
template < typename F >
|
||||||
: f_(std::forward<U>(u))
|
inline constexpr bool is_curried_v = is_curried<F>::value;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace kari_hpp::detail
|
||||||
|
{
|
||||||
|
template < typename F, typename... Args >
|
||||||
|
constexpr auto curry_or_apply(F&& f, std::tuple<Args...>&& args) {
|
||||||
|
if constexpr ( std::is_invocable_v<std::decay_t<F>, Args...> ) {
|
||||||
|
return std::apply(std::forward<F>(f), std::move(args));
|
||||||
|
} else {
|
||||||
|
return curry_t(std::forward<F>(f), std::move(args));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace kari_hpp
|
||||||
|
{
|
||||||
|
template < typename F, typename... Args >
|
||||||
|
class curry_t final {
|
||||||
|
public:
|
||||||
|
constexpr curry_t(F f)
|
||||||
|
: f_(std::move(f)) {}
|
||||||
|
|
||||||
|
constexpr curry_t(F f, std::tuple<Args...> args)
|
||||||
|
: f_(std::move(f))
|
||||||
, args_(std::move(args)) {}
|
, args_(std::move(args)) {}
|
||||||
|
|
||||||
// min_arity
|
constexpr auto operator()() && {
|
||||||
|
return detail::curry_or_apply(
|
||||||
constexpr std::size_t min_arity() const noexcept {
|
|
||||||
return N;
|
|
||||||
}
|
|
||||||
|
|
||||||
// recurry
|
|
||||||
|
|
||||||
template < std::size_t M >
|
|
||||||
constexpr decltype(auto) recurry() &&
|
|
||||||
noexcept(noexcept(
|
|
||||||
detail::make_curry<M>(
|
|
||||||
std::move(std::declval<F>()),
|
|
||||||
std::move(std::declval<std::tuple<Args...>>()))))
|
|
||||||
{
|
|
||||||
return detail::make_curry<M>(
|
|
||||||
std::move(f_),
|
std::move(f_),
|
||||||
std::move(args_));
|
std::move(args_));
|
||||||
}
|
}
|
||||||
|
|
||||||
template < std::size_t M >
|
|
||||||
constexpr decltype(auto) recurry() const &
|
|
||||||
noexcept(noexcept(
|
|
||||||
std::move(std::declval<curry_t>()).template recurry<M>()))
|
|
||||||
{
|
|
||||||
return std::move(curry_t(*this)).template recurry<M>();
|
|
||||||
}
|
|
||||||
|
|
||||||
// operator(As&&...)
|
|
||||||
|
|
||||||
constexpr decltype(auto) operator()() &&
|
|
||||||
noexcept(noexcept(
|
|
||||||
std::move(std::declval<curry_t>()).template recurry<0>()))
|
|
||||||
{
|
|
||||||
return std::move(*this).template recurry<0>();
|
|
||||||
}
|
|
||||||
|
|
||||||
template < typename A >
|
template < typename A >
|
||||||
constexpr decltype(auto) operator()(A&& a) &&
|
constexpr auto operator()(A&& a) && {
|
||||||
noexcept(noexcept(
|
return detail::curry_or_apply(
|
||||||
detail::make_curry<(N > 0 ? N - 1 : 0)>(
|
|
||||||
std::move(std::declval<F>()),
|
|
||||||
std::tuple_cat(
|
|
||||||
std::move(std::declval<std::tuple<Args...>>()),
|
|
||||||
std::make_tuple(std::forward<A>(a))))))
|
|
||||||
{
|
|
||||||
return detail::make_curry<(N > 0 ? N - 1 : 0)>(
|
|
||||||
std::move(f_),
|
std::move(f_),
|
||||||
std::tuple_cat(
|
std::tuple_cat(
|
||||||
std::move(args_),
|
std::move(args_),
|
||||||
@@ -121,138 +75,38 @@ namespace kari
|
|||||||
}
|
}
|
||||||
|
|
||||||
template < typename A, typename... As >
|
template < typename A, typename... As >
|
||||||
constexpr decltype(auto) operator()(A&& a, As&&... as) &&
|
constexpr auto operator()(A&& a, As&&... as) && {
|
||||||
noexcept(noexcept(
|
|
||||||
std::move(std::declval<curry_t>())(std::forward<A>(a))(std::forward<As>(as)...)))
|
|
||||||
{
|
|
||||||
return std::move(*this)(std::forward<A>(a))(std::forward<As>(as)...);
|
return std::move(*this)(std::forward<A>(a))(std::forward<As>(as)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template < typename... As >
|
template < typename... As >
|
||||||
constexpr decltype(auto) operator()(As&&... as) const &
|
constexpr auto operator()(As&&... as) const & {
|
||||||
noexcept(noexcept(
|
|
||||||
std::move(std::declval<curry_t>())(std::forward<As>(as)...)))
|
|
||||||
{
|
|
||||||
return std::move(curry_t(*this))(std::forward<As>(as)...);
|
return std::move(curry_t(*this))(std::forward<As>(as)...);
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
F f_;
|
F f_;
|
||||||
std::tuple<Args...> args_;
|
std::tuple<Args...> args_;
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
|
||||||
// is_curried, is_curried_v
|
|
||||||
//
|
|
||||||
|
|
||||||
namespace detail
|
|
||||||
{
|
|
||||||
template < typename F >
|
|
||||||
struct is_curried_impl
|
|
||||||
: std::false_type {};
|
|
||||||
|
|
||||||
template < std::size_t N, typename F, typename... Args >
|
|
||||||
struct is_curried_impl<curry_t<N, F, Args...>>
|
|
||||||
: std::true_type {};
|
|
||||||
}
|
|
||||||
|
|
||||||
template < typename F >
|
|
||||||
struct is_curried
|
|
||||||
: detail::is_curried_impl<std::remove_cv_t<F>> {};
|
|
||||||
|
|
||||||
template < typename F >
|
|
||||||
inline constexpr bool is_curried_v = is_curried<F>::value;
|
|
||||||
|
|
||||||
//
|
|
||||||
// curry
|
|
||||||
//
|
|
||||||
|
|
||||||
template
|
|
||||||
<
|
|
||||||
typename F,
|
|
||||||
typename std::enable_if_t<is_curried_v<std::decay_t<F>>, int> = 0
|
|
||||||
>
|
|
||||||
constexpr auto curry(F&& f)
|
|
||||||
KARI_HPP_NOEXCEPT_DECLTYPE_RETURN(
|
|
||||||
std::forward<F>(f))
|
|
||||||
|
|
||||||
template
|
|
||||||
<
|
|
||||||
typename F,
|
|
||||||
typename std::enable_if_t<!is_curried_v<std::decay_t<F>>, int> = 0
|
|
||||||
>
|
|
||||||
constexpr auto curry(F&& f)
|
|
||||||
KARI_HPP_NOEXCEPT_DECLTYPE_RETURN(
|
|
||||||
detail::make_curry<0>(std::forward<F>(f)))
|
|
||||||
|
|
||||||
template < typename F, typename A, typename... As >
|
|
||||||
constexpr auto curry(F&& f, A&& a, As&&... as)
|
|
||||||
KARI_HPP_NOEXCEPT_DECLTYPE_RETURN(
|
|
||||||
curry(std::forward<F>(f))(std::forward<A>(a), std::forward<As>(as)...))
|
|
||||||
|
|
||||||
//
|
|
||||||
// curryV
|
|
||||||
//
|
|
||||||
|
|
||||||
template
|
|
||||||
<
|
|
||||||
typename F,
|
|
||||||
std::size_t MaxN = std::numeric_limits<std::size_t>::max(),
|
|
||||||
typename std::enable_if_t<is_curried_v<std::decay_t<F>>, int> = 0
|
|
||||||
>
|
|
||||||
constexpr auto curryV(F&& f)
|
|
||||||
KARI_HPP_NOEXCEPT_DECLTYPE_RETURN(
|
|
||||||
std::forward<F>(f).template recurry<MaxN>())
|
|
||||||
|
|
||||||
template
|
|
||||||
<
|
|
||||||
typename F,
|
|
||||||
std::size_t MaxN = std::numeric_limits<std::size_t>::max(),
|
|
||||||
typename std::enable_if_t<!is_curried_v<std::decay_t<F>>, int> = 0
|
|
||||||
>
|
|
||||||
constexpr auto curryV(F&& f)
|
|
||||||
KARI_HPP_NOEXCEPT_DECLTYPE_RETURN(
|
|
||||||
detail::make_curry<MaxN>(std::forward<F>(f)))
|
|
||||||
|
|
||||||
template < typename F, typename A, typename... As >
|
|
||||||
constexpr auto curryV(F&& f, A&& a, As&&... as)
|
|
||||||
KARI_HPP_NOEXCEPT_DECLTYPE_RETURN(
|
|
||||||
curryV(std::forward<F>(f))(std::forward<A>(a), std::forward<As>(as)...))
|
|
||||||
|
|
||||||
//
|
|
||||||
// curryN
|
|
||||||
//
|
|
||||||
|
|
||||||
template
|
|
||||||
<
|
|
||||||
std::size_t N, typename F,
|
|
||||||
typename std::enable_if_t<is_curried_v<std::decay_t<F>>, int> = 0
|
|
||||||
>
|
|
||||||
constexpr auto curryN(F&& f)
|
|
||||||
KARI_HPP_NOEXCEPT_DECLTYPE_RETURN(
|
|
||||||
std::forward<F>(f).template recurry<N>())
|
|
||||||
|
|
||||||
template
|
|
||||||
<
|
|
||||||
std::size_t N, typename F,
|
|
||||||
typename std::enable_if_t<!is_curried_v<std::decay_t<F>>, int> = 0
|
|
||||||
>
|
|
||||||
constexpr auto curryN(F&& f)
|
|
||||||
KARI_HPP_NOEXCEPT_DECLTYPE_RETURN(
|
|
||||||
detail::make_curry<N>(std::forward<F>(f)))
|
|
||||||
|
|
||||||
template
|
|
||||||
<
|
|
||||||
std::size_t N, typename F, typename A, typename... As,
|
|
||||||
std::size_t Args = sizeof...(As) + 1,
|
|
||||||
std::size_t MaxN = std::numeric_limits<std::size_t>::max(),
|
|
||||||
typename std::enable_if_t<(MaxN - Args >= N), int> = 0
|
|
||||||
>
|
|
||||||
constexpr auto curryN(F&& f, A&& a, As&&... as)
|
|
||||||
KARI_HPP_NOEXCEPT_DECLTYPE_RETURN(
|
|
||||||
curryN<N + Args>(std::forward<F>(f))(std::forward<A>(a), std::forward<As>(as)...))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace kari
|
namespace kari_hpp
|
||||||
|
{
|
||||||
|
template < typename F >
|
||||||
|
constexpr auto curry(F&& f) {
|
||||||
|
if constexpr ( is_curried_v<std::decay_t<F>> ) {
|
||||||
|
return std::forward<F>(f);
|
||||||
|
} else {
|
||||||
|
return detail::curry_or_apply(std::forward<F>(f), {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template < typename F, typename A, typename... As >
|
||||||
|
constexpr auto curry(F&& f, A&& a, As&&... as) {
|
||||||
|
return curry(std::forward<F>(f))(std::forward<A>(a), std::forward<As>(as)...);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace kari_hpp::ext
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
// fid
|
// fid
|
||||||
@@ -260,9 +114,9 @@ namespace kari
|
|||||||
|
|
||||||
struct fid_t {
|
struct fid_t {
|
||||||
template < typename A >
|
template < typename A >
|
||||||
constexpr auto operator()(A&& a) const
|
constexpr auto operator()(A&& a) const {
|
||||||
KARI_HPP_NOEXCEPT_DECLTYPE_RETURN(
|
return std::forward<A>(a);
|
||||||
std::forward<A>(a))
|
}
|
||||||
};
|
};
|
||||||
inline constexpr auto fid = curry(fid_t{});
|
inline constexpr auto fid = curry(fid_t{});
|
||||||
|
|
||||||
@@ -272,9 +126,9 @@ namespace kari
|
|||||||
|
|
||||||
struct fconst_t {
|
struct fconst_t {
|
||||||
template < typename A, typename B >
|
template < typename A, typename B >
|
||||||
constexpr auto operator()(A&& a, B&&) const
|
constexpr auto operator()(A&& a, [[maybe_unused]] B&& b) const {
|
||||||
KARI_HPP_NOEXCEPT_DECLTYPE_RETURN(
|
return std::forward<A>(a);
|
||||||
std::forward<A>(a))
|
}
|
||||||
};
|
};
|
||||||
inline constexpr auto fconst = curry(fconst_t{});
|
inline constexpr auto fconst = curry(fconst_t{});
|
||||||
|
|
||||||
@@ -284,12 +138,9 @@ namespace kari
|
|||||||
|
|
||||||
struct fflip_t {
|
struct fflip_t {
|
||||||
template < typename F, typename A, typename B >
|
template < typename F, typename A, typename B >
|
||||||
constexpr auto operator()(F&& f, A&& a, B&& b) const
|
constexpr auto operator()(F&& f, A&& a, B&& b) const {
|
||||||
KARI_HPP_NOEXCEPT_DECLTYPE_RETURN(
|
return curry(std::forward<F>(f), std::forward<B>(b), std::forward<A>(a));
|
||||||
curry(
|
}
|
||||||
std::forward<F>(f),
|
|
||||||
std::forward<B>(b),
|
|
||||||
std::forward<A>(a)))
|
|
||||||
};
|
};
|
||||||
inline constexpr auto fflip = curry(fflip_t{});
|
inline constexpr auto fflip = curry(fflip_t{});
|
||||||
|
|
||||||
@@ -299,13 +150,11 @@ namespace kari
|
|||||||
|
|
||||||
struct fpipe_t {
|
struct fpipe_t {
|
||||||
template < typename G, typename F, typename A >
|
template < typename G, typename F, typename A >
|
||||||
constexpr auto operator()(G&& g, F&& f, A&& a) const
|
constexpr auto operator()(G&& g, F&& f, A&& a) const {
|
||||||
KARI_HPP_NOEXCEPT_DECLTYPE_RETURN(
|
return curry(
|
||||||
curry(
|
|
||||||
std::forward<F>(f),
|
std::forward<F>(f),
|
||||||
curry(
|
curry(std::forward<G>(g), std::forward<A>(a)));
|
||||||
std::forward<G>(g),
|
}
|
||||||
std::forward<A>(a))))
|
|
||||||
};
|
};
|
||||||
inline constexpr auto fpipe = curry(fpipe_t{});
|
inline constexpr auto fpipe = curry(fpipe_t{});
|
||||||
|
|
||||||
@@ -315,13 +164,11 @@ namespace kari
|
|||||||
|
|
||||||
struct fcompose_t {
|
struct fcompose_t {
|
||||||
template < typename G, typename F, typename A >
|
template < typename G, typename F, typename A >
|
||||||
constexpr auto operator()(G&& g, F&& f, A&& a) const
|
constexpr auto operator()(G&& g, F&& f, A&& a) const {
|
||||||
KARI_HPP_NOEXCEPT_DECLTYPE_RETURN(
|
return curry(
|
||||||
curry(
|
|
||||||
std::forward<G>(g),
|
std::forward<G>(g),
|
||||||
curry(
|
curry(std::forward<F>(f), std::forward<A>(a)));
|
||||||
std::forward<F>(f),
|
}
|
||||||
std::forward<A>(a))))
|
|
||||||
};
|
};
|
||||||
inline constexpr auto fcompose = curry(fcompose_t{});
|
inline constexpr auto fcompose = curry(fcompose_t{});
|
||||||
|
|
||||||
@@ -329,144 +176,125 @@ namespace kari
|
|||||||
// fpipe operators
|
// fpipe operators
|
||||||
//
|
//
|
||||||
|
|
||||||
template
|
template < typename G, typename F
|
||||||
<
|
, std::enable_if_t<std::disjunction_v<
|
||||||
typename G, typename F,
|
is_curried<std::decay_t<G>>,
|
||||||
typename std::enable_if_t<is_curried_v<std::decay_t<G>>, int> = 0,
|
is_curried<std::decay_t<F>>>, int> = 0 >
|
||||||
typename std::enable_if_t<is_curried_v<std::decay_t<F>>, int> = 0
|
constexpr auto operator|(G&& g, F&& f) {
|
||||||
>
|
constexpr bool gc = is_curried_v<std::decay_t<G>>;
|
||||||
constexpr auto operator|(G&& g, F&& f)
|
constexpr bool fc = is_curried_v<std::decay_t<F>>;
|
||||||
KARI_HPP_NOEXCEPT_DECLTYPE_RETURN(
|
|
||||||
fpipe(
|
|
||||||
std::forward<G>(g),
|
|
||||||
std::forward<F>(f)))
|
|
||||||
|
|
||||||
template
|
if constexpr ( gc && fc ) {
|
||||||
<
|
return fpipe(std::forward<G>(g), std::forward<F>(f));
|
||||||
typename F, typename A,
|
}
|
||||||
typename std::enable_if_t< is_curried_v<std::decay_t<F>>, int> = 0,
|
|
||||||
typename std::enable_if_t<!is_curried_v<std::decay_t<A>>, int> = 0
|
|
||||||
>
|
|
||||||
constexpr auto operator|(F&& f, A&& a)
|
|
||||||
KARI_HPP_NOEXCEPT_DECLTYPE_RETURN(
|
|
||||||
std::forward<F>(f)(std::forward<A>(a)))
|
|
||||||
|
|
||||||
template
|
if constexpr ( gc && !fc) {
|
||||||
<
|
return std::forward<G>(g)(std::forward<F>(f));
|
||||||
typename A, typename F,
|
}
|
||||||
typename std::enable_if_t<!is_curried_v<std::decay_t<A>>, int> = 0,
|
|
||||||
typename std::enable_if_t< is_curried_v<std::decay_t<F>>, int> = 0
|
if constexpr ( !gc && fc) {
|
||||||
>
|
return std::forward<F>(f)(std::forward<G>(g));
|
||||||
constexpr auto operator|(A&& a, F&& f)
|
}
|
||||||
KARI_HPP_NOEXCEPT_DECLTYPE_RETURN(
|
|
||||||
std::forward<F>(f)(std::forward<A>(a)))
|
static_assert(gc || fc, "F or G or both arguments should be curried");
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// fcompose operators
|
// fcompose operators
|
||||||
//
|
//
|
||||||
|
|
||||||
template
|
template < typename G, typename F
|
||||||
<
|
, std::enable_if_t<std::disjunction_v<
|
||||||
typename G, typename F,
|
is_curried<std::decay_t<G>>,
|
||||||
typename std::enable_if_t<is_curried_v<std::decay_t<G>>, int> = 0,
|
is_curried<std::decay_t<F>>>, int> = 0 >
|
||||||
typename std::enable_if_t<is_curried_v<std::decay_t<F>>, int> = 0
|
constexpr auto operator*(G&& g, F&& f) {
|
||||||
>
|
constexpr bool gc = is_curried_v<std::decay_t<G>>;
|
||||||
constexpr auto operator*(G&& g, F&& f)
|
constexpr bool fc = is_curried_v<std::decay_t<F>>;
|
||||||
KARI_HPP_NOEXCEPT_DECLTYPE_RETURN(
|
|
||||||
fcompose(
|
|
||||||
std::forward<G>(g),
|
|
||||||
std::forward<F>(f)))
|
|
||||||
|
|
||||||
template
|
if constexpr ( gc && fc ) {
|
||||||
<
|
return fcompose(std::forward<G>(g), std::forward<F>(f));
|
||||||
typename F, typename A,
|
}
|
||||||
typename std::enable_if_t< is_curried_v<std::decay_t<F>>, int> = 0,
|
|
||||||
typename std::enable_if_t<!is_curried_v<std::decay_t<A>>, int> = 0
|
|
||||||
>
|
|
||||||
constexpr auto operator*(F&& f, A&& a)
|
|
||||||
KARI_HPP_NOEXCEPT_DECLTYPE_RETURN(
|
|
||||||
std::forward<F>(f)(std::forward<A>(a)))
|
|
||||||
|
|
||||||
template
|
if constexpr ( gc && !fc) {
|
||||||
<
|
return std::forward<G>(g)(std::forward<F>(f));
|
||||||
typename A, typename F,
|
}
|
||||||
typename std::enable_if_t<!is_curried_v<std::decay_t<A>>, int> = 0,
|
|
||||||
typename std::enable_if_t< is_curried_v<std::decay_t<F>>, int> = 0
|
|
||||||
>
|
|
||||||
constexpr auto operator*(A&& a, F&& f)
|
|
||||||
KARI_HPP_NOEXCEPT_DECLTYPE_RETURN(
|
|
||||||
std::forward<F>(f)(std::forward<A>(a)))
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace kari
|
if constexpr ( !gc && fc) {
|
||||||
{
|
return std::forward<F>(f)(std::forward<G>(g));
|
||||||
namespace underscore
|
}
|
||||||
{
|
|
||||||
struct us_t {};
|
|
||||||
inline constexpr us_t _{};
|
|
||||||
|
|
||||||
//
|
static_assert(gc || fc, "F or G or both arguments should be curried");
|
||||||
// is_underscore, is_underscore_v
|
|
||||||
//
|
|
||||||
|
|
||||||
template < typename T >
|
|
||||||
struct is_underscore
|
|
||||||
: std::bool_constant<std::is_same_v<us_t, std::remove_cv_t<T>>> {};
|
|
||||||
|
|
||||||
template < typename T >
|
|
||||||
inline constexpr bool is_underscore_v = is_underscore<T>::value;
|
|
||||||
|
|
||||||
//
|
|
||||||
// unary operators
|
|
||||||
//
|
|
||||||
|
|
||||||
#define KARI_HPP_DEFINE_UNDERSCORE_UNARY_OP(op, func) \
|
|
||||||
constexpr auto operator op (us_t) KARI_HPP_NOEXCEPT_DECLTYPE_RETURN(curry(func))
|
|
||||||
|
|
||||||
KARI_HPP_DEFINE_UNDERSCORE_UNARY_OP(-, std::negate<>())
|
|
||||||
KARI_HPP_DEFINE_UNDERSCORE_UNARY_OP(~, std::bit_not<>())
|
|
||||||
KARI_HPP_DEFINE_UNDERSCORE_UNARY_OP(!, std::logical_not<>())
|
|
||||||
#undef KARI_HPP_DEFINE_UNDERSCORE_UNARY_OP
|
|
||||||
|
|
||||||
//
|
|
||||||
// binary operators
|
|
||||||
//
|
|
||||||
|
|
||||||
#define KARI_HPP_DEFINE_UNDERSCORE_BINARY_OP(op, func) \
|
|
||||||
constexpr auto operator op (us_t, us_t) \
|
|
||||||
KARI_HPP_NOEXCEPT_DECLTYPE_RETURN(curry(func)) \
|
|
||||||
\
|
|
||||||
template < typename A, typename std::enable_if_t<!is_underscore_v<std::decay_t<A>>, int> = 0 > \
|
|
||||||
constexpr auto operator op (A&& a, us_t) \
|
|
||||||
KARI_HPP_NOEXCEPT_DECLTYPE_RETURN(curry(func, std::forward<A>(a))) \
|
|
||||||
\
|
|
||||||
template < typename B, typename std::enable_if_t<!is_underscore_v<std::decay_t<B>>, int> = 0 > \
|
|
||||||
constexpr auto operator op (us_t, B&& b) \
|
|
||||||
KARI_HPP_NOEXCEPT_DECLTYPE_RETURN(fflip(func, std::forward<B>(b)))
|
|
||||||
|
|
||||||
KARI_HPP_DEFINE_UNDERSCORE_BINARY_OP(+ , std::plus<>())
|
|
||||||
KARI_HPP_DEFINE_UNDERSCORE_BINARY_OP(- , std::minus<>())
|
|
||||||
KARI_HPP_DEFINE_UNDERSCORE_BINARY_OP(* , std::multiplies<>())
|
|
||||||
KARI_HPP_DEFINE_UNDERSCORE_BINARY_OP(/ , std::divides<>())
|
|
||||||
KARI_HPP_DEFINE_UNDERSCORE_BINARY_OP(% , std::modulus<>())
|
|
||||||
|
|
||||||
KARI_HPP_DEFINE_UNDERSCORE_BINARY_OP(< , std::less<>())
|
|
||||||
KARI_HPP_DEFINE_UNDERSCORE_BINARY_OP(> , std::greater<>())
|
|
||||||
KARI_HPP_DEFINE_UNDERSCORE_BINARY_OP(<=, std::less_equal<>())
|
|
||||||
KARI_HPP_DEFINE_UNDERSCORE_BINARY_OP(>=, std::greater_equal<>())
|
|
||||||
|
|
||||||
KARI_HPP_DEFINE_UNDERSCORE_BINARY_OP(==, std::equal_to<>())
|
|
||||||
KARI_HPP_DEFINE_UNDERSCORE_BINARY_OP(!=, std::not_equal_to<>())
|
|
||||||
|
|
||||||
KARI_HPP_DEFINE_UNDERSCORE_BINARY_OP(| , std::bit_or<>())
|
|
||||||
KARI_HPP_DEFINE_UNDERSCORE_BINARY_OP(& , std::bit_and<>())
|
|
||||||
KARI_HPP_DEFINE_UNDERSCORE_BINARY_OP(^ , std::bit_xor<>())
|
|
||||||
|
|
||||||
KARI_HPP_DEFINE_UNDERSCORE_BINARY_OP(||, std::logical_or<>())
|
|
||||||
KARI_HPP_DEFINE_UNDERSCORE_BINARY_OP(&&, std::logical_and<>())
|
|
||||||
#undef KARI_HPP_DEFINE_UNDERSCORE_BINARY_OP
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef KARI_HPP_NOEXCEPT_RETURN
|
namespace kari_hpp::ext::underscore
|
||||||
#undef KARI_HPP_NOEXCEPT_DECLTYPE_RETURN
|
{
|
||||||
|
struct us_t {};
|
||||||
|
inline constexpr us_t _{};
|
||||||
|
|
||||||
|
//
|
||||||
|
// is_underscore, is_underscore_v
|
||||||
|
//
|
||||||
|
|
||||||
|
template < typename T >
|
||||||
|
struct is_underscore
|
||||||
|
: std::bool_constant<std::is_same_v<us_t, std::remove_cv_t<T>>> {};
|
||||||
|
|
||||||
|
template < typename T >
|
||||||
|
inline constexpr bool is_underscore_v = is_underscore<T>::value;
|
||||||
|
|
||||||
|
//
|
||||||
|
// unary operators
|
||||||
|
//
|
||||||
|
|
||||||
|
#define KARI_HPP_DEFINE_UNDERSCORE_UNARY_OP(op, func)\
|
||||||
|
constexpr auto operator op (us_t) {\
|
||||||
|
return curry(func);\
|
||||||
|
}
|
||||||
|
|
||||||
|
KARI_HPP_DEFINE_UNDERSCORE_UNARY_OP(-, std::negate<>())
|
||||||
|
KARI_HPP_DEFINE_UNDERSCORE_UNARY_OP(~, std::bit_not<>())
|
||||||
|
KARI_HPP_DEFINE_UNDERSCORE_UNARY_OP(!, std::logical_not<>())
|
||||||
|
#undef KARI_HPP_DEFINE_UNDERSCORE_UNARY_OP
|
||||||
|
|
||||||
|
//
|
||||||
|
// binary operators
|
||||||
|
//
|
||||||
|
|
||||||
|
#define KARI_HPP_DEFINE_UNDERSCORE_BINARY_OP(op, func)\
|
||||||
|
constexpr auto operator op (us_t, us_t) {\
|
||||||
|
return curry(func);\
|
||||||
|
}\
|
||||||
|
\
|
||||||
|
template < typename A, std::enable_if_t<!is_underscore_v<std::decay_t<A>>, int> = 0 >\
|
||||||
|
constexpr auto operator op (A&& a, us_t) {\
|
||||||
|
return curry(func, std::forward<A>(a));\
|
||||||
|
}\
|
||||||
|
\
|
||||||
|
template < typename B, std::enable_if_t<!is_underscore_v<std::decay_t<B>>, int> = 0 >\
|
||||||
|
constexpr auto operator op (us_t, B&& b) {\
|
||||||
|
return fflip(func, std::forward<B>(b));\
|
||||||
|
}
|
||||||
|
|
||||||
|
KARI_HPP_DEFINE_UNDERSCORE_BINARY_OP(+ , std::plus<>())
|
||||||
|
KARI_HPP_DEFINE_UNDERSCORE_BINARY_OP(- , std::minus<>())
|
||||||
|
KARI_HPP_DEFINE_UNDERSCORE_BINARY_OP(* , std::multiplies<>())
|
||||||
|
KARI_HPP_DEFINE_UNDERSCORE_BINARY_OP(/ , std::divides<>())
|
||||||
|
KARI_HPP_DEFINE_UNDERSCORE_BINARY_OP(% , std::modulus<>())
|
||||||
|
|
||||||
|
KARI_HPP_DEFINE_UNDERSCORE_BINARY_OP(< , std::less<>())
|
||||||
|
KARI_HPP_DEFINE_UNDERSCORE_BINARY_OP(> , std::greater<>())
|
||||||
|
KARI_HPP_DEFINE_UNDERSCORE_BINARY_OP(<=, std::less_equal<>())
|
||||||
|
KARI_HPP_DEFINE_UNDERSCORE_BINARY_OP(>=, std::greater_equal<>())
|
||||||
|
|
||||||
|
KARI_HPP_DEFINE_UNDERSCORE_BINARY_OP(==, std::equal_to<>())
|
||||||
|
KARI_HPP_DEFINE_UNDERSCORE_BINARY_OP(!=, std::not_equal_to<>())
|
||||||
|
|
||||||
|
KARI_HPP_DEFINE_UNDERSCORE_BINARY_OP(| , std::bit_or<>())
|
||||||
|
KARI_HPP_DEFINE_UNDERSCORE_BINARY_OP(& , std::bit_and<>())
|
||||||
|
KARI_HPP_DEFINE_UNDERSCORE_BINARY_OP(^ , std::bit_xor<>())
|
||||||
|
|
||||||
|
KARI_HPP_DEFINE_UNDERSCORE_BINARY_OP(||, std::logical_or<>())
|
||||||
|
KARI_HPP_DEFINE_UNDERSCORE_BINARY_OP(&&, std::logical_and<>())
|
||||||
|
#undef KARI_HPP_DEFINE_UNDERSCORE_BINARY_OP
|
||||||
|
}
|
||||||
|
|||||||
145
untests/kari_examples.cpp
Normal file
145
untests/kari_examples.cpp
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* This file is part of the "https://github.com/BlackMATov/kari.hpp"
|
||||||
|
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||||
|
* Copyright (C) 2017-2020, by Matvey Cherevko (blackmatov@gmail.com)
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include <kari.hpp/kari.hpp>
|
||||||
|
#include "doctest/doctest.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <numeric>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
TEST_CASE("kari_examples") {
|
||||||
|
SUBCASE("Basic currying") {
|
||||||
|
using namespace kari_hpp;
|
||||||
|
|
||||||
|
auto foo = [](int a, int b, int c){
|
||||||
|
return a + b + c;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto c0 = curry(foo); // currying of `foo` function
|
||||||
|
auto c1 = c0(10); // apply to first argument
|
||||||
|
auto c2 = c1(20); // apply to second argument
|
||||||
|
auto rr = c2(12); // apply to third argument and call the `foo` function
|
||||||
|
|
||||||
|
REQUIRE(rr == 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("Partial application") {
|
||||||
|
using namespace kari_hpp;
|
||||||
|
|
||||||
|
curry_t c0 = [](int v1, int v2, int v3, int v4) {
|
||||||
|
return v1 + v2 + v3 + v4;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto c1 = c0(15, 20); // partial application of two arguments
|
||||||
|
auto rr = c1(2, 5); // partial application and call `(15,20,2,5)`
|
||||||
|
|
||||||
|
REQUIRE(rr == 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("Calling nested curried functions") {
|
||||||
|
using namespace kari_hpp;
|
||||||
|
|
||||||
|
curry_t boo = [](int a, int b) {
|
||||||
|
return a + b;
|
||||||
|
};
|
||||||
|
|
||||||
|
curry_t foo = [boo](int a, int b) {
|
||||||
|
return boo(a + b);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto c0 = foo(38,3,1);
|
||||||
|
auto c1 = foo(38,3)(1);
|
||||||
|
auto c2 = foo(38)(3,1);
|
||||||
|
|
||||||
|
REQUIRE(c0 == 42);
|
||||||
|
REQUIRE(c1 == 42);
|
||||||
|
REQUIRE(c2 == 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("Binding member functions and member objects") {
|
||||||
|
using namespace kari_hpp;
|
||||||
|
|
||||||
|
struct foo_t {
|
||||||
|
int v = 40;
|
||||||
|
int add_v(int add) {
|
||||||
|
v += add;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
} foo;
|
||||||
|
|
||||||
|
auto c0 = curry(&foo_t::add_v);
|
||||||
|
auto c1 = curry(&foo_t::v);
|
||||||
|
|
||||||
|
auto r0 = c0(std::ref(foo))(2);
|
||||||
|
auto r1 = c1(std::cref(foo));
|
||||||
|
|
||||||
|
REQUIRE(r0 == 42);
|
||||||
|
REQUIRE(r1 == 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("API/is_curried") {
|
||||||
|
using namespace kari_hpp;
|
||||||
|
|
||||||
|
constexpr auto l = [](int a, int b){
|
||||||
|
return a + b;
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr curry_t c = l;
|
||||||
|
|
||||||
|
STATIC_REQUIRE(is_curried_v<decltype(c)>);
|
||||||
|
STATIC_REQUIRE(is_curried<decltype(c)>::value);
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("Section of operators") {
|
||||||
|
using namespace kari_hpp::ext::underscore;
|
||||||
|
std::vector v{1, 2, 3, 4};
|
||||||
|
|
||||||
|
REQUIRE(std::accumulate(v.begin(), v.end(), 0, _ + _) == 10);
|
||||||
|
|
||||||
|
std::transform(v.begin(), v.end(), v.begin(), _ * 2);
|
||||||
|
REQUIRE(v == std::vector{2, 4, 6, 8});
|
||||||
|
|
||||||
|
std::transform(v.begin(), v.end(), v.begin(), - _);
|
||||||
|
REQUIRE(v == std::vector{-2, -4, -6, -8});
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("Pipe operator") {
|
||||||
|
using namespace kari_hpp::ext;
|
||||||
|
using namespace kari_hpp::ext::underscore;
|
||||||
|
|
||||||
|
constexpr auto r0 = (_*2) | (_+2) | 4; // (4 * 2) + 2 = 10
|
||||||
|
constexpr auto r1 = 4 | (_*2) | (_+2); // (4 * 2 + 2) = 10
|
||||||
|
|
||||||
|
STATIC_REQUIRE(r0 == 10);
|
||||||
|
STATIC_REQUIRE(r1 == 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("Compose operator") {
|
||||||
|
using namespace kari_hpp::ext;
|
||||||
|
using namespace kari_hpp::ext::underscore;
|
||||||
|
|
||||||
|
constexpr auto r0 = (_*2) * (_+2) * 4; // (4 + 2) * 2 = 12
|
||||||
|
constexpr auto r1 = 4 * (_*2) * (_+2); // (4 * 2 + 2) = 10
|
||||||
|
|
||||||
|
STATIC_REQUIRE(r0 == 12);
|
||||||
|
STATIC_REQUIRE(r1 == 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("Point-free style") {
|
||||||
|
using namespace kari_hpp::ext;
|
||||||
|
using namespace kari_hpp::ext::underscore;
|
||||||
|
|
||||||
|
// (. (+2)) (*2) $ 10 == 24 // haskell analog
|
||||||
|
constexpr auto r0 = (_*(_+2))(_*2) * 10;
|
||||||
|
|
||||||
|
// ((+2) .) (*2) $ 10 == 22 // haskell analog
|
||||||
|
constexpr auto r1 = ((_+2)*_)(_*2) * 10;
|
||||||
|
|
||||||
|
STATIC_REQUIRE(r0 == 24);
|
||||||
|
STATIC_REQUIRE(r1 == 22);
|
||||||
|
}
|
||||||
|
}
|
||||||
108
untests/kari_ext_tests.cpp
Normal file
108
untests/kari_ext_tests.cpp
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* This file is part of the "https://github.com/BlackMATov/kari.hpp"
|
||||||
|
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||||
|
* Copyright (C) 2017-2020, by Matvey Cherevko (blackmatov@gmail.com)
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include <kari.hpp/kari.hpp>
|
||||||
|
#include "doctest/doctest.hpp"
|
||||||
|
|
||||||
|
using namespace kari_hpp;
|
||||||
|
using namespace kari_hpp::ext;
|
||||||
|
|
||||||
|
TEST_CASE("kari_ext") {
|
||||||
|
struct box final {
|
||||||
|
int v;
|
||||||
|
constexpr box(int v): v(v) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
SUBCASE("fid") {
|
||||||
|
STATIC_REQUIRE(fid(box(10)).v == 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("fconst") {
|
||||||
|
STATIC_REQUIRE(fconst(box(10), 20).v == 10);
|
||||||
|
{
|
||||||
|
constexpr auto f = fconst(box(10));
|
||||||
|
STATIC_REQUIRE(f(20).v == 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("fflip") {
|
||||||
|
using namespace underscore;
|
||||||
|
STATIC_REQUIRE(fflip(_ - _)(10, 20) == 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("fpipe") {
|
||||||
|
using namespace underscore;
|
||||||
|
STATIC_REQUIRE(fpipe(_+2, _*2, 4) == 12);
|
||||||
|
STATIC_REQUIRE(((_+2) | (_*2) | 4) == 12);
|
||||||
|
STATIC_REQUIRE((4 | (_+2) | (_*2)) == 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("fcompose") {
|
||||||
|
using namespace underscore;
|
||||||
|
STATIC_REQUIRE(fcompose(_+2, _*2, 4) == 10);
|
||||||
|
STATIC_REQUIRE((_+2) * (_*2) * 4 == 10);
|
||||||
|
STATIC_REQUIRE(4 * (_+2) * (_*2) == 12);
|
||||||
|
{
|
||||||
|
constexpr auto s3 = [](int v1, int v2, int v3){
|
||||||
|
return v1 + v2 + v3;
|
||||||
|
};
|
||||||
|
constexpr auto c = curry(s3) * (_*2) * 10 * 20 * 30;
|
||||||
|
STATIC_REQUIRE(c == 70);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// (. (+2)) (*2) $ 10 == 24
|
||||||
|
constexpr int i = fflip(fcompose)(_+2, _*2, 10);
|
||||||
|
constexpr int j = (_*(_+2))(_*2)(10);
|
||||||
|
STATIC_REQUIRE(i == 24);
|
||||||
|
STATIC_REQUIRE(j == 24);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// ((+2) .) (*2) $ 10 == 24
|
||||||
|
constexpr int i = fcompose(_+2)(_*2, 10);
|
||||||
|
constexpr int j = ((_+2) * _)(_*2)(10);
|
||||||
|
STATIC_REQUIRE(i == 22);
|
||||||
|
STATIC_REQUIRE(j == 22);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("underscore") {
|
||||||
|
using namespace underscore;
|
||||||
|
STATIC_REQUIRE((-_)(40) == -40);
|
||||||
|
|
||||||
|
STATIC_REQUIRE((_ + 40)(2) == 42);
|
||||||
|
STATIC_REQUIRE((_ - 2)(44) == 42);
|
||||||
|
STATIC_REQUIRE((_ * 21)(2) == 42);
|
||||||
|
STATIC_REQUIRE((_ / 2)(84) == 42);
|
||||||
|
STATIC_REQUIRE((_ % 100)(142) == 42);
|
||||||
|
|
||||||
|
STATIC_REQUIRE((_ == 42)(42));
|
||||||
|
STATIC_REQUIRE((_ != 42)(40));
|
||||||
|
STATIC_REQUIRE_FALSE((_ == 42)(40));
|
||||||
|
STATIC_REQUIRE_FALSE((_ != 42)(42));
|
||||||
|
|
||||||
|
STATIC_REQUIRE((40 + _)(2) == 42);
|
||||||
|
STATIC_REQUIRE((44 - _)(2) == 42);
|
||||||
|
STATIC_REQUIRE((21 * _)(2) == 42);
|
||||||
|
STATIC_REQUIRE((84 / _)(2) == 42);
|
||||||
|
STATIC_REQUIRE((142 % _)(100) == 42);
|
||||||
|
|
||||||
|
STATIC_REQUIRE((42 == _)(42));
|
||||||
|
STATIC_REQUIRE((42 != _)(40));
|
||||||
|
STATIC_REQUIRE_FALSE((42 == _)(40));
|
||||||
|
STATIC_REQUIRE_FALSE((42 != _)(42));
|
||||||
|
|
||||||
|
STATIC_REQUIRE((_ + _)(40,2) == 42);
|
||||||
|
STATIC_REQUIRE((_ - _)(44,2) == 42);
|
||||||
|
STATIC_REQUIRE((_ * _)(21,2) == 42);
|
||||||
|
STATIC_REQUIRE((_ / _)(84,2) == 42);
|
||||||
|
STATIC_REQUIRE((_ % _)(142,100) == 42);
|
||||||
|
|
||||||
|
STATIC_REQUIRE((_ == _)(42,42));
|
||||||
|
STATIC_REQUIRE((_ != _)(42,40));
|
||||||
|
STATIC_REQUIRE_FALSE((_ == _)(42,40));
|
||||||
|
STATIC_REQUIRE_FALSE((_ != _)(42,42));
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user