From 50fd7b711144ce9b8728fb48d0e81b4909b16851 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Fri, 11 Dec 2020 04:51:38 +0700 Subject: [PATCH] update README --- README.md | 124 ++++++++++++++------------------- untests/kari_examples.cpp | 143 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+), 72 deletions(-) create mode 100644 untests/kari_examples.cpp diff --git a/README.md b/README.md index 0484f99..8264bb6 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,8 @@ target_link_libraries(your_project_target kari.hpp) ### Basic currying ```cpp +using namespace kari_hpp; + auto foo = [](int v1, int v2, int v3) { return v1 + v2 + v3; }; @@ -68,13 +70,14 @@ std::cout << rr << std::endl; ### Partial application of curried functions ```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; }; -auto c0 = curry(foo); // currying auto c1 = c0(15, 20); // partial application of two arguments -auto rr = c1(2, 5); // partial application and call `foo(15,20,2,5)` +auto rr = c1(2, 5); // partial application and call `(15,20,2,5)` // output: 42 std::cout << rr << std::endl; @@ -83,17 +86,19 @@ std::cout << rr << std::endl; ### Calling nested curried functions ```cpp -auto boo = [](int v1, int v2) { - return v1 + v2; +using namespace kari_hpp; + +curry_t boo = [](int a, int b) { + return a + b; }; -auto foo = [boo](int v1, int v2) { - return curry(boo, v1 + v2); +curry_t foo = [boo](int a, int b) { + return boo(a + b); }; -auto c0 = curry(foo)(38,3,1); -auto c1 = curry(foo)(38,3)(1); -auto c2 = curry(foo)(38)(3,1); +auto c0 = foo(38,3,1); +auto c1 = foo(38,3)(1); +auto c2 = foo(38)(3,1); // output: 42,42,42 std::cout << c0 << "," << c1 << "," << c2 << std::endl; @@ -102,19 +107,21 @@ std::cout << c0 << "," << c1 << "," << c2 << std::endl; ### Binding member functions and member objects ```cpp -struct Foo { +using namespace kari_hpp; + +struct foo_t { int v = 40; - int addV(int add) { + int add_v(int add) { v += add; return v; } } foo; -auto c0 = curry(&Foo::addV); -auto c1 = curry(&Foo::v); +auto c0 = curry(&foo_t::add_v); +auto c1 = curry(&foo_t::v); auto r0 = c0(std::ref(foo))(2); -auto r1 = c1(foo); +auto r1 = c1(std::cref(foo)); // output: 42,42 std::cout << r0 << "," << r1 << std::endl; @@ -125,13 +132,7 @@ std::cout << r0 << "," << r1 << std::endl; ```cpp namespace kari_hpp { template < typename F, typename... Args > - constexpr decltype(auto) curry(F&& f, Args&&... args) const; - - 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; + constexpr auto curry(F&& f, Args&&... args); template < typename F > struct is_curried; @@ -139,10 +140,12 @@ namespace kari_hpp { template < typename F > inline constexpr bool is_curried_v = is_curried::value; - template < std::size_t N, typename F, typename... Args > - struct curry_t { + template < typename F, typename... Args > + class curry_t { + constexpr curry_t(F f); + template < typename... As > - constexpr decltype(auto) operator()(As&&... as) const; + constexpr auto operator()(As&&... as) const; }; } ``` @@ -155,43 +158,16 @@ Returns a curried function **`f`** or copy the function result with **`args`** a --- -### `kari_hpp::curryV(F&& f, Args&&... args)` - -Allows carrying variadic functions. - -```cpp -auto c0 = 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_hpp::curryN(F&& f, Args&&... args)` - -Allows carrying variadic functions for **`N`** arguments. - -```cpp -char buffer[256] = {'\0'}; -auto c = curryN<3>(std::snprintf, buffer, 256, "%d + %d = %d"); -c(37, 5, 42); -std::cout << buffer << std::endl; // output: 37 + 5 = 42 -``` - ---- - ### `kari_hpp::is_curried, kari_hpp::is_curried_v` Checks whether F is a curried function type. ```cpp -auto l = [](int v1, int v2){ - return v1 + v2; +using namespace kari_hpp; + +constexpr curry_t c = [](int a, int b){ + return a + b; }; -auto c = curry(l); // output: is `l` curried? no std::cout @@ -213,13 +189,14 @@ std::cout 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 -int foo(int v1, int v2, int v3, int v4) { - return v1 + v2 + v3 + v4; -} +using namespace kari_hpp; -auto c0 = curry(foo); // currying -auto c1 = c0(15, 20); // partial application -auto rr = c2(2, 5); // function call - foo(15,20,2,5) +curry_t c0 = [](int a, int b, int c, int d) { + return a + b + c + d; +}; + +auto c1 = c0(15, 20); // partial application +auto rr = c2(2, 5); // function call - foo(15,20,2,5) // output: 42 std::cout << rr << std::endl; @@ -232,17 +209,17 @@ std::cout << rr << std::endl; ### Section of operators ```cpp -using namespace underscore; -std::vector v{1,2,3,4}; +using namespace kari_hpp::ext::underscore; +std::vector v{1, 2, 3, 4}; // result: 10 -std::accumulate(v.begin(), v.end(), 0, _+_); +std::accumulate(v.begin(), v.end(), 0, _ + _); -// v = 2, 3, 6, 8 -std::transform(v.begin(), v.end(), v.begin(), _*2); +// v = {2, 4, 6, 8} +std::transform(v.begin(), v.end(), v.begin(), _ * 2); -// v = -2,-3,-6,-8 -std::transform(v.begin(), v.end(), v.begin(), -_); +// v = {-2, -4, -6, -8} +std::transform(v.begin(), v.end(), v.begin(), - _); ``` ### Function composition @@ -250,7 +227,8 @@ std::transform(v.begin(), v.end(), v.begin(), -_); #### Pipe operator ```cpp -using namespace underscore; +using namespace kari_hpp::ext; +using namespace kari_hpp::ext::underscore; auto r0 = (_*2) | (_+2) | 4; // (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 ```cpp -using namespace underscore; +using namespace kari_hpp::ext; +using namespace kari_hpp::ext::underscore; auto r0 = (_*2) * (_+2) * 4; // (4 + 2) * 2 = 12 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 ```cpp -using namespace underscore; +using namespace kari_hpp::ext; +using namespace kari_hpp::ext::underscore; // (. (+2)) (*2) $ 10 == 24 // haskell analog auto r0 = (_*(_+2))(_*2) * 10; diff --git a/untests/kari_examples.cpp b/untests/kari_examples.cpp new file mode 100644 index 0000000..02db4ef --- /dev/null +++ b/untests/kari_examples.cpp @@ -0,0 +1,143 @@ +/******************************************************************************* + * 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 +#include "doctest/doctest.hpp" + +#include +#include +#include + +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 curry_t c = [](int a, int b){ + return a + b; + }; + + STATIC_REQUIRE(is_curried_v); + STATIC_REQUIRE(is_curried::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); + } +}