Boost logo

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