|
Boost : |
From: Joel de Guzman (joel_at_[hidden])
Date: 2004-02-19 22:11:36
Fernando Cacciola wrote:
> Here's my review of the lambda sublibrary:
>
> FCPP Lamba is "explicit" as opposed to "implicit" as in BLL.
> That is, I don't create a lambda expression on the fly by merely putting a
> placeholder somewhere in the expression; rather, I have to use brackets (%
> for infix notation).
>
> I really like this part of the design....
> I was myself caught at the "early evaluation" problems of implicit lambda
> until trained to put BLL's "protect()" on the right place.
>
> When you work with complex lambdas it's hard to get it right (that is, avoid
> all possible early evaluations) implicitly, yet explicitly (the way FC++
> works), it's straight forward (even if it requires a more adorned syntax).
>
> OTOH, I don't agree with the arguments against overloaded operators.
> Since FC++ lambda expressions are explicit I can't see any overload
> resolution problems.
> Given that C++ allows you to overload operators is hard to understand why
> would I have to write
> [X %plus% 1] instead of [X+1]
The problem with operator overloading, LL/Phoenix style, can
be alleviated by simply by disallowing automatic conversions.
This is what I am advocating now, given the experience with Phoenix,
possibly through a #define DISALLOW_AUTO_CONVERSION which defaults
to ON. Let me explain... The folling snippet is your typical complaint:
for_each(f, l, cout << "hello, " << arg1);
<< arg1 in Phoenix is _1 in LL >>
The problem is that the expression cout << "hello, " is immediate,
hence, cout << "hello, " << arg1 is immediate + deferred. Yet,
the compiler does not catch this because the library allows
overloading of the LHS and RHS where the RHS and LHS are curryable
epxressions such as arg1 (or _1). This may be hard to get right.
It took me lots of explaining about overloading rules in phoenix
to make this point clear. See:
http://www.boost.org/libs/spirit/doc/phoenix.html
A simple solution is to simply disallow automatic conversions.
Thus, if you want deferred evaluation, state it explicitly using
var and val:
for_each(f, l, var(cout) << val("hello, ") << arg1);
In my opinion, this syntax is still clear while being safe from
incorrect usage. Expressions such as:
for_each(f, l, cout << "hello, " << arg1);
will simply not compile.
That said, I should point out that even FC++'s scheme can be
prone to improper use too. No-one can prevent users from writing
incorrect code with mixed operators such as:
x * y %plus% 1
where x + y is immediately evaluated. Worse, the % precedence might
be another cause of confusion. Consider:
x + y %plus% 1
where the precedence of + is lower than %. The first will compile,
(x * y is immediately evaluated) but the second might not (you
probably can't add (y %plus% 1) to x.
I fear that there might be still more confusion if you add ^ to
the mix:
x ^f^ y // ok
x & y ^f^ y // x & y is immediate
x | y ^f^ y // probably compile error
<< Having said all these, I have no problems with either LL/Phoenix
or FC++'s syntax. IMO, it's just a matter of properly explaning the
issues. >>
Regards,
-- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk