Boost logo

Boost :

Subject: Re: [boost] phoenix::bind
From: Giovanni Piero Deretta (gpderetta_at_[hidden])
Date: 2008-10-01 11:20:13

On Wed, Oct 1, 2008 at 5:32 AM, Joel de Guzman
<joel_at_[hidden]> wrote:
> Peter Dimov wrote:
>>>> With boost::bind and a suitably defined f, one can do
>>>> boost::bind( f, 0, 0, _1 )
>>>> to approximate a lambda with two local variables, initially 0, and one
>>>> argument.
>>> That's a nice trick! That can be quite useful on certain occasions.
>> If you manage to include both Phoenix and boost::bind, you can do a
>> generator function that returns 1, 2, 3... with:
>> boost::bind<int>( ++arg1, 0 )
>> Of course if you have Phoenix you should be able to do the same with
>> lambda( _a = 0 )[ ++_a ]
>> but it doesn't seem to work. Maybe I'm doing something wrong. :-)
> Maybe you want:
> let(_a = 0)[ ++_a ]
> and herein I think lies the general misconception about Phoenix
> lambda (and possibly a flaw in the general API).
> The bottom line is that lambda introduces a new scope. It is
> somewhat a cousin of BLL protect in that the lambda[...]. It
> returns a lambda function that returns a lambda function.
> I once called it the lambda-lambda. It came about when trying
> to implement this:
> - \ \
> double - /\ f . /\ x. f(f x)
> - / \ / \

It looks a bit messed up. Is that the same as this: (haskel syntax:
'\' is lambda and function application doesn't require parenthesis)

   double = \f -> \ x -> f f x

> in the original Phoenix1 implementation. Later, I realized it
> is the same motivation behind protect, albeit in a less general
> sense (from the lambda docs):
> "Primary motivation for including protect into the library,
> was to allow nested STL algorithm invocations (the section
> called "Nesting STL algorithm invocations"
> [])."

I never got that detail. How is protect better in that case than using
unlambda? Why would I want to mask a lambda just for one evaluation
round and not forever?

> Phoenix had lazy functions since its inception. The general problem
> was how to implement a higher-order-function that accepts higher-
> order-function. Like say:
> phx::for_each(_1, std::cout << _1 << std::endl)
> substituting the left _1 for the container. The right _1 substitutes
> the container's element which happens when for_each invokes the
> input function for each element: f(element).
> But that can't happen because all _1 will be substituted eagerly.
> hence, it was necessary to brace the higher-order-function argument:
> phx::for_each(_1, lambda[std::cout << _1 << std::endl])

Ok, I do exactly the same with a modified boost.lambda, except that
the 'lambda[]' syntax has 'unlambda' semantics, not 'protect'

> Notice that like protect, there are two function invocations happening
> here.

And this is where I get a bit lost. Why protect and lambda[] have to
add another function evaluation round? I understand that in phoenix
the extra round is to initialize the local variables, but I find it
surprising: why has this to be exposed to the user? can't phoenix use
an internal gateway to do it?

I think as 'lambda' exactly as the lambda symbol in lambda calculus (\
in haskel). Think of the following expression in haskell (ignoring the
fact that print has side effects):

   (\x -> for_each x (\y -> print y))

Now, translate every (\..) with lambda[]

  lambda[ for_each(_1, lambda[ print(_1) ]) ]

IMHO the top level lambda is still introducing a new scope for the
placeholder, so it is really not a special case.

If you remove the top level lambda (which is not required by phoenix),
the result is pretty much your initial phoenix expression. Now (\y ->
print y) is an expression that returns an unary function, so I would
expect lambda[ print(_1)] to also return a nullary.

When nested inside a lambda expression, phoenix takes care itself of
the extra evaluation round, but that is not the case when used top


Boost list run by bdawes at, gregod at, cpdaniel at, john at