|
Boost : |
From: shunsuke (pstade.mb_at_[hidden])
Date: 2008-04-09 20:32:45
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
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk