Boost logo

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