Boost logo

Boost :

Subject: Re: [boost] De Bruijn Bind (alternate bind syntax) Interest?
From: David Sankel (camior_at_[hidden])
Date: 2010-09-03 21:29:57


On Fri, Sep 3, 2010 at 7:46 PM, Joel de Guzman <joel_at_[hidden]>wrote:

> On 9/4/2010 5:42 AM, Dave Abrahams wrote:
>
>> On Fri, Sep 3, 2010 at 5:22 PM, David Sankel<camior_at_[hidden]> wrote:
>>
>>> On Fri, Sep 3, 2010 at 4:46 PM, Dave Abrahams<dave_at_[hidden]> wrote:
>>>
>>> On Fri, Sep 3, 2010 at 2:52 PM, Larry Evans<cppljevans_at_[hidden]>
>>>> wrote:> Since, as I mentioned, I had trouble understanding how apply
>>>>
>>>>> worked, and the code seems pretty complicated, at least to me,
>>>>> I was hoping DeBruijn's method would offer simplifications.
>>>>>
>>>>
>>>> > From the examples I've seen so far, this would make it easier for bind
>>>> library writers at the expense of usability. On th other hand, once
>>>> lambdas start to use protect() I'm usually giving up on them ;-)
>>>>
>>>>
>>> Usability is hurt from whose perspective? The bind author or the bind
>>> user?
>>>
>>
>> The bind user
>>
>> And how so?
>>>
>>
>> 1. It means learning a totally new paradigm for writing ordinary
>> lambdas that—so far—seems to require the grasp of quite a few concepts
>> that are not familiar to the average C++ programmer. bind and its
>> cousins may not be as flexible, but they're designed to be intuitively
>> graspable (to a C++ programmer), and the paradigm is now going into
>> the standard so will be lingua franca.
>>
>> 2. Again, please correct me if I'm wrong about this, but it looks like
>> for "ordinary lambdas" (those that don't need protect), the
>> corresponding bind expressions are always shorter and simpler.
>>
>
> Perhaps it would be good to have a practical use-case. In a
> lengthy discussions about scopes and higher order functions
> (with Jaakko, Dave, Doug plus some more I don't recall), we
> had this use case (this is the one you can see in the phoenix
> docs here: http://tinyurl.com/295ha83):
>
> write a lambda expression that accepts:
>
> 1. a 2-dimensional container (e.g. vector<vector<int> >)
> 2. a container element (e.g. int)
>
> and pushes-back the element to each of the vector<int>.
>
> The phoenix solution:
>
> for_each(_1,
> lambda(_a = _2) // local _a captures the outer arg2
> [
> push_back(_1, _a)
> ]
> )
>
> We once had outer. The outer solution looked like this:
>
> for_each(_1,
> lambda
> [
> push_back(_1, outer(_2))
> ]
> )
>
> How would that look like if you extend phoenix with your syntax?
>

Thanks for the example Joel.

Assuming for_eachF and push_backF are normal polymorphic functions, the
core would look like:

auto f = lam<2>( app( for_eachF
                    , _1_1
                    , lam( app( push_backF
                              , _1_1
                              , _2_1
                              )
                         )
                    )
               );

If we extend the language with for_each and push_back primitives,

auto for_each = appPrim( for_eachF );
auto push_back = appPrim( push_backF );

it gets a bit simpler:

auto f = lam<2>( for_each( _1_1
                         , lam( push_back( _1_1, _2_1 ) )
                         )
               );

If we do Stefan's multi-arity suggestion, which I still don't like too much,
it looks like this:

auto f = for_each( _1_1
                 , lam( push_back( _1_1, _2_1 ) )
                 );
If we make _1 a synonym for _1_1 and so on (not saying we should), we get:

auto f = for_eachF( _1
                  , lam( push_back( _1, _2_1) )
                  );

>
> It would be good to tackle this through practical use-cases
> and using plain C++ terms. Too much formality hurts my brain.
> If this use-case is too simplistic, then perhaps you can provide
> something more elaborate, yet still practical.

I think your example is a valid use case. Unfortunately, I think it would
take longer to explain my particular use cases than the formality of this
syntax :/.

An example of a bind weakness that really bites is the following function.
This came up when I was making a high performance stream library:

//Where g is some other function
template< typename F >
auto doThingee( F f )
    -> decltype( boost::bind( g, f ) )
{
    return boost::bind( g, f );
}

doThingee does what you would expect most of the time. That is, until a user
makes a call like:

doThingee( boost::bind( h, 22, _1) );

See the problem here? This is an example of the really crappy bind semantics
that hardly anyone is aware of. With something like De Bruijn bind, there
are no surprises.

-- 
David Sankel
Sankel Software
www.sankelsoftware.com
585 617 4748 (Office)

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk