Giter Site home page Giter Site logo

boostorg / hof Goto Github PK

View Code? Open in Web Editor NEW
505.0 40.0 96.0 2.32 MB

Higher-order functions for c++

Home Page: http://boost-hof.readthedocs.io/

License: Other

C++ 96.24% CMake 1.77% HTML 0.08% Shell 1.19% Starlark 0.72%
modern constexpr cplusplus lambda functional c-plus-plus cplusplus-11 cplusplus-14 cpp cpp11 cpp14 functional-programming

hof's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

hof's Issues

apply - what is the utility of apply?

Oh, I believe I have found why I don't understand. The template parameters TS are not part of the function apply
This

template<class F, class... Ts>
constexpr auto apply(F&& f, Ts&&... xs);

should be

template<class F>
constexpr auto apply(F&& f);

The semantics given us

assert(apply(f)(xs...) == f(xs...));

But the usage is incompatible

assert(fit::apply(sum_f(), 1, 2) == 3);

that I believe should be

assert(fit::apply(sum_f())(1, 2) == 3);

Unfortunately the implementation is not inline at all.

Not clear how to make a function object pipable

It is not clear if the two previous lines are needed to make sum pipable.

FIT_STATIC_FUNCTION(sum) = sum_f();

and

const constexpr pipable_adaptor<sum_f> sum = {};

It would be great to have a link to the complete example in the example/tutorial directory.

BTW, why const constexpr?

Constexpr in `left/right/partial_ap` Seems to Break Placeholder for Pipable Objects

Hi, first of all, thanks so much for your speedy response on my previous issue!

It seems like the newest commit 65c8d3f broke the following code that tries to pass expression made by placeholders as a function object to a pipable object.

Removing the constexpr from the constructor and operator () in left, right, and partial_ap seems to fix it.

The code:

    vector<int> vectorValues = { 1, 2, 3, 4, 5 };
    auto filter =
        pipable(
            [] (auto input, auto pred) {
                decltype(input) output(input.size());
                output.erase(
                    ::std::copy_if(
                        ::std::begin(input),
                        ::std::end(input),
                        ::std::begin(output),
                        pred
                    ),
                    ::std::end(output)
                );
                return output;
            }
        );

    vectorValues
        | filter(_1 > 1);

I get the following error message on Clang3.5

./fit/placeholders.h:245:40: error: call to implicitly-deleted copy constructor of 'fit::detail::pipe_closure<(lambda at test.cpp:103:13), fit::detail::pack_base<fit::detail::seq<0>,
      fit::detail::lazy_invoker<fit::operators::greater_than, fit::detail::pack_base<fit::detail::seq<0, 1>, fit::detail::simple_placeholder<1>, int> > &&> >'
    constexpr partial_ap(const T& x) : val(x)
                                       ^   ~
./fit/placeholders.h:294:23: note: in instantiation of member function 'fit::detail::unamed_placeholder::partial_ap<fit::detail::pipe_closure<(lambda at test.cpp:103:13),
      fit::detail::pack_base<fit::detail::seq<0>, fit::detail::lazy_invoker<fit::operators::greater_than, fit::detail::pack_base<fit::detail::seq<0, 1>, fit::detail::simple_placeholder<1>, int> > &&> >,
      fit::detail::unamed_placeholder::right<fit::operators::bit_or> >::partial_ap' requested here
FIT_FOREACH_BINARY_OP(FIT_UNAMED_PLACEHOLDER_BINARY_OP)

Do we need to know explicitly the result of the adaptors?

I'm wondering if we need to name the result of the adaptors, or just say that it is an unspecified function with a specific signature that behaves as ....

What is the advantage to giving it a name?

Giving a name to the result type would imply to add the description of all of its operations.

Consider supporting arbitrary Callables instead of FunctionObjects

Most adaptors require FunctionObjects. Is there any reason not to allow arbitrary Callables, and to use something like std::invoke to call the functions instead? That would be slightly more flexible, as one could use member pointers & al instead of regular function objects. However, there's probably a non-negligible compile-time penalty if this is supported everywhere in the library.

construct - template cannot be MoveConstructible

It is not clear from the description what would be the result of

auto x = construct<vector>()(1);

I have a make function that can also take a type-constructor so that we can

auto x = make<expected<_, error_code>>(1);
auto x = make<unique_ptr<_, MyDeleter>>()(1);

Of course, we need some kind of customization from the user.

Unnamed Placeholder Not Working In A Boolean Expression

Hi, when I tried the unnamed placeholder in your library in the following expression:

auto comp = _ > 1;
assert(comp(2) == true);
assert(comp(1) == false);

I get false for both the comparisons.

Replacing _ with the normal placeholder _1 would return true for comp(2) as intended. Expressions that produce a value such as _ + _ and _ * 10 also work as intended.

I looked into the placeholder code, but couldn't really figure out why the boolean expression didn't work as intended. Could you please take a look?

oveload sets are functions having hetegeneous domain and co-domain

It is not clear from the documentation that your library can consider an oveload set as a function
and that match and conditional are overload set factories and that their result can be considered as functions arguments for the other functions. I believe that this is very important.

I suggest to use signatures to describe oveloads sets as e.g.

(T1->U1 T2->U2)

could represent an overloaded set that can transform a T1 on a U1 and a T2 on a U2.

The signature of by could be for the binary case

by :: ( (T1->U1 T2->U2)  (U1 U2 -> V) ) -> ((T1  T2) -> V) 

The documentation must state this clearly.

differences between partial and decorate

I don't see the differences between the two function

assert(decorate(f)(x)(g)(xs...) == f(x, g, xs...));

assert(partial(f)(xs...)(ys...) == f(xs..., ys...));

implicit semantic is not clear enough

implicit is not an adaptor as the other as it is a class not a function.

I would like to have in the documentation a more comprehensive description of how implicit behaves.

Is something like that

implicit<TC>(xs....).operator X() <==> TC<X>(xs...).operator X()

[if_] Clarify the documentation

The documentation of if_ is not clear to me. It says

The if_ function decorator applies a boolen condition to the function.

However, it seems to me like what it does is make the function callable only when the predicate is satisfied, or something like that. Also, there's a typo in the above quote (boolen).

compress versus fold

There are two variants of fold, left associative versus right.

What about naming them fold_left and fold_right?

Given an adaptor that results in a variadic function as compress

compress:: F A -> (B.... -> A)

we could define it by using a function having a tuple as parameter

compress(f, z)(xs...) = fold(f, z, forward_as_tuple(xs...))

I believe that we have already this kind of fold fuctions in Hana.

So given

fold: F A H -> A 

partial(fold, F, A) would almost behave like compress except that partial(fold, F A) takes a tuple as parameter and compress takes a variadic number of arguments. So we can see compress something like

compress(f, a) = compose(partial(fold, f, a), forward_as_tuple)

I'm not saying that compress is not useful, even if the name has not too much relation to what is behind the scenes. I would say that it could be a useful shortcut when the user doesn't has already a sequence or a tuple.

Of course, in C++ we don't want to pay more than needed, so the implementation should be more efficient than using the right hand side.

It would be fanny to have some performance measures. This is micro-benchmarking, but having the figures would help the user to understand the trade-offs.

test/by.cpp fails to compile on OS X

$make by
[100%] Building CXX object CMakeFiles/by.dir/test/by.cpp.o
fit/test/by.cpp:24:20: error: no matching function for call to object of type
      'by_adaptor<std::__1::__mem_fn<int foo::*>, fit::operators::add>'
    FIT_TEST_CHECK(fit::by(std::mem_fn(&foo::x), add)(foo(1), foo(2)) == 3);
                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
fit/test/test.h:66:35: note: expanded from macro 'FIT_TEST_CHECK'
#define FIT_TEST_CHECK(...) if (!(__VA_ARGS__)) std::cout << "*****FAILED: " << #__VA_ARGS__ << "@" << __FILE__ << ": " << __LINE__ ...
                                  ^
fit/./fit/by.h:86:20: note: candidate template ignored: substitution failure [with Ts = <foo, foo>]: no
      matching function for call to object of type 'const std::__1::__mem_fn<int foo::*>'
    constexpr auto operator()(Ts&&... xs) const FIT_RETURNS
                   ^
1 error generated.
make[3]: *** [CMakeFiles/by.dir/test/by.cpp.o] Error 1
make[2]: *** [CMakeFiles/by.dir/all] Error 2
make[1]: *** [CMakeFiles/by.dir/rule] Error 2
make: *** [by] Error 2

Compiler:
Apple LLVM version 6.0 (clang-600.0.56) (based on LLVM 3.5svn)
Target: x86_64-apple-darwin14.1.0
Thread model: posix

indirect - what happens if the parameter is nullptr

unique_ptr<sum> psum;
int r = indirect(psum)(3,2);

It seems that there is a precondition missing. It should be more explicit the operation used on the parameter, in this case operator*() I guess.

Applicatives have a similar and safer function that returns another Applicative having the same type-constructor.

(<*>) :: f (a -> b) -> f a -> f b

if f is also a Monad <*> is equal to ap

auto r = ap(psum, make_unique(3,2)); // decltype x is unique_ptr<int>

As defined, the parameter should be a model of a PossiblyNull or PossyblyValued concept supporting the dereference function.

Use .hpp extension for headers

Boost normally uses the .hpp extension for its headers. It would be nice to make sure that Fit follows that, in preparation for the review.

capture versus partial

capture(xs...)(f)(ys...) <==> f(xs..., ys...)

Another way is to define it as

capture(xs...)(f) <==> partial(f, xs...)

Is there a difference in performances, between the 3 expressions?

capture(xs...)(f)(ys...) <==> f(xs..., ys...) <==> partial(f, xs...)(ys...)

BTW, the example would clearer if you use different number

assert(f(5, 8) == std::make_tuple(std::make_pair(1, 5), std::make_pair(2, 8)));

or even different types.

Consider dropping the "dependent typing" term

As we discussed at CppCon, it might be a good idea to stop using the term dependent typing in the documentation in favor of another word which is not already used in math/functional programming.

Add a section describing how the reference documentation is structured

I believe that it is important for the user to know how the specification of the reference section is structured. I will add a section to describe it.

I like the way the semantic is given. This is almost the equivalent as the as if on the standard

assert(by(p, f)(xs...) == f(p(xs)...));

The signature of by seems to be

by :: ( (T->U)  (V -> W) ) -> ((X...) -> W) 
by :: p f -> xs... -> w) 

where U is convertible to V and X is convertible to T.

But I believe that it supports more than that, I don't believe the library is forcing all types in X... to be the same.

I like the way the semantic is given

assert(by(p, f)(xs...) == f(p(xs)...));

p(xs) must be well formed, p should have an overload taking a type convertible from decltype(xs) for each xs.

f(p(xs)...)) must be well formed, so f should have an overload taking types V... convertible from decltype(p(xs))...
This means that p

Wouldn't the by function has a signature something like

by :: ( ((T->U)...)  (V... -> W) ) -> ((X...) -> W) 

Are there any constraints on the types T..,U..., V..., X..., W? Must all these types by CopyConstructibles?
What the library can control at compile time?

E.g. would the following compile ?

using foo = pair<int, string>;
using bar = pair<string, float>;
int baz(int, float);

foo a; 
bar b;
//...
auto x = fit::by(overload(std::mem_fn(&foo::first, std::mem_fn(&bar::second), baz)(a, b);
``

Variadic signatures are difficult to follow. Giving a specific case will surely help

by :: ( (T1->U1 T2->U2) (V1 V2 -> W) ) -> ((X1 X2) -> W)
requires convertible<Xi, Ti> && convertible<Ui, Vi>


Could the functions be only movable?

Is the result type of the function copyable, movable, .... can be stored statically at compile time, and with the help of which macro?

fit::pipable parameter order

Hello,

First of all, thanks for your library. Functional Programming with C++ is possible thanks to your work.

I have played with your Fit library and encounter a little problem

Let say I define the following filter_ method.

struct filter_ {
  template<class L, class F>
    L operator()(F f, L l) const {
      L result;
      for (auto& item : l)
        if (f(item))
          result.push_back(item);
      return result;
    }
};

I can use it with partial

auto filter = fit::partial(filter_());

In order to some filtering:

auto odd = fit::_1 % 2;

assert(filter(odd)(std::list<int>({1, 2, 3, 4, 5})) == {1, 3, 5});

But if I want to reuse my filter_ method with fit::pipable, I have to rewrite it just to exchange the order of the parameters:

// do not compile because of params that are not in expected order (list first, method second)
std::list<int>({1, 2, 3, 4, 5}) | fit::pipable(filter_())(odd); 

Do I miss something ? Is it possible to adapt fit::pipable so that we can use it with same function object as fit::partial, fit::fuse ...

Have a nice day

Bertrand

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.