
Giovanni Piero Deretta wrote:
(As you pointed,) Those macros are unneeded in the case of stateless functions. So, I propose a new `static_` form:
The adapting form in review version: result_of_curry2<F>::type const c = BOOST_EGG_CURRY2({}); // stateless is changed to a new `static_` form *without macros*: static_< result_of<T_curry2(F)> >::type const c = {{}};
How do you think about this new form?
Much better, but I do not particulary like the static_ name (expecially because of the trailing '_'). Statically_initialized is too long, so if you can't come up with anything better and shorter, I can live with static.
Ok.
Btw, can you explain me (again, please :P) why
result_of<T_curry2(F)> >::type const c = {{{}};
isn't enough? I guess I should see static_ implementation.
Because, in general, a higher-order function must support stateful functions. `static_` makes a default-constructed function object on-the-fly and forwards arguments to it. ( I call it "staticalization". ) Thus, `static_` can offer the static-initialization.
What about collapsing static_ and result_of in a single class, and allowing arbitrary composition:
apply<compose, apply<compose, my_fun1, my_fun2>, my_fun3>::type c = {{}};
The apply name is already taken by MPL, but maybe something of the sort.
I chose to minimize the set of names. static_< result_of<compose(result_of<compose(my_fun1, my_fun2)>::type, my_fun3)> >::type const c = {{}}; is enough. Users don't need to remember yet another metafunction `apply`. (result_of expression is a little combersome, though.) If you need static-initialization, wrap a result_of expression by `static_`. Isn't it simple? Also, it is only the outmost metafunction(i.e. static_) that knows when "staticalization" should be performed. Otherwise, all the metafunction have to perform "staticalization", which is wasteful.
The biggest problem is of course is that gcc fails to compile this:
namespace a {
struct foo_t {};
struct bar { }; };
void bar(...);
int main() { a::foo_t foo;
bar(foo); }
I.e. ADL finds even non callable entities! Comeau online compiles it cleanly.
I know this problem, which annoyed Boost.MPL. The problem is that `bar` is not a FunctionObject.
- "Binding functions"
"boost::result_of isn't used for now". Why?
Because of a bug described in the `egg::result_of_` section.
Ah, now understand why I'm missing some documentation details! the documentation doesn't always clearly show which words are links. for example in my browser result_of_ is not visually distinguished from the rest of the paragraph. I do not know if it is a problem of my browser of you need to fix your CSS.
I'm using CSS of boost. I hope it will be updated.
Also I do not like the T_* convention to name function types. I liked best the old op_* convention.
LexicallyTypedObject concept is not restricted to FunctionObjects. "T_" is short of "typeof". This concept is a workaround for "missig decltype" in C++03.
What about using the prefix 'typeof_' or a postfix '_type'? I really dislike that uppercase T. Maybe even a postfix _t would be more than enough.
I prefer prefix, because result_of expression and function-call expression are in sync: result_of<T_plus(int, int)>::type r = plus(1, 2); I thought 'typeof_' was too long. Anyway we should decide by majority vote.
- Getting initializers
"As mentioned before, the static initialization is important". Why? You still do not explain it.
Click "before".
Right, missed the link. Probably this part should go in an advanced section or at least an had-hoc section on static initialization. Certainlin not in the quick start.
Ok.
always_result means a return type of always.
sure, but it also contains the body of 'always' (i.e. the call member), so it is a misleading name.
return_type_of_always might be better.
nestN returns a function which returns a function which returns a function... Probably I should change the notation into C++0x lambda expression?
That would probably help a little. Much more prose (like a step by step analisys of what that expression means) would be much better.
Ok.
Even better would be to segregate function object types to their own namespace, a-la fusion or proto. (i.e. the functional namespace).
See this:
namespace poost { namespace op { struct foo {}; }
op::foo const foo = {};
namespace nested { namespace op { struct bar {}; } op::bar const bar = {};
void test() { op::foo my_foo = foo; // doesn't compile } } }
Thus, I've rejected segregated-namespace-way.
uh? ... void test() { poost::op::foo my_foo = foo; // it compiles! }
Seems a very simple fix (and arguably the right thing in the first place!)
A result_of expression becomes long. This is a decision from my experince.
Making a function object pipable should just be a matter of deriving it from an (empty) egg::pipable, which would manifest the intention of the object to participate in pipelines.
I'm not sure it has an advantage over higer-order functions.
Less intellectual overhead :). I need to know less things to appreciate what 'pipable' means. Concept should be as simple as possible IMHO. (you can of course add "... but not simpler" :) )
I want to define PipableFunctionObject without dependency on Egg library.
I meant Egg of course. What I wanted to say is:
Egg provides a templated operator| in egg namespace. There is a single implementation for this operator and is:
namespace pibable_ {
struct pipable {}; // ADL hook
template<typename Lhs, typename Rhs> typename boost::lazy_enable_if<bost::is_base_and_derived<pipable, Rhs>, boost::result_of<Rhs(Lhs&)> >::type operator|(Lhs& lhs, Rhs rhs) { return rhs(lhs); }
template<typename Lhs, typename Rhs> typename boost::lazy_enable_if<bost::is_base_and_derived<pipable, Rhs>, boost::result_of<Rhs(const Lhs&)> >::type operator|(const Lhs& lhs, Rhs rhs) { return rhs(lhs); } }
Any unary function object that wants to model the pipable concept must derive from pipable_::pipable. This will trigger ADL and will find the operator | when necessary. (In c++0x of course you would put operator| in the global namespace and make it a template function constrained on the (non auto) concept Pipable)
I can't always use inheritance -- for stateful static initialization. Also, how can we make `_1+_2` pipable? A higher-order function is needed, after all.
This of course only work with unary callable entities. to make a non-unary entity unary, apply the appropriate curry/bind grease :). This will also get rid of the need for Ambi.
Though I'm not sure what you mention, curry/bind is not can-do-everything. It must capture arguments by-copy.
As an extension (I'm not proposing it, it is just for the sake of discussion), '|' could be a synonym for compose (IIRC it is spelled '$' in haskell):
A PipableFunctionObject supports '$' using `operator|=`. It might be different what you expect, though.
if lhs and rhs are callable entites (in practice you detect pipability): a | b is the same as: compose(b, a);
else if only b is a callable entity: a | b is the same as compose(b, always(a));
else the expression is illegal.
[IIRC in haskel values are in practice treated as nullary functions, so the use of 'always' here would mimic the functional comunity usage]
IMO, this seems to introduce unneeded complexity. "If 'a' is callable,... else if 'b' is pipable,... else if 'c' is a placeholder..." seems a bad switch statement.
This means that you can create pipelines of transforms:
map(my_range, my_first_view|my_second_view)
Would be the same as:
map(my_range, compose(my_second_view, my_first_view));
I understand it.
or
map(my_range, my_first_view) | protect(lazy(_1, my_second_view))
Sorry, I couldn't understand this expression.
Sorry, I couldn't understand this proposal. What does `x | foo(_, 10, "bar")` mean?
hum, let me see, in egg syntax it should be:
compose(lazy(foo)(_1, 10, "bar), always(x))
I understand it.
but see below:
Also, f(_, 10, _)("baz", "bar"); seems supported by egg::lazy.
yes, the only difference is that the result is not a lambda expression (as if there was an implicit protect): see the difference between:
lazy(foo)(lazy(bar)(_1, _2)); // ll::bind(foo, ll::bind(bar, _1))
I understand it.
and:
lazy(foo)(protect(lazy(bar)(_1, _2))); // ll::bind(foo, protect(ll::bind(bar, _1)))
with my syntax (actually this is lifted directly from the generalized currying in FC++), you could spell the latter:
foo(bar(_,_)); // s
I'm lost. How can I use this `foo(bar(_,_))` ?
this is important if egg were (as one would expect) to register its operator| with boost::lambda:
map(my_range, _1 | lazy(is_less)(_1, 10)); // does not work!
map(my_range, _1 | protect(lazy(is_less)(_1, 10))) ; //ok
map(my_range, _1 | is_less(_, 10)); // also ok
I'm lost again. Is `_1 | lazy(is_less)(_1, 10)` translated into `compose(lazy(is_less)(_1, 10), _1)` ?
A nice name for the generalized curry operation is of course curry:
BTW, strictly speaking, this is not a currying. This is a partial application.
auto is_less = curry(is_less_non_curriable);
assert( is_less(_, 10)(7) == true );
This is trivially implementable with lazy + protect, but an ah-hoc implementation might be simpler and easier on the compiler (no need for full blown lambda support). Also, i spell the missing parameters '_' because that's what FC++ used, 'deferred' might also be a good name.
What do you think?
I'm still lost. Why not `lazy_ex(is_less_non_curriable)(_, 10)(7)` ? (Assume `lazy_ex` supports "non-numbered placeholder".)
- Major Function Object: this name is not descriptive. Make it Lambda Compatible Polymorphic Function Object.
A specific name should not be contained in this concept name so that MajorFunctionObject can support yet another lambda library.u
Hum, the support hook shuld only be boost::result_of and its protocol.
Or better, modify boost.lambda so that any Polymorphic function object work with it (shouldn't be hard), so that we do not need an ad-hoc concept.
It seems hard.
I do not think so, in fact I think that there are patches around. On the other hand, I'm not the one doing it, so I shouldn't complain.
IIRC, Daniel Walker was trying it. But, strictly speaking, it seems impossible to detect whether a FunctionObject is ResultOf conforming or Lambda conforming. We need C++0x Concept. (has_sig_template or has_result_template is not enough.)
- Static Function Object: again, you are mixing a concept, with pertains types, with restriction on object usages or initialization. I do not think that you can encode the fact that 'f' is statically initialized in a concept. Also, this 'concept' badly need a rationale!
I'm going to modify this concept. The "stateless" requirement will be added, and the static-initialization guarantee will be removed. Do you think it is right?
I do not know if it is right, but I have only needed static initialization for stateless function objects. Others might have different experiences (and they should better speak now ;) ).
Ok.
=== Function builders
- function: finally we find the function class! I object in another class in boost called function. What about function_adaptor?
This is not an adaptor.
Hum, IMHO,yes. It is just adapt a class from the internal Minor function object convention to the external Major function object.
IIRC, Boost.Phoenix also uses "function".
Yes, I saw it too. But Phoenix is not a first class boost library yet :). when it will be reviewed, I'll make the same comment.
We have namespaces so that it should be ok. :-)
I rarely use Boost.Bind and Boost.Lambda in production code.
Why not? I do all the time.
I don't need a hammer to break an egg. :-)
- implicit. "Implicit adds implicit conversion support to a cast from function". What does that means? What are the use cases?
It automatically passes initialized types to cast functions.
could you provide an example?
std::string str = lexical(20); is translated into: std::string str = X_lexical_cast<std::string>()(20); is translated into: std::string str = lexical_cast<std::string>(20); The target type std::string is automagically passed to X_lexical_cast<>. So, you don't need to specify a target type.
I couldn't find boost.bind apply_visitor.
The actual name is visit_each (which I think is the protocol used by apply_visitor). There is some reference in bind/storage.hpp. The actual api is not documented, but I guess that the intent is that boost::visit_each(vistitor, bind-expression); should iterate on all arguments closed in the bind expression. Searching the boost mailing list reveals that bind_visitor has been broken for a while. I do not know if it is still true (I have never used it - yet).
It seems challenging. E.g. Making a function adapting expression into Proto tree expression!
If reviewers regard it as an inadequate component, I will simply remove nestN from Egg. BTW, how can we write nested lambda using phoenix?
lambda[ for_each(_1, lambda(_a = _1)[ front(_a) += _1] )(range)
[at least It should work, given appropriate definitions of for_each and front]
It seems different motivation. nestN returns a function which returns a function which returns a.... e.g. nest3(....)(1)(2,3)(4);
bll_N is a model of LexicallyTypedObject so that you don't need to remember the name "placeholderN_type".
so now I have to remember two types instead of one :)
You have to remember placeholder1_type is const-qualified.
- construct_braced{1,2}. Eh? what is this for?
- construct_variadic. Same as above.
egg::generator needs this to initialize pod types.
Is it a general purpose utility of just an implementation detail?
I want egg::generator to support POD types.
Unless lambda registers its types of course. Anyways, It would be enough for me if this worked:
BOOST_AUTO(foobar, compose(foo, bar));
I will consider Boost.Typeof support. Regards, -- Shunsuke Sogame