# Boost :

From: Brian McNamara (lorgon_at_[hidden])
Date: 2003-10-08 22:39:20

On Wed, Oct 08, 2003 at 10:24:05PM -0400, David Abrahams wrote:
> After some more thought, I have a question: what's the meaning of:
>
> // f(x) -> 2*(x+3)
> lambda(X)[
> let[
> Y == X %plus% 3 // y = x+3
> , Fun == multiplies(2) // fun(x) -> 2*x NOTE PARENS
> ].in[
> Fun[Y] // fun(y)
> ]
> ]
>
> ??
>
> if it's the same as
>
> // f(x) -> 2*(x+3)
> lambda(X)[
> let[
> Y == X %plus% 3 // y = x+3
> , Fun == multiplies[2] // fun(x) -> 2*x NOTE BRACKETS
> ].in[
> Fun[Y] // fun(y)
> ]
> ]

It is the same. More generally, inside a lambda expression,

f(cppexpr) = f[cppexpr]

holds true for all "f" which do not cause side-effects. (Subject to
the constraint that "cppexpr" is a C++ expression, and not a lambda
expression. When "cppexpr" is a lambda expression--let's now call it
"lexpr" instead--"f(lexpr)" usually doesn't compile, and when it does,
it typically means something different than "f[lexpr]".)

> Then I'm inclined to think
> parens-on-lambda-variables-delays-evaluation is a reasonable idea.

Again, another reason I am un-fond of this is because I perceive it to
be accident prone. The lambda library example from the DPCOOL paper is
a good one. (These examples actually don't work, but for merely
distracting reasons which I'll describe later[*].)

// boost::lambda
// print each element
for_each( begin, end, cout << _1 << endl );

for_each( begin, end, cout << "X is " << _1 << endl ); // whoops!

Using FC++ lambda, the example would go

// print each element
for_each( begin, end,
lambda(X)[ &cout %out_stream% X %out_stream% endl ] );

for_each( begin, end,
lambda(X)[ &cout %out_stream% "X is " %out_stream%
X %out_stream% endl ] ); // works

which avoids the accident in the first place. (This example also
illustrates that FC++ is not the best tool for every job (way to
verbose!), but I want to concentrate on the "accidental early
evaluation problem" right now.)

Now, if we overload () to mean [] for lambda expressions, then the FC++
examples become:

// print each element
for_each( begin, end,
lambda(X)[ &cout ^out_stream^ X ^out_stream^ endl ] );

for_each( begin, end,
lambda(X)[ &cout ^out_stream^ "X is " ^out_stream^
X ^out_stream^ endl ] ); // whoops!

and we are right back to the same accident as in boost::lambda.

I don't like that. I prefer the explicit control of using [] and % to
delay evaluation, rather than the implicit control of whether-or-not-
there-happens-to-be-a-lambda-variable-in-this-expression-subtree.
Seemingly-innocuous changes to an expression ought not change its
overall semantics. This is my opinion.

[*] As promised, here are the notes about why none of these examples
compile.

First, both the lambda and FC++ examples try to use "endl". But the way
C++ defines "endl" makes it far-too-slippery a beast to be captured by
such simplicity. See my previous messages in different threads (my
messages talk about "make_manip") for details.

Second, I doubt the FC++ example can handle the string literal. I had
help. When you try to capture a string literal in a function like

template <class T>
S func( const T& x );

calls to func("literal") deduce T to be an array-of-char type. Since
arrays are not copy-by-value-able in C++, and FC++ does everything "by
value", many FC++ calls involving string literals (usually those
involving currying or lambda) fail to compile. You will notice in FC++
documentation examples that rather than say

f("foo")

we usually say

std::string s = "foo";
f(s)

Now you know why. What a dirty secret. Surely there is some easy
remedy for this, but I have never bothered to try to figure out what.
```--