Boost logo

Boost :

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


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

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

Indeed we are converging. I like code that is made up of independently
useful and simple pieces that are composed together in powerful ways.

Your lambda uses brackets and I'm assuming this is for sequencing zero
argument procedures. So I'd make a separate function which would be useful
for that (Instead of brackets, I'd use normal function calls with variadic
templates for the same effect).

So

seq(a,b,c...) => { a(); b(); c();...}

lambda would be kept simple, as a single argument, and maybe a lamseq could
be

auto lamseq = lamAppPrim( seq );

And then we get a true equivalence:

  for_each(_1,
    lamseq
    (
      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.
>>
>
> 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.).

Why keep outer if you have scoped_argument?

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