|
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"
> [http://tinyurl.com/5xm6rt])."
>
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
level.
-- gpd
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk