Boost logo

Boost :

Subject: Re: [boost] De Bruijn Bind (alternate bind syntax) Interest?
From: Joel de Guzman (joel_at_[hidden])
Date: 2010-09-03 22:05:35


On 9/4/2010 9:29 AM, David Sankel wrote:
> 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) )
> );

Assuming phoenix::outer is reinstated, and assuming we have predefined
placeholders for the most common arities and scopes (e.g. 1..10 args
and 1..3 scopes), the phoenix equivalent would then be:

    for_each(_1,
      lambda
      [
        push_back(_1, _2_1)
      ]
    )

Very close to your syntax.

>> 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.

I don't think Phoenix has a problem with that. Just make
doThingee a phoenix::function or use phoenix::bind with
phoenix::lambda.

One thing's clear to me though: I have to reinstate 'outer' and
scoped_argument<ArgN, ScopeN> facility to make it easier.
E.g.

   scoped_argument<2, 1> const _2_1 = scoped_argument<2, 1>;

(or something like that; I'm still confused with the indexing
used and seems to be mixing zero based and one based indexing.).

Regards,

-- 
Joel de Guzman
http://www.boostpro.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