Boost logo

Boost :

From: Giovanni Piero Deretta (gpderetta_at_[hidden])
Date: 2008-04-11 10:46:51


On Fri, Apr 11, 2008 at 12:45 PM, shunsuke <pstade.mb_at_[hidden]> wrote:
> Giovanni Piero Deretta wrote:
> >> 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 hear that
>
> `_1 < ref(m) && _2 < m`
> will be:
> int m_ = m;
> [&m, m_](int a, int b){ return a < m && b < m_ };
> in C++0x.
> `m_` is needed to copy.
> IMO, placeholder expression seems more beautiful than C++0x expression. :-p

For simple expressions, definitely!
Also, I have lost hope of C++0x lambdas being useful when they dropped
polymorphic lambdas. May be a future revision...

>
> >> 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.
>
> FC++ currying slightly confuse me.
> Again, that is not currying. :-(

I have read many different definitions of currying and partial applications.
For what I can infer, formally the curry operation transforms a
function of multiple arguments to an unary higher order function:

given

 int f(int a, int b); // (int int) -> int

and a curry operator

  Callable<Callable<int(int)>(int)> curry( Callable<int(int, int)> )
; // ((int, int) -> int) -> int -> int -> int

then

  f_curryed = curry(f)

has type

  Callable<int(int)> f_curryed(int); // int -> int -> int

In haskell all functions are automatically curryed (unless you
explicitly ask for tuples).

in other languages you can to implement both currying and partial
application via closures. So the terms become a little fuzzy. I have
no formal functional programming training, so I
have gotten all my terminology from FC++ where currying and partial
application are practically the same thing.
In fc++, if you have a function object of type:

   int divide(a, b)

It is already curryied a-la haskel, so you can write:

   auto divide_10_by = divide(10)

This can also be written:

   auto divide_10_by = divide(10, _)

This syntax is useful if you do not want to partially apply only the
first arguemnt:

  auto divided_by_10 = divide(_, 10)

I think that to do this with only currying you need some argument
juggling to swap order (or a plain old lambda).
FC++ calls the operation that transform a nary function in a function
that accept the '_' placeholder, generalized currying, or (as it,
IIRC, doesn't have a plain curry operator), just currying. In FC++ all
function objects are in practice always curryied.

So, yes, it is a misnomer, but in practice, at least in c++, the
difference is not that great, and the distinction not very useful.

> >> 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;
> > }
>
> Egg uses this to implement curryN.
> See: egg/detail/bind_left1.hpp.

Of course. But I think that the documentation states that curryN is
implemented in term of nest. I was going for the other route,
that is, nest is not necessary and you can just curry nested lambdas
to get the effect of the phoenix lambda(...)[] syntax and still be
result of compatible.

>
> > 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
>
> How about:
> auto always = close1(close1, _1);
>

sure, but my point is writing lambdas that can return lambdas (see the
"needlessy convoluted" note) :).

Now, back on writing the review.

-- 
gpd

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