
On Wed, Oct 1, 2008 at 5:32 AM, Joel de Guzman <joel@boost-consulting.com> 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