Boost logo

Boost :

From: Giovanni Piero Deretta (gpderetta_at_[hidden])
Date: 2008-04-11 03:20:24


On 4/11/08, shunsuke <pstade.mb_at_[hidden]> wrote:
> Giovanni Piero Deretta wrote:
>
> >> 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.
> >
> > +1 for separate namespace. If that is controversial, +1 for typeof_ instead.
>
>
> After all, I will follow Boost.Fusion (and other Boost libraries).
> Ditto `X_` prefix.
>

the 'functional' namespace?

>
> >> Though I'm not sure what you mention,
> >> curry/bind is not can-do-everything.
> >> It must capture arguments by-copy.
> >
> > use ref/cref if you want capture by reference. In almost all my use
> > cases, it is fine to capture the object by copy (usually a function
> > object).
>
>
> Egg developement started to remove ref/bll::make_const from pipable functions.
> That's the forwarding problem!
>

I know :).

Anyways, my idea was that the '_' syntax could capture by reference by
default (i.e. only downward funargs)..

>
> > BTW, on a completely unrelated topic: it would be nice if bind/lambda
> > allowed to specify the default capture behaviour, like C++0x lambdas
> > (I think that with proto this wouldn't be hard to do).
>
>
> I tend to think the way using ref is not so bad. :-)
> `_1 < ref(m) && _2 < m` will be cumbersome in C++0x.

Not sure what you means here.
The syntax I had in mind was:

  lambda_ref[ _1 < m ]

which would be the same as:

  lambda[ _1 < ref(m) ]

or

  lambda[_1 < cref(m) ]

i.e. it would do perfect forwarding.

>
> > I was imprecise:
> > foo(bar(_,_))
> >
> > Actually corresponds to:
> >
> > lazy(foo)(protect(lazy(bar)(_1, _2))) (); // we are calling it!
> >
> > The point is that unlike lambda expressions, '_' does not cause the
> > full expression to be a lazy expression (up to the protect)
> > I'll try to be more clear later.
>
>
> I perhaps understand what you mention.
> You are trying to express *un*lambdification by using `_`.
> Am I right?

hum. I think that the authors of fc++ can explain it way better than I can:

  http://www.cc.gatech.edu/~yannis/fc++/currying.html

>
> I tend to think it should be expressed by using nesting-levels.
> E.g. `foo(bar(_,_))` can be translated into
> `nest1(foo)(nest2(bar)(_1_(_1), _1_(_2)))` // \() -> foo(\(x,y)->bar(x,y))
> (or a better syntax sugar.)

hargh! :P parse error!

>
> >> I'm still lost.
> >> Why not `lazy_ex(is_less_non_curriable)(_, 10)(7)` ?
> >> (Assume `lazy_ex` supports "non-numbered placeholder".)
> >
> > Almost the same, except that you would need to wrap the call to
> > lazy_ex around protect(). If lazy_ex were to apply protect implicitly
> > would be exaclty the same. BTW, I do not consider non-numbered
> > placeholders necessary, I was just mimicking FC++ syntax.
> >
> > The protect is important, you do not need the full power of lambda
> > (ie.e function composition) , so you want to stop 'lambdification'
> > immediately. This is why a better name would be a lazy_simple :)
>
>
> I think only the outmost lazy function knows when *un*lambdification should be performed.

I think that I still I haven't explained my self: you need to unlambda
the immediate expression that uses placeholders. I want no Lambda, I
just want to express simple generalized a-la FC++ currying using what
just I have: boost.lambda.

> BTW, bll::unlambda should be used instead of protect?

Yes! have never understood exactly what protect is useful for. I,
wrongly, use often protect when I mean unlambda. In this case I
definitely meant unlambda!

>
>
> >> 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.)
> >
> > Uh? Why? doens't your result_of work seamlessy with both the sig and
> > result_of protocol? where is the problem?
>
>
> After some thoughts,
> "If has_sig_template is true, use Lambda protocol.
> Otherwise, use ResultOf protocol."
> may be a good solution.

This is what I do in my result_of replacement and so far has worked flawlessy.

> (But I'm not sure it is a better way to apply a heavy patch into a stable library.)

This is a much needed one!

>
> > Never tried it, but I'm quite sure that the phoenix lambda syntax also
> > let you return lambdas:
> >
> > auto always = lambda[ lambda(_a = _1)[ _1 ] ];
> >
> > auto always_10 = always(10);
> >
> > assert(alsways_10() == 10);
> >
> > should work (modulo modifying phoenix to work with rvalues).
> > Disclaimer: I haven't tryed it! But even if it doesn't work with
> > phoenix, it shouldn't be terribly hard to make this syntax work.
>
>
> I expect Proto-professionals will invent cool syntax for nestN. :-)

for me nestN is completely opaque. I should sit down and trace step by
step what it does. I'll probably have one of those 'A-ah!' moments :).

Another thing that bothers me with nestN is that it seems that there
is an hard coded limit (i.e. the max N in _N_) on the number of
nesting levels and also you have to specify the nesting level
explictly instead of being implicit in the expression.

> Probably nestN should sleep until the day will come.

Agree.

> FWIW, nestN can be used with result_of,
> because its expression contains only function-calls.

You can express nested lambdas using curry.
[I will keep using both the lambda[] syntax and optionally lazy
functions because I find it more readable, just substitute it with
'unlambda' and apply magic 'lazy' pixie dust where necessary :)]

let's define (in pseduo c++0x syntax var args and new function syntax).

  template<class F, class C>
  struct closure1 {
     F f;
     C c;

    template<typename Args...>
    auto operator ()(Args... args) -> decltype(f(c, args...)) {
         return f(c, args...);
    }

  };

  // this is actually a function object
  template<class F, class C>
  closure<F, C>
  close1(F f, C c) {
     closure<F, C> result = { f, c};
     return result;
  }

Now you can express this
> auto always = lambda[ lambda(_a = _1 + 7)[ _1 ] ];

as:

  auto always = lambda[ close1(lambda[ arg1 ], arg1) ];

or, obviously,

  // another function object
  template<class F>
  auto curry1(F f) -> decltype(lambda[ close1(f, _1) ]) {
      return lambda[ close1(f, _1) ];
  }

  auto always = curry1( lambda[arg1] );

or, just for the heck of it:

  auto always = lambda[ curry1(lambda[arg1])(arg1) ]; // needlessy convoluted

  // more fun!
  auto always_the_sum = lambda[ curry1(lambda[arg1])(arg1 + arg2) ];

  auto always_10 = always_the_sum(4, 6);

  assert(always_10() == 10);

(functional programming is fun)
BTW I've implemented the above with just an hour of work with lambda
adaptors (with plain c++03) and it worked! The best thing is it is
completely result_of compatible (you can express the type 'always'
with result_of).

Anyways, all of this is just for toying with c++ and not really
relevant with egg.

You have clarified all my doubts on your library . I hope to write the
final review done for tonight (spoiler: accepted modulo a mini review
only for the documentation).

-- 
gpd

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk