De Bruijn Bind (alternate bind syntax) Interest?

Hello all, I've been working on an alternate bind syntax based on De Bruijn indices[1]. The syntax is very simple, yet the terms are very powerful. Here is an example of a function const that takes in an argument c and returns another function that, for all input, returns c: //λx.λy.x = λλ1 with De Bruijn indices. auto const_ = abs<1>( abs<1>( var<1,0>() ) ); More examples, further explanation, and an implementation are available here[2]. I'm thinking that this library could also be useful as a core for more syntax heavy bind variations. David [1] http://en.wikipedia.org/wiki/De_Bruijn_index [2] http://bitbucket.org/camior/de-bruijn-bind/ -- David Sankel Sankel Software www.sankelsoftware.com 585 617 4748 (Office)

On Thu, Sep 2, 2010 at 11:57 AM, David Sankel <camior@gmail.com> wrote:
Hello all,
I've been working on an alternate bind syntax based on De Bruijn indices[1]. The syntax is very simple, yet the terms are very powerful.
Here is an example of a function const that takes in an argument c and returns another function that, for all input, returns c:
//λx.λy.x = λλ1 with De Bruijn indices.
Not according to the page you linked below. What am I missing? [1] http://en.wikipedia.org/wiki/De_Bruijn_index
For those of us without time to study up on functional lingo, it would be interesting to see some examples translated from a C++-ish lambda syntax (e.g. phoenix, C++0x lambdas, Bind) to your suggestion -- Dave Abrahams BoostPro Computing http://www.boostpro.com

2010/9/2 Dave Abrahams <dave@boostpro.com>
On Thu, Sep 2, 2010 at 11:57 AM, David Sankel <camior@gmail.com> wrote:
Hello all,
I've been working on an alternate bind syntax based on De Bruijn indices[1]. The syntax is very simple, yet the terms are very powerful.
Here is an example of a function const that takes in an argument c and returns another function that, for all input, returns c:
//λx.λy.x = λλ1 with De Bruijn indices.
Not according to the page you linked below. What am I missing?
Sorry about that. The wikipedia page uses 1-indexed indices where I'm using 0-indexed indices. So, increment 1 on all my examples to get the wikipedia syntax.
For those of us without time to study up on functional lingo, it would be interesting to see some examples translated from a C++-ish lambda syntax (e.g. phoenix, C++0x lambdas, Bind) to your suggestion
A couple bind examples: bind( f , bind( g , _1 ) ) => abs<1>( app( f , app( g , var<0,0>() ) ) ) bind( f , _1 , boost::protect( bind( g , 12 , _1 ) ) ) => abs<1>( app( f , var<0,0>() , abs<1>( app( g , 12 , var<0,0>() ) ) ) ) The reverse direction is not always possible. For example: abs<1>( app( f , var<0,0>() , abs<1>( app( g , var<1,0>() , var<0,0>() ) ) ) ) does not have a corresponding bind. David -- David Sankel Sankel Software www.sankelsoftware.com 585 617 4748 (Office)

2010/9/2 David Sankel <camior@gmail.com>
2010/9/2 Dave Abrahams <dave@boostpro.com>
On Thu, Sep 2, 2010 at 11:57 AM, David Sankel <camior@gmail.com> wrote:
Hello all,
I've been working on an alternate bind syntax based on De Bruijn indices[1]. The syntax is very simple, yet the terms are very powerful.
Here is an example of a function const that takes in an argument c and returns another function that, for all input, returns c:
//λx.λy.x = λλ1 with De Bruijn indices.
Not according to the page you linked below. What am I missing?
Sorry about that. The wikipedia page uses 1-indexed indices where I'm using 0-indexed indices. So, increment 1 on all my examples to get the wikipedia syntax.
I'm beginning to think it is better to use 1-indexing instead of 0-indexing all around. It will be both compatible with the Wikipedia page and familiar to boost bind/lambda users. Funny thing is, I started with 1-indexing but changed it to 0-indexing for the fallacious reasoning of the arrrrgh function that Dave A mentioned. -- David Sankel Sankel Software www.sankelsoftware.com 585 617 4748 (Office)

On 09/02/10 16:08, David Sankel wrote:> 2010/9/2 David Sankel <camior@gmail.com>
2010/9/2 Dave Abrahams <dave@boostpro.com>
On Thu, Sep 2, 2010 at 11:57 AM, David Sankel <camior@gmail.com> wrote:
Hello all,
I've been working on an alternate bind syntax based on De Bruijn indices[1]. The syntax is very simple, yet the terms are very powerful.
Here is an example of a function const that takes in an argument c and returns another function that, for all input, returns c:
//λx.λy.x = λλ1 with De Bruijn indices.
Not according to the page you linked below. What am I missing?
Sorry about that. The wikipedia page uses 1-indexed indices where I'm
using
0-indexed indices. So, increment 1 on all my examples to get the wikipedia syntax.
I'm beginning to think it is better to use 1-indexing instead of 0-indexing all around. It will be both compatible with the Wikipedia page and familiar to boost bind/lambda users.
Funny thing is, I started with 1-indexing but changed it to 0-indexing for the fallacious reasoning of the arrrrgh function that Dave A mentioned.
Hi David, I looked at the web page mentioned in your first post to this thread and could not understand the flip example: For flip, I'm introducing subscripts on abstractions (λ) to indicate the number of arguments. For the variables (1,2,etc.) I use a subscript to indicate which argument of the abstraction it refers to. flip = λx.λ(y,z).x(z,y) = λ₁λ₂1(1₁, 2₀) which seems to have a 0 for the subscript of 2. and 1 for the subscript of 1. That's consistent with the argument indices starting at 0 (1₁ corresponds z {the 2nd argument in the argument list, (z,y)}, and 2₀ corresponds to y {the 1st argument in the argument list, (z,y)}); however, subscrited numbers should be the same since both y and z are bound by the same lamba, the nearest one corresponding to number 1. Hence, shouldn't the above be: flip = λx.λ(y,z).x(z,y) = λ₁λ₂2₀(1₁, 1₀) ? Also, since the abstraction indices seem to start at 1, shouldn't the argument indices also start at 1? I think that would make the notation more consistent, and would result in: flip = λx.λ(y,z).x(z,y) = λ₁λ₂2_1(1_2, 1_1) (where a subscript of I have been replaced with _I).

On Sun, Jun 23, 2013 at 9:40 AM, Larry Evans <cppljevans@suddenlink.net>wrote:
On 09/02/10 16:08, David Sankel wrote:> 2010/9/2 David Sankel <camior@gmail.com>
2010/9/2 Dave Abrahams <dave@boostpro.com>
On Thu, Sep 2, 2010 at 11:57 AM, David Sankel <camior@gmail.com> wrote:
Hello all,
I've been working on an alternate bind syntax based on De Bruijn indices[1]. The syntax is very simple, yet the terms are very powerful.
Here is an example of a function const that takes in an argument c and returns another function that, for all input, returns c:
//λx.λy.x = λλ1 with De Bruijn indices.
Not according to the page you linked below. What am I missing?
Sorry about that. The wikipedia page uses 1-indexed indices where I'm
using
0-indexed indices. So, increment 1 on all my examples to get the wikipedia syntax.
I'm beginning to think it is better to use 1-indexing instead of 0-indexing all around. It will be both compatible with the Wikipedia page and familiar to boost bind/lambda users.
Funny thing is, I started with 1-indexing but changed it to 0-indexing for the fallacious reasoning of the arrrrgh function that Dave A mentioned.
Hi David,
I looked at the web page mentioned in your first post to this thread and could not understand the flip example:
For flip, I'm introducing subscripts on abstractions (λ) to indicate the number of arguments. For the variables (1,2,etc.) I use a subscript to indicate which argument of the abstraction it refers to.
flip = λx.λ(y,z).x(z,y) = λ₁λ₂1(1₁, 2₀)
which seems to have a 0 for the subscript of 2. and 1 for the subscript of 1. That's consistent with the argument indices starting at 0 (1₁ corresponds z {the 2nd argument in the argument list, (z,y)}, and 2₀ corresponds to y {the 1st argument in the argument list, (z,y)}); however, subscrited numbers should be the same since both y and z are bound by the same lamba, the nearest one corresponding to number 1. Hence, shouldn't the above be:
flip = λx.λ(y,z).x(z,y) = λ₁λ₂2₀(1₁, 1₀)
? Also, since the abstraction indices seem to start at 1, shouldn't the argument indices also start at 1? I think that would make the notation more consistent, and would result in:
flip = λx.λ(y,z).x(z,y) = λ₁λ₂2_1(1_2, 1_1)
(where a subscript of I have been replaced with _I).
You are correct on both accounts. I fixed the README at de-bruijn-bind's new home on github. https://github.com/camio/de-bruijn-bind Thanks for the bug report! -- David

David Sankel wrote:
I've been working on an alternate bind syntax based on De Bruijn indices[1].
I started to look at the Wikipedia page, but realized there was way too much for me to learn to even begin to understand what you mean. Therefore, this reference is of no value to anyone without your background.
The syntax is very simple, yet the terms are very powerful.
I must take your word for it.
Here is an example of a function const that takes in an argument c and returns another function that, for all input, returns c:
//λx.λy.x = λλ1 with De Bruijn indices. auto const_ = abs<1>( abs<1>( var<1,0>() ) );
Presumably, "abs" doesn't mean absolute value as it would in pretty much any other programming context. That seems unwise at best.
More examples, further explanation, and an implementation are available here[2].
I found your README. The grammar section assumes I know what you mean by "abstraction" and "application," at the least, which I don't. The examples section does absolutely nothing for me as I don't understand the syntax or terminology. The result is that I can't make anything of your *bind* proposal. _____ Rob Stewart robert.stewart@sig.com Software Engineer, Core Software using std::disclaimer; Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

2010/9/2 Stewart, Robert <Robert.Stewart@sig.com>
David Sankel wrote:
I've been working on an alternate bind syntax based on De Bruijn indices[1].
I started to look at the Wikipedia page, but realized there was way too much for me to learn to even begin to understand what you mean. Therefore, this reference is of no value to anyone without your background.
It requires someone casually familiar with lambda calculus. Lambda calculus involves the creation of anonymous functions. λv.e declares a new function. v is the identifier bound to the argument and e is an expression that can use v. For example λx.x+1 creates a new function that returns its argument plus 1. Expressions can include other lambdas (called abstractions) and function applications. Consider the function λx.λy.x+y. This function actually returns another function which can be applied yet again. Here it is being called with some values. ((λx.λy.x+y)(22))(14) ← here x is being substituted by 22 => (λy.22+y)(14) ← now y is being substituted by 14. => 22+14 => 36 De Bruijn indices are a way to remove the need for binding a name to the variable in our function declaration. So, instead of λv.e, where e can use v, we say λe where e uses "1" to refer to the argument. The reason for using 1 is to distinguish between between nested lambdas. λx.λy.x(y) becomes λλ2(1). Note that 1 and 2 aren't the normal arithmetic variants, these are De Bruijn indices. The 2 refers to the outer lambda and the 1 refers to the inner lambda.
Here is an example of a function const that takes in an argument c and
returns another function that, for all input, returns c:
//λx.λy.x = λλ1 with De Bruijn indices. auto const_ = abs<1>( abs<1>( var<1,0>() ) );
Presumably, "abs" doesn't mean absolute value as it would in pretty much any other programming context. That seems unwise at best.
I disagree. We have namespaces to take care of duplicate identifiers and abs is the commonly used identifier for lambda abstractions in programming contexts you might not be aware of.
More examples, further explanation, and an implementation are
available here[2].
I found your README. The grammar section assumes I know what you mean by "abstraction" and "application," at the least, which I don't. The examples section does absolutely nothing for me as I don't understand the syntax or terminology. The result is that I can't make anything of your *bind* proposal.
Hopefully this helps. Thanks for the feedback. David -- David Sankel Sankel Software www.sankelsoftware.com 585 617 4748 (Office)

David Sankel wrote:
2010/9/2 Stewart, Robert <Robert.Stewart@sig.com>
David Sankel wrote:
I've been working on an alternate bind syntax based on De Bruijn indices[1].
I started to look at the Wikipedia page, but realized there was way too much for me to learn to even begin to understand what you mean. Therefore, this reference is of no value to anyone without your background.
It requires someone casually familiar with lambda calculus. Lambda calculus involves the creation of anonymous functions.
λv.e declares a new function. v is the identifier bound to [snip explanation] Hopefully this helps.
It did indeed. Thank you.
Presumably, "abs" doesn't mean absolute value as it would in pretty much any other programming context. That seems unwise at best.
I disagree. We have namespaces to take care of duplicate identifiers and abs is the commonly used identifier for lambda abstractions in programming contexts you might not be aware of.
I don't doubt that the name is meaningful, but it does conflict with an extremely common name which isn't necessarily even in a named namespace. Isn't there another name you could use that wouldn't have that potential for confusion and ambiguity? _____ Rob Stewart robert.stewart@sig.com Software Engineer, Core Software using std::disclaimer; Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

On 09/02/10 12:52, Stewart, Robert wrote:
David Sankel wrote: [snip]
I disagree. We have namespaces to take care of duplicate identifiers and abs is the commonly used identifier for lambda abstractions in programming contexts you might not be aware of.
I don't doubt that the name is meaningful, but it does conflict with an extremely common name which isn't necessarily even in a named namespace. Isn't there another name you could use that wouldn't have that potential for confusion and ambiguity?
How about using the names from: http://www.comlab.ox.ac.uk/richard.bird/online /BirdPaterson99DeBruijn.pdf i.e. Lam for the lambda symbol, Var for the positive number representing a arg or maybe Arg would be even better, and more like the current mpl. ?

On Thu, Sep 2, 2010 at 2:32 PM, Larry Evans <cppljevans@suddenlink.net>wrote:
On 09/02/10 12:52, Stewart, Robert wrote:
David Sankel wrote: [snip]
I disagree. We have namespaces to take care of duplicate identifiers and abs is the commonly used identifier for lambda abstractions in programming contexts you might not be aware of.
I don't doubt that the name is meaningful, but it does conflict with an extremely common name which isn't necessarily even in a named namespace. Isn't there another name you could use that wouldn't have that potential for confusion and ambiguity?
How about using the names from:
http://www.comlab.ox.ac.uk/richard.bird/online /BirdPaterson99DeBruijn.pdf
i.e.
Lam for the lambda symbol,
I'm open to using lam instead of abs. Var for the positive number representing a arg
or maybe Arg would be even better, and more like the current mpl.
I don't want to veer too far away from the language of the semantic domain[1] since thinking in the semantic domain can elevate the user's thoughts beyond what they assume is possible (And what better way to encourage thinking in the semantic domain than by using its vocabulary?). On the other hand, arg seems pretty reasonable. [1] http://en.wikipedia.org/wiki/Denotational_semantics David -- David Sankel Sankel Software www.sankelsoftware.com 585 617 4748 (Office)

On Thu, Sep 2, 2010 at 3:10 PM, David Sankel <camior@gmail.com> wrote:
On the other hand, arg seems pretty reasonable.
and how is _1 different from arg<0>? -- Dave Abrahams BoostPro Computing http://www.boostpro.com

On Thu, Sep 2, 2010 at 3:21 PM, Dave Abrahams <dave@boostpro.com> wrote:
On Thu, Sep 2, 2010 at 3:10 PM, David Sankel <camior@gmail.com> wrote:
On the other hand, arg seems pretty reasonable.
and how is _1 different from arg<0>?
I'm not sure what you mean here. var (or arg if you like) in this library takes two template arguments. -- David Sankel Sankel Software www.sankelsoftware.com 585 617 4748 (Office)

On 09/02/10 14:41, David Sankel wrote:
On Thu, Sep 2, 2010 at 3:21 PM, Dave Abrahams <dave@boostpro.com> wrote:
On Thu, Sep 2, 2010 at 3:10 PM, David Sankel <camior@gmail.com> wrote:
On the other hand, arg seems pretty reasonable.
and how is _1 different from arg<0>?
I'm not sure what you mean here. var (or arg if you like) in this library takes two template arguments.
Initially I was confused by ths binary var, until I read: http://bitbucket.org/camior/de-bruijn-bind/src#cl-45 Should have done that earlier. -Larry

On Thu, Sep 2, 2010 at 3:41 PM, David Sankel <camior@gmail.com> wrote:
On Thu, Sep 2, 2010 at 3:21 PM, Dave Abrahams <dave@boostpro.com> wrote:
On Thu, Sep 2, 2010 at 3:10 PM, David Sankel <camior@gmail.com> wrote:
On the other hand, arg seems pretty reasonable.
and how is _1 different from arg<0>?
I'm not sure what you mean here. var (or arg if you like) in this library takes two template arguments.
Maybe you're thinking that _1 seems similar to arg<0,0> which is true until abstractions (lambdas/binds) get nested. -- David Sankel Sankel Software www.sankelsoftware.com 585 617 4748 (Office)

David Sankel wrote:
On Thu, Sep 2, 2010 at 3:41 PM, David Sankel <camior@gmail.com> wrote:
On Thu, Sep 2, 2010 at 3:21 PM, Dave Abrahams wrote:
and how is _1 different from arg<0>?
I'm not sure what you mean here. var (or arg if you like) in this library takes two template arguments.
Maybe you're thinking that _1 seems similar to arg<0,0> which is true until abstractions (lambdas/binds) get nested.
Shorthand is helpful, so perhaps you could use _1_1, _1_2, _2_1, etc. (I skipped zero because you were figuring on switching to one-based indexing.) _1_1 is more readable and succinct than arg<1,1>: auto const_ = lam<1>( lam<1>( arg<2,1>() ) ); auto const_ = lam<1>( lam<1>( _2_1() ) ); and: auto flip = lam<1>( lam<2>( app( arg<2,1>() , arg<1,2>() , arg<1,1>() ) ) ); auto flip = lam<1>(lam<2>(app(_2_1(), _1_2(), _1_1()))); _____ Rob Stewart robert.stewart@sig.com Software Engineer, Core Software using std::disclaimer; Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Zitat von "Stewart, Robert" <Robert.Stewart@sig.com>:
_1_1 is more readable and succinct than arg<1,1>:
auto const_ = lam<1>( lam<1>( arg<2,1>() ) );
auto const_ = lam<1>( lam<1>( _2_1() ) );
and:
auto flip = lam<1>( lam<2>( app( arg<2,1>() , arg<1,2>() , arg<1,1>() ) ) );
auto flip = lam<1>(lam<2>(app(_2_1(), _1_2(), _1_1())));
or: auto flip=lam(lam(app(_2_1,_1,_2))); david, why do you need to number the lambdas? doesn't the "nested index" of the args refer to the level of nesting instead of the number given to the lam<> template? _1, _2 in the example above would refer to the placeholders e.g. of Boost.Bind and be equivalent to _1_1 and _1_2. thomas (author of phoenix v3) has mentioned an effort to unify all the placeholders in boost (Bind, Phoenix, ...)

On 09/03/10 06:33, Stefan Strasser wrote: [snip]
auto flip = lam<1>(lam<2>(app(_2_1(), _1_2(), _1_1())));
or:
auto flip=lam(lam(app(_2_1,_1,_2)));
david, why do you need to number the lambdas? doesn't the "nested index" of the args refer to the level of nesting instead of the number given to the lam<> template? In:
lam<I> I is arity, not nesting, of the lambda expression: http://bitbucket.org/camior/de-bruijn-bind/src#cl-45 IOW: \(x1,x2,x3).x1+x2+x3 => lam<3>(arg<0,1>+arg<0,2>+arg<0,3>) [snip]

Larry Evans wrote:
On 09/03/10 06:33, Stefan Strasser wrote:
In:
lam<I>
I is arity, not nesting, of the lambda expression:
http://bitbucket.org/camior/de-bruijn-bind/src#cl-45
IOW:
\(x1,x2,x3).x1+x2+x3 => lam<3>(arg<0,1>+arg<0,2>+arg<0,3>)
lam<3>(_0_1 + _0_2 + _0_3) (using zero-based indices) So much more readable, don't you think? ;-) _____ Rob Stewart robert.stewart@sig.com Software Engineer, Core Software using std::disclaimer; Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

On Fri, Sep 3, 2010 at 8:04 AM, Stewart, Robert <Robert.Stewart@sig.com>wrote:
Larry Evans wrote:
On 09/03/10 06:33, Stefan Strasser wrote:
In:
lam<I>
I is arity, not nesting, of the lambda expression:
http://bitbucket.org/camior/de-bruijn-bind/src#cl-45
IOW:
\(x1,x2,x3).x1+x2+x3 => lam<3>(arg<0,1>+arg<0,2>+arg<0,3>)
lam<3>(_0_1 + _0_2 + _0_3) (using zero-based indices)
So much more readable, don't you think? ;-)
Yes. I've added the following syntactic enhancements to the code (and updated README) at http://bitbucket.org/camior/de-bruijn-bind - abs -> lam - var -> arg - some _x_y shorthands for arg<x,y>() -- David Sankel Sankel Software www.sankelsoftware.com 585 617 4748 (Office)

On 09/03/10 10:33, David Sankel wrote:
On Fri, Sep 3, 2010 at 8:04 AM, Stewart, Robert <Robert.Stewart@sig.com>wrote:
Larry Evans wrote:
On 09/03/10 06:33, Stefan Strasser wrote:
In:
lam<I>
I is arity, not nesting, of the lambda expression:
http://bitbucket.org/camior/de-bruijn-bind/src#cl-45
IOW:
\(x1,x2,x3).x1+x2+x3 => lam<3>(arg<0,1>+arg<0,2>+arg<0,3>)
lam<3>(_0_1 + _0_2 + _0_3) (using zero-based indices)
So much more readable, don't you think? ;-)
Yes. I've added the following syntactic enhancements to the code (and updated README) at http://bitbucket.org/camior/de-bruijn-bind
- abs -> lam - var -> arg - some _x_y shorthands for arg<x,y>()
Since most of the time the user will be referring to the arg<0,I>, why not switch args to arg to allow defaulting the 2nd arg for the common case? IOW, something like: template < unsigned ArgIndex , unsigned NestingLevel=0
struct arg ; instead of: template < unsigned NestingLevel , unsigned ArgIndex
struct arg ; This would allow: typedef arg<1> _1; typedef arg<2> _2; ...

On 09/04/10 06:09, Larry Evans wrote:
On 09/03/10 10:33, David Sankel wrote:
On Fri, Sep 3, 2010 at 8:04 AM, Stewart, Robert <Robert.Stewart@sig.com>wrote:
Larry Evans wrote:
On 09/03/10 06:33, Stefan Strasser wrote:
In:
lam<I>
I is arity, not nesting, of the lambda expression:
http://bitbucket.org/camior/de-bruijn-bind/src#cl-45
IOW:
\(x1,x2,x3).x1+x2+x3 => lam<3>(arg<0,1>+arg<0,2>+arg<0,3>)
lam<3>(_0_1 + _0_2 + _0_3) (using zero-based indices)
So much more readable, don't you think? ;-)
Yes. I've added the following syntactic enhancements to the code (and updated README) at http://bitbucket.org/camior/de-bruijn-bind
- abs -> lam - var -> arg - some _x_y shorthands for arg<x,y>()
Since most of the time the user will be referring to the arg<0,I>, why not switch args to arg to allow defaulting the 2nd arg for the common case? IOW, something like:
template < unsigned ArgIndex , unsigned NestingLevel=0
struct arg ;
instead of:
template < unsigned NestingLevel , unsigned ArgIndex
struct arg ;
This would allow:
typedef arg<1> _1; typedef arg<2> _2; ...
Also, if: template < unsigned Arity=1
struct lam ; then: \(x1,x2,x3).x1+x2+x3 =curry=> \x1\x2\x3.x1+x2+x3 =deBruijn=> lam(lam(lam(arg<1,2>+arg<1,1>+arg<1,0>))) which illustrates that only a single argument arg template is needed. I also illustrates that the nesting level is the reverse of the argument order. I wonder why DeBruijn preferred that way? OTOH, how would you express a nullary function? IOW, is the following possible with current code? lam<0>(9)

On Sat, Sep 4, 2010 at 7:39 AM, Larry Evans <cppljevans@suddenlink.net>wrote:
On 09/04/10 06:09, Larry Evans wrote:
On 09/03/10 10:33, David Sankel wrote:
On Fri, Sep 3, 2010 at 8:04 AM, Stewart, Robert <Robert.Stewart@sig.com wrote:
Larry Evans wrote:
On 09/03/10 06:33, Stefan Strasser wrote:
In:
lam<I>
I is arity, not nesting, of the lambda expression:
http://bitbucket.org/camior/de-bruijn-bind/src#cl-45
IOW:
\(x1,x2,x3).x1+x2+x3 => lam<3>(arg<0,1>+arg<0,2>+arg<0,3>)
lam<3>(_0_1 + _0_2 + _0_3) (using zero-based indices)
So much more readable, don't you think? ;-)
Yes. I've added the following syntactic enhancements to the code (and updated README) at http://bitbucket.org/camior/de-bruijn-bind
- abs -> lam - var -> arg - some _x_y shorthands for arg<x,y>()
Since most of the time the user will be referring to the arg<0,I>, why not switch args to arg to allow defaulting the 2nd arg for the common case? IOW, something like:
template < unsigned ArgIndex , unsigned NestingLevel=0
struct arg ;
instead of:
template < unsigned NestingLevel , unsigned ArgIndex
struct arg ;
This would allow:
typedef arg<1> _1; typedef arg<2> _2; ...
Also, if:
template < unsigned Arity=1
struct lam ;
then:
\(x1,x2,x3).x1+x2+x3
=curry=>
\x1\x2\x3.x1+x2+x3
=deBruijn=>
lam(lam(lam(arg<1,2>+arg<1,1>+arg<1,0>)))
which illustrates that only a single argument arg template is needed.
I also illustrates that the nesting level is the reverse of the argument order. I wonder why DeBruijn preferred that way?
DeBruijn likely never worked with multiple arguments. One nice thing is that a lambda that doesn't include any reference to arguments of a lambda it is nested in, that is one that is self contained, can be copied and pasted anywhere and still have the same meaning. OTOH, how would you express a nullary function?
IOW, is the following possible with current code?
lam<0>(9)
Yes this is possible with the current code. See [1] for a similar case [1] http://bitbucket.org/camior/de-bruijn-bind/src/cb8e2edc75fb/src/main.cpp#cl-... -- David Sankel Sankel Software www.sankelsoftware.com 585 617 4748 (Office)

On Sat, Sep 4, 2010 at 7:09 AM, Larry Evans <cppljevans@suddenlink.net>wrote:
On 09/03/10 10:33, David Sankel wrote:
On Fri, Sep 3, 2010 at 8:04 AM, Stewart, Robert <Robert.Stewart@sig.com wrote:
Larry Evans wrote:
On 09/03/10 06:33, Stefan Strasser wrote:
In:
lam<I>
I is arity, not nesting, of the lambda expression:
http://bitbucket.org/camior/de-bruijn-bind/src#cl-45
IOW:
\(x1,x2,x3).x1+x2+x3 => lam<3>(arg<0,1>+arg<0,2>+arg<0,3>)
lam<3>(_0_1 + _0_2 + _0_3) (using zero-based indices)
So much more readable, don't you think? ;-)
Yes. I've added the following syntactic enhancements to the code (and updated README) at http://bitbucket.org/camior/de-bruijn-bind
- abs -> lam - var -> arg - some _x_y shorthands for arg<x,y>()
Since most of the time the user will be referring to the arg<0,I>,
This would be arg<1,1> in the new notation.
why not switch args to arg to allow defaulting the 2nd arg for the common case? IOW, something like:
template < unsigned ArgIndex , unsigned NestingLevel=0
struct arg ;
instead of:
template < unsigned NestingLevel , unsigned ArgIndex
struct arg ;
This would allow:
typedef arg<1> _1; typedef arg<2> _2; ...
We could always do typedef arg<1,1> _1; //etc. So I don't see the necessity of the default arg template argument. If someone wants to go beyond the _a{_b} syntax sugar then they will likely not take advantage of the template arguments. I liked the idea of _x sugar in its familiarity to bind users, but given the new recursion functionality I prefer _x as sugar to refer to the function created by the xth nested lambda. -- David Sankel Sankel Software www.sankelsoftware.com 585 617 4748 (Office)

On 9/3/2010 7:33 PM, Stefan Strasser wrote:
_1, _2 in the example above would refer to the placeholders e.g. of Boost.Bind and be equivalent to _1_1 and _1_2. thomas (author of phoenix v3) has mentioned an effort to unify all the placeholders in boost (Bind, Phoenix, ...)
Phoenix-3 is a port to proto, not a total rewrite. Please give credit to the people before Thomas. Phoenix v3 is co-authored by Thomas plus the original contributors Dan, and yours truly. Eric Niebler also contributed significantly to Phoenix V3 core and is thus also a co- author. Just to set things straight. Regards, -- Joel de Guzman http://www.boostpro.com http://spirit.sf.net

david, why do you need to number the lambdas? doesn't the "nested index" of the args refer to the level of nesting instead of the number given to the lam<> template?
after reading the link larry posted (1), let me correct my question: does lam need to know the number of parameters of the resulting function? Boost.Bind simply ignores additional arguments, and generates a compiler error for insufficient arguments. void f(int,int); bind(&f,3,_1)(1,2); // -> f(3,1) (1) http://bitbucket.org/camior/de-bruijn-bind/src#cl-45

On Fri, Sep 3, 2010 at 7:50 AM, Stefan Strasser <strasser@uni-bremen.de>wrote:
david, why do you need to number the lambdas? doesn't the "nested index"
of the args refer to the level of nesting instead of the number given to the lam<> template?
after reading the link larry posted (1), let me correct my question: does lam need to know the number of parameters of the resulting function?
Boost.Bind simply ignores additional arguments, and generates a compiler error for insufficient arguments.
void f(int,int); bind(&f,3,_1)(1,2); // -> f(3,1)
It is possible to make the semantics such that lam does not need to know the number of parameters of the resulting function. Instead of returning a function with a set arity it will return a function with many arities, like bind does. My preference is for a set arity return value. Every time I've used bind I've always had a specific arity in mind and its current behavior has been a source of subtle run-time bugs. If someone really wants a multi-arity function then they can use another tool to do that: make_multi_arity( lam<1>( _1_1 ) ); Furthermore it really complicates the simple semantics we have. That would make me sad :( -- David Sankel Sankel Software www.sankelsoftware.com 585 617 4748 (Office)

Can someone explain to me, in English, what this technique allows me to express that I couldn't otherwise? Is this just about allowing generic composition of lambdas? -- Dave Abrahams BoostPro Computing http://www.boostpro.com

On 09/03/10 12:09, Dave Abrahams wrote:
Can someone explain to me, in English, what this technique allows me to express that I couldn't otherwise?
Is this just about allowing generic composition of lambdas?
Nothing, AFAICT. What it does do is make implementation of beta reduction easier to implement because one doesn't have to do any alpha conversions. I think one has to do some decrementing of the NestLevel in the arg<NestLevel,ArgIndex> terms, as suggested by: 2. decrease the free variables of M to match the removal of the outer λ-binder From: http://en.wikipedia.org/wiki/De_Bruijn_index Now I'm guessing here David, but when I've encountered problems with mpl's apply<F,A1,A2,...,An>, sometimes I've had to wrap F in an mpl::lambda to get it to work Now one thing that this DeBruijn technique *might* do, is simplify that lambda, because mpl::lambda looks real complicated to me. Maybe Mr. Sankel could give that a try, although his code only deals with values and not types. For those unfamiliar with the beta and alpha terms, this may help: http://en.wikipedia.org/wiki/%CE%91_conversion#.CE.B1-conversion

On Fri, Sep 3, 2010 at 2:03 PM, Larry Evans <cppljevans@suddenlink.net> wrote:
What it does do is make implementation of beta reduction easier
And why would a C++ programmer care about making the implementation of beta-reduction easier? -- Dave Abrahams BoostPro Computing http://www.boostpro.com

On 09/03/10 13:21, Dave Abrahams wrote:
On Fri, Sep 3, 2010 at 2:03 PM, Larry Evans <cppljevans@suddenlink.net> wrote:
What it does do is make implementation of beta reduction easier
And why would a C++ programmer care about making the implementation of beta-reduction easier?
I thought that's essentially what mpl::apply<F,A> does. Let's see, from: http://en.wikipedia.org/wiki/%CE%91_conversion#.CE.B1-conversion there's: Beta-reduction captures the idea of function application. Beta-reduction is defined in terms of substitution: the beta-reduction of ((λV.E) E′) is E[V := E′]. For example, assuming some encoding of 2, 7, *, we have the following β-reductions: ((λn.n*2) 7) → 7*2. 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. OTOH, maybe I'm completely missing something. -regards, Larry

Wouldn't this be a perfect candidate for a layer adjacent to the bind layer in Boost.Phoenix3? It seems like it could be easily composable in the Boost.Phoenix world. Seems almost like it could transparently exist inside the bind layer using the _1_1 syntax, it could dynamically mutate itself as necessary (where _1 and _2 and so equals _1_1 and _1_2 and so forth), would reduce the need of let[] and lambda[] and other such constructs for multi-level complicated bind expression. Sorry for the possible top-post, on my phone, I have no clue where the quoted text will end up... On 9/3/10, Larry Evans <cppljevans@suddenlink.net> wrote:
On 09/03/10 13:21, Dave Abrahams wrote:
On Fri, Sep 3, 2010 at 2:03 PM, Larry Evans <cppljevans@suddenlink.net> wrote:
What it does do is make implementation of beta reduction easier
And why would a C++ programmer care about making the implementation of beta-reduction easier?
I thought that's essentially what mpl::apply<F,A> does. Let's see, from:
http://en.wikipedia.org/wiki/%CE%91_conversion#.CE.B1-conversion
there's:
Beta-reduction captures the idea of function application. Beta-reduction is defined in terms of substitution: the beta-reduction of ((λV.E) E′) is E[V := E′].
For example, assuming some encoding of 2, 7, *, we have the following β-reductions: ((λn.n*2) 7) → 7*2.
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.
OTOH, maybe I'm completely missing something.
-regards, Larry
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

On Fri, Sep 3, 2010 at 2:52 PM, Larry Evans <cppljevans@suddenlink.net> wrote:
And why would a C++ programmer care about making the implementation of beta-reduction easier?
I thought that's essentially what mpl::apply<F,A> does. Let's see, from:
http://en.wikipedia.org/wiki/%CE%91_conversion#.CE.B1-conversion
I've already read it; I know what it is.
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 ;-)
-- Dave Abrahams BoostPro Computing http://www.boostpro.com

On Fri, Sep 3, 2010 at 4:46 PM, Dave Abrahams <dave@boostpro.com> wrote:
On Fri, Sep 3, 2010 at 2:52 PM, Larry Evans <cppljevans@suddenlink.net> 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? And how so? -- David Sankel Sankel Software www.sankelsoftware.com 585 617 4748 (Office)

On Fri, Sep 3, 2010 at 5:22 PM, David Sankel <camior@gmail.com> wrote:
On Fri, Sep 3, 2010 at 4:46 PM, Dave Abrahams <dave@boostpro.com> wrote:
On Fri, Sep 3, 2010 at 2:52 PM, Larry Evans <cppljevans@suddenlink.net> 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. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

On Fri, Sep 3, 2010 at 3:42 PM, Dave Abrahams <dave@boostpro.com> wrote:
On Fri, Sep 3, 2010 at 5:22 PM, David Sankel <camior@gmail.com> wrote:
On Fri, Sep 3, 2010 at 4:46 PM, Dave Abrahams <dave@boostpro.com> wrote:
On Fri, Sep 3, 2010 at 2:52 PM, Larry Evans <cppljevans@suddenlink.net> 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.
Again, if built on Boost.Phoenix, could it not be just like this as some examples? using namespace boost::phoenix; bind(f, _1, 314)(42); bind(f, bind(g, _2_1), _1_1)(42); And whatever else...

On Fri, Sep 3, 2010 at 6:17 PM, OvermindDL1 <overminddl1@gmail.com> wrote:
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.
Again, if built on Boost.Phoenix, could it not be just like this as some examples?
using namespace boost::phoenix;
bind(f, _1, 314)(42);
bind(f, bind(g, _2_1), _1_1)(42);
And whatever else...
I'm sorry, but I don't understand how your question relates to the text you quoted. Actually, I can't parse the question either :-) -- Dave Abrahams BoostPro Computing http://www.boostpro.com

On 9/4/2010 5:42 AM, Dave Abrahams wrote:
On Fri, Sep 3, 2010 at 5:22 PM, David Sankel<camior@gmail.com> wrote:
On Fri, Sep 3, 2010 at 4:46 PM, Dave Abrahams<dave@boostpro.com> wrote:
On Fri, Sep 3, 2010 at 2:52 PM, Larry Evans<cppljevans@suddenlink.net> 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? 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. Regards, -- Joel de Guzman http://www.boostpro.com http://spirit.sf.net

On Fri, Sep 3, 2010 at 7:46 PM, Joel de Guzman <joel@boost-consulting.com>wrote:
On 9/4/2010 5:42 AM, Dave Abrahams wrote:
On Fri, Sep 3, 2010 at 5:22 PM, David Sankel<camior@gmail.com> wrote:
On Fri, Sep 3, 2010 at 4:46 PM, Dave Abrahams<dave@boostpro.com> wrote:
On Fri, Sep 3, 2010 at 2:52 PM, Larry Evans<cppljevans@suddenlink.net>
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)

On 9/4/2010 9:29 AM, David Sankel wrote:
On Fri, Sep 3, 2010 at 7:46 PM, Joel de Guzman<joel@boost-consulting.com>wrote:
On 9/4/2010 5:42 AM, Dave Abrahams wrote:
On Fri, Sep 3, 2010 at 5:22 PM, David Sankel<camior@gmail.com> wrote:
On Fri, Sep 3, 2010 at 4:46 PM, Dave Abrahams<dave@boostpro.com> wrote:
On Fri, Sep 3, 2010 at 2:52 PM, Larry Evans<cppljevans@suddenlink.net>
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

On Fri, Sep 3, 2010 at 10:05 PM, Joel de Guzman <joel@boost-consulting.com>wrote:
On 9/4/2010 9:29 AM, David Sankel wrote:
On Fri, Sep 3, 2010 at 7:46 PM, Joel de Guzman<joel@boost-consulting.com
wrote:
On 9/4/2010 5:42 AM, Dave Abrahams wrote:
On Fri, Sep 3, 2010 at 5:22 PM, David Sankel<camior@gmail.com> wrote:
On Fri, Sep 3, 2010 at 4:46 PM, Dave Abrahams<dave@boostpro.com>
wrote:
On Fri, Sep 3, 2010 at 2:52 PM, Larry Evans<cppljevans@suddenlink.net
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)

On 9/4/2010 10:40 AM, David Sankel wrote:
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.
Yep. That's the mantra of Phoenix.
Your lambda uses brackets and I'm assuming this is for sequencing zero argument procedures.
Yeah. It's use and syntax is historical. The intent is to be as close as possible to C++ and to somehow emulate C++ statements. It is used that way, e.g.: if_(c) [ dothis() ] .else_ [ dothat() ] It's not only for the sake of being clever. More importantly, C++ users connect to this easily compared to other concocted means. It is instantly recognizable.
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) ) )
Why keep outer if you have scoped_argument?
Agreed. outer is(was) built on top of scoped_argument anyway. Regards -- Joel de Guzman http://www.boostpro.com http://spirit.sf.net

On Fri, Sep 3, 2010 at 11:13 PM, Joel de Guzman <joel@boost-consulting.com>wrote:
On 9/4/2010 10:40 AM, David Sankel wrote:
Your lambda uses brackets and I'm assuming this is for sequencing zero argument procedures.
Yeah. It's use and syntax is historical. The intent is to be as close as possible to C++ and to somehow emulate C++ statements. It is used that way, e.g.:
if_(c) [ dothis() ] .else_ [ dothat() ]
It's not only for the sake of being clever. More importantly, C++ users connect to this easily compared to other concocted means. It is instantly recognizable.
I think there's virtue in using syntactic sugar to expose a lambda calculus engine in a way that resembles C++ code. My focus for DBB is to create a powerful, yet simple, engine that, among other things, others can build syntax sugar on top of -- whether that be a C++ resembling lambda syntax or functional reactive programming syntax. As an aside, I taught a fork of DBB a new trick today, fix[1]. This enables one to declare recursive functions like factorial and fib. With some syntax sugar this could be quite nifty: //llam → lazy lambda is only evaluated when needed //_a_b → the b argument of the a nested lambda //_a → the a nested lambda auto fact = iff( _1_1 == 1 , 1 , llam( _2_1 * _2( _2_1 - 1 ) ) ) ⇒ syntax sugar traslated to fix fix( lam( lam( iff( _1_1 == 1 , 1 , llam( _2_1 * _3_1( _2_1 - 1 ) ) ) ) ) ); I'm also pretty confidant I can make the evaluator do tail call optimization [1] http://en.wikipedia.org/wiki/Fixed_point_combinator [2] http://en.wikipedia.org/wiki/Tail_call#Tail_call_optimization -- David Sankel Sankel Software www.sankelsoftware.com 585 617 4748 (Office)

//llam → lazy lambda is only evaluated when needed //_a_b → the b argument of the a nested lambda //_a → the a nested lambda
auto fact = iff( _1_1 == 1 , 1 , llam( _2_1 * _2( _2_1 - 1 ) ) )
⇒ syntax sugar traslated to fix
fix( lam( lam( iff( _1_1 == 1 , 1 , llam( _2_1 * _3_1( _2_1 - 1 ) ) ) ) ) );
I am not sure if this is entirely relevant, but there was a discussion on boost::proto list about defining mutually recursive lambdas : http://www.mail-archive.com/proto@lists.boost.org/msg00074.html I haven't gone through all the discussion on this thread, but the recursive definition of "fact" caught my eye. Can this be used for mutually recursive functions as well? Manjunath
I'm also pretty confidant I can make the evaluator do tail call optimization
[1] http://en.wikipedia.org/wiki/Fixed_point_combinator [2] http://en.wikipedia.org/wiki/Tail_call#Tail_call_optimization
-- David Sankel Sankel Software www.sankelsoftware.com 585 617 4748 (Office) _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

On Sat, Sep 4, 2010 at 2:53 PM, Manjunath Kudlur <keveman@gmail.com> wrote:
//llam → lazy lambda is only evaluated when needed //_a_b → the b argument of the a nested lambda //_a → the a nested lambda
auto fact = iff( _1_1 == 1 , 1 , llam( _2_1 * _2( _2_1 - 1 ) ) )
⇒ syntax sugar traslated to fix
fix( lam( lam( iff( _1_1 == 1 , 1 , llam( _2_1 * _3_1( _2_1 - 1 ) ) ) ) ) );
I am not sure if this is entirely relevant, but there was a discussion on boost::proto list about defining mutually recursive lambdas : http://www.mail-archive.com/proto@lists.boost.org/msg00074.html I haven't gone through all the discussion on this thread, but the recursive definition of "fact" caught my eye. Can this be used for mutually recursive functions as well?
Yes, thanks to Bekič's theorem (I don't have a good reference but here is one[1] that leads to more.) [1] http://www.haskell.org/pipermail/haskell-cafe/2007-March/023585.html -- David Sankel Sankel Software www.sankelsoftware.com 585 617 4748 (Office)

On 9/5/10 2:07 AM, David Sankel wrote:
On Fri, Sep 3, 2010 at 11:13 PM, Joel de Guzman <joel@boost-consulting.com>wrote:
On 9/4/2010 10:40 AM, David Sankel wrote:
Your lambda uses brackets and I'm assuming this is for sequencing zero argument procedures.
Yeah. It's use and syntax is historical. The intent is to be as close as possible to C++ and to somehow emulate C++ statements. It is used that way, e.g.:
if_(c) [ dothis() ] .else_ [ dothat() ]
It's not only for the sake of being clever. More importantly, C++ users connect to this easily compared to other concocted means. It is instantly recognizable.
I think there's virtue in using syntactic sugar to expose a lambda calculus engine in a way that resembles C++ code.
My focus for DBB is to create a powerful, yet simple, engine that, among other things, others can build syntax sugar on top of -- whether that be a C++ resembling lambda syntax or functional reactive programming syntax.
As an aside, I taught a fork of DBB a new trick today, fix[1]. This enables one to declare recursive functions like factorial and fib. With some syntax sugar this could be quite nifty:
//llam → lazy lambda is only evaluated when needed //_a_b → the b argument of the a nested lambda //_a → the a nested lambda
auto fact = iff( _1_1 == 1 , 1 , llam( _2_1 * _2( _2_1 - 1 ) ) )
⇒ syntax sugar traslated to fix
fix( lam( lam( iff( _1_1 == 1 , 1 , llam( _2_1 * _3_1( _2_1 - 1 ) ) ) ) ) );
I'm also pretty confidant I can make the evaluator do tail call optimization
[1] http://en.wikipedia.org/wiki/Fixed_point_combinator [2] http://en.wikipedia.org/wiki/Tail_call#Tail_call_optimization
Rockin! David, I am very interested with what you are doing. I love fine-grained elements that compose well into very powerful constructs. Phoenix happens to be designed with that in mind and I believe your ideas can be incorporated into phoenix without disrupting the higher level (sugar and all) interfaces that are in place. So, instead of having yet another bind library, why don't you just work on phoenix-3 with your ideas? I am very open to new ideas. Regards, -- Joel de Guzman http://www.boostpro.com http://spirit.sf.net

-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost- bounces@lists.boost.org] On Behalf Of Joel de Guzman Sent: Friday, September 03, 2010 8:14 PM To: boost@lists.boost.org Subject: Re: [boost] De Bruijn Bind (alternate bind syntax) Interest?
On 9/4/2010 10:40 AM, David Sankel wrote:
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.
Yep. That's the mantra of Phoenix.
Your lambda uses brackets and I'm assuming this is for sequencing zero argument procedures.
Yeah. It's use and syntax is historical. The intent is to be as close as possible to C++ and to somehow emulate C++ statements. It is used that way, e.g.:
if_(c) [ dothis() ] .else_ [ dothat() ]
If we're talking C++0x you could also use initializer-list constructors: auto con = if_( c ), then_ { dothis0(), dothis1(), dothis2() }, else_ { dothat0(), dothat1(), dothat2() }; For an Adam/Eve DSEL in C++ we used this syntax: sheet save_file = "save_file" == sheet { interface == section { var{"file_name"} == [](arg_map args){ return any(""); }, var{"file_type"} == "txt", (var{"password"} <= (var{"file_type"}, var{"password1"})) == [](arg_map args) { return args.get<string>("file_type") == "txt" ? any("") : args["password1"]; } // more constraints }, output == section { // more constraints }, invariant == section { // more constraints } }; Which is assuredly overkill for a DBB, but still an option for syntax. BTW, initializer lists work great for an inline JSON library, i.e., json myjson = dict { key{"foo"} == list [ "baz", "bif", list [ "bop", "quux" ], key{"baz"} == "bar" };
It's not only for the sake of being clever. More importantly, C++ users connect to this easily compared to other concocted means. It is instantly recognizable.
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) ) )
Why keep outer if you have scoped_argument?
Agreed. outer is(was) built on top of scoped_argument anyway.
Regards -- Joel de Guzman http://www.boostpro.com http://spirit.sf.net
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

On Fri, Sep 3, 2010 at 5:46 PM, Joel de Guzman <joel@boost-consulting.com> wrote:
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?
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.
Actually that is pretty close to what I was describing, but imagine outer being like this to indicate how many levels up to go: for_each(_1, lambda [ push_back(_1, outer<1>(_2)) ] ) Then simplify the syntax by doing something like outer<1,2>, which could then have a predefined value called _1_2 or so... I like outer, guess it would be fully powerful if it could specify how many 'outer' levels to go out to with a template param like the above?

On 9/4/2010 9:55 AM, OvermindDL1 wrote:
On Fri, Sep 3, 2010 at 5:46 PM, Joel de Guzman <joel@boost-consulting.com> wrote:
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?
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.
Actually that is pretty close to what I was describing, but imagine outer being like this to indicate how many levels up to go:
for_each(_1, lambda [ push_back(_1, outer<1>(_2)) ] )
Then simplify the syntax by doing something like outer<1,2>, which could then have a predefined value called _1_2 or so...
I like outer, guess it would be fully powerful if it could specify how many 'outer' levels to go out to with a template param like the above?
Yep. We had that too. There is also the possibility to write your own scoped place-holders like _1_2. I should reinstate them. I don't recall now why they were decommissioned. Perhaps because you can do what it does with local variables and lambda? I don't recall anymore. Regards, -- Joel de Guzman http://www.boostpro.com http://spirit.sf.net

On Fri, Sep 3, 2010 at 5:42 PM, Dave Abrahams <dave@boostpro.com> wrote:
On Fri, Sep 3, 2010 at 5:22 PM, David Sankel <camior@gmail.com> wrote:
On Fri, Sep 3, 2010 at 4:46 PM, Dave Abrahams <dave@boostpro.com> wrote:
On Fri, Sep 3, 2010 at 2:52 PM, Larry Evans <cppljevans@suddenlink.net> 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.
I agree that, so far, I haven't presented a very easy path to understanding De Bruijn notation. I do think, on the other hand it can be completely explained succinctly in simple English. Some brain time on this is probably worthwhile.
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.
I'd say the authors have failed in making bind intuitively graspable. Users may certainly feel like they know what it does, but in reality they don't understand the needlessly complex semantics. These surface once someone does something even remotely complicated. I'm quite confidant that once a user begins to approach counter-intuitive things like protect and apply they'll try a non-lambda approach that would be much more clearly expressed in lambda world.
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.
"ordinary lambdas" being those that don't need protect, don't need apply, aren't being passed to other functions, and aren't using other functions? The only reason you consider these uses ordinary is because anything out of "ordinary" is too complex to think about with bind semantics. De Bruijn bind (DBB) can make a new, much more compelling, ordinary. But to answer your question. I do think that DBB can be easily made to be as succinct as "ordinary" bind, but this might make learning DBB (and unlearning bind limitations), more difficult for new users. -- David Sankel Sankel Software www.sankelsoftware.com 585 617 4748 (Office)

On 09/03/10 15:46, Dave Abrahams wrote:
On Fri, Sep 3, 2010 at 2:52 PM, Larry Evans <cppljevans@suddenlink.net> wrote:
And why would a C++ programmer care about making the implementation of beta-reduction easier?
I thought that's essentially what mpl::apply<F,A> does. Let's see, from:
http://en.wikipedia.org/wiki/%CE%91_conversion#.CE.B1-conversion
I've already read it; I know what it is.
Yes, I figured that; however, that made me wonder why you asked the question. The only way I could answer the question, I thought, was to provide the reference. Besides, even though you are familiar with it, others reading the thread might not be, which is another reason why the reference was provided. Sorry, I didn't mean to imply that you didn't know about it. I guess the reason is that the question was asked from the perspective of the user, not the implementer, of the library. That perspective is also suggested by your followup statement:
this would make it easier for bind library writers at the expense of usability
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 ;-)
OK, but at the time I was having trouble, I didn't know of an alternative. I'll try to remember the particulars of the problem I had to illustrate the point. That also might give Mr. Sankel another use case. -respectfully Larry

On 09/03/10 17:12, Larry Evans wrote:
On 09/03/10 15:46, Dave Abrahams wrote: [snip]
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 ;-)
OK, but at the time I was having trouble, I didn't know of an alternative. I'll try to remember the particulars of the problem I had to illustrate the point. That also might give Mr. Sankel another use case.
The attached illustrates the difficulty I was having with mpl::apply. Maybe the apply_apply typedef would be a good use-case for De Bruijn's method. I suspect the problem with apply_apply is something akin to the variable capture problem (search for "variable getting captured in: http://en.wikipedia.org/wiki/%CE%91_conversion#.CE.B1-conversion ). That problem is what De Bruijn's method is supposed to solve. In apply_apply the "variables" are the _1 and _2. Since those are used both in the op typedef and in the apply_apply typedef and the apply typedef uses op, I'm guessing that leads to what amounts to the variable capture. Of course Mr. Sankel's code doesn't operate on types, only values; however, I'm guessing it could be transformed to work on types also, just like much of mpl's code is a translation of value functions to type functions. -Larry

On 09/04/10 08:24, Larry Evans wrote: [snip]
The attached illustrates the difficulty I was having with mpl::apply. Maybe the apply_apply typedef would be a good use-case for De Bruijn's method. [snip] Attached is much simplified code that still illustrates problem. Compile errors included:
>' apply_apply.cpp:39: instantiated from here /home/evansl/prog_dev/boost-svn/ro/boost_1_44_0/boost/mpl/aux_/preprocessed/gcc/apply_wrap.hpp:39: error: 'boost::mpl::apply<boost::mpl::next<mpl_::arg<1> >, mpl_::arg<1> > ::apply' names 'boost::mpl::apply< boost::mpl::next<mpl_::arg<1> >, mpl_::arg<1> >', which is not a class template apply_apply.cpp:33: error: 'type' in class 'boost::mpl::apply< boost::mpl::apply<boost::mpl::next<mpl_::arg<1> >, mpl_::arg<1> > , mpl_::int_<1> >' does not name a type make: *** [/home/evansl/prog_dev/boost-svn/ro/boost_1_44_0/sandbox-local/build/gcc4_4v/boost-svn/ro/boost_1_44_0/sand

On Sat, Sep 4, 2010 at 10:55 AM, Larry Evans <cppljevans@suddenlink.net>wrote:
On 09/04/10 08:24, Larry Evans wrote: [snip]
The attached illustrates the difficulty I was having with mpl::apply. Maybe the apply_apply typedef would be a good use-case for De Bruijn's method. [snip] Attached is much simplified code that still illustrates problem. Compile errors included:
Thanks for the example. If a De Bruijn indices were used, this would look like typedef< app< lam< app< op, _1_1 > > , int_<1> > >::type apply_apply; I think this is a valid example of the needlessly cryptic bind/lambda semantics that are widespread. -- David Sankel Sankel Software www.sankelsoftware.com 585 617 4748 (Office)

On 09/04/10 13:54, David Sankel wrote:
On Sat, Sep 4, 2010 at 10:55 AM, Larry Evans <cppljevans@suddenlink.net>wrote:
On 09/04/10 08:24, Larry Evans wrote: [snip]
The attached illustrates the difficulty I was having with mpl::apply. Maybe the apply_apply typedef would be a good use-case for De Bruijn's method. [snip] Attached is much simplified code that still illustrates problem. Compile errors included:
Thanks for the example. If a De Bruijn indices were used, this would look like
typedef< app< lam< app< op, _1_1 > > , int_<1> > >::type apply_apply;
Typo: typedef<
I think this is a valid example of the needlessly cryptic bind/lambda semantics that are widespread.
I tried translating the above code back to the, let's call at the type 'universe' as opposed to the value 'universe'. I'm using universe because that's a term used by nuprl: http://www.cs.cornell.edu/Info/People/sfa/Nuprl/NuprlPrimitives/Xuniverse_do... to describe hierarchy of types and also because the only other name I could think of was domain, and that's used by proto and for other reasons. Another candidate was 'kind': http://www.haskell.org/haskellwiki/Kind but I thought of 'universe' first and it seemed more general. However, compiling my first try( the attached) gives: [COMPILATION]
make -f my-make.mk /home/evansl/download/gcc/4.5.1-release/install/bin/g++ -Iinclude -I/home/evansl/prog_dev/boost-svn/ro/boost_1_44_0 -std=gnu++0x src/apply_apply.cpp src/test.cpp -o bin/apply_apply.exe src/apply_apply.cpp: In function 'int main()': src/apply_apply.cpp:63:28: error: cannot convert 'App<int (*)(int), boost::fusion::vector1<int> >' to 'int' in initialization make: *** [bin/apply_apply.exe] Error 1 [/COMPILATION]
What am I doing wrong? TIA. -regards, Larry

On Sun, Sep 5, 2010 at 7:56 PM, Larry Evans <cppljevans@suddenlink.net>wrote:
On 09/04/10 13:54, David Sankel wrote:
On Sat, Sep 4, 2010 at 10:55 AM, Larry Evans <cppljevans@suddenlink.net wrote:
On 09/04/10 08:24, Larry Evans wrote: [snip]
The attached illustrates the difficulty I was having with mpl::apply. Maybe the apply_apply typedef would be a good use-case for De Bruijn's method. [snip] Attached is much simplified code that still illustrates problem. Compile errors included:
Thanks for the example. If a De Bruijn indices were used, this would look like
typedef< app< lam< app< op, _1_1 > > , int_<1> > >::type apply_apply;
Typo:
typedef<
I think this is a valid example of the needlessly cryptic bind/lambda semantics that are widespread.
I tried translating the above code back to the, let's call at the type 'universe' as opposed to the value 'universe'. I'm using universe because that's a term used by nuprl:
http://www.cs.cornell.edu/Info/People/sfa/Nuprl/NuprlPrimitives/Xuniverse_do...
to describe hierarchy of types and also because the only other name I could think of was domain, and that's used by proto and for other reasons. Another candidate was 'kind':
http://www.haskell.org/haskellwiki/Kind
but I thought of 'universe' first and it seemed more general.
However, compiling my first try( the attached) gives: [COMPILATION]
make -f my-make.mk /home/evansl/download/gcc/4.5.1-release/install/bin/g++ -Iinclude -I/home/evansl/prog_dev/boost-svn/ro/boost_1_44_0 -std=gnu++0x src/apply_apply.cpp src/test.cpp -o bin/apply_apply.exe src/apply_apply.cpp: In function 'int main()': src/apply_apply.cpp:63:28: error: cannot convert 'App<int (*)(int), boost::fusion::vector1<int> >' to 'int' in initialization make: *** [bin/apply_apply.exe] Error 1 [/COMPILATION]
What am I doing wrong?
int result = app( next, 1 ); With the reference implementation, app should always be used within a lam. In this case you don't need a lam: int result = next( 1 ). But if you really wanted to make a nullary function (note the extra '()' at the end): int result = lam<0>( app( next, 1 ) )(); Looking at the subsequent expression: int result = app(lam_app,1); Again we can apply the function directly here: int result = lam_app(1); Does that help? -David -- David Sankel Sankel Software www.sankelsoftware.com 585 617 4748 (Office)

On 09/07/10 10:41, David Sankel wrote:
On Sun, Sep 5, 2010 at 7:56 PM, Larry Evans <cppljevans@suddenlink.net>wrote: [snip]
However, compiling my first try( the attached) gives: [snip] What am I doing wrong?
int result = app( next, 1 );
With the reference implementation, app should always be used within a lam. In this case you don't need a lam:
int result = next( 1 ).
But if you really wanted to make a nullary function (note the extra '()' at the end):
int result = lam<0>( app( next, 1 ) )();
Looking at the subsequent expression:
int result = app(lam_app,1);
Again we can apply the function directly here:
int result = lam_app(1);
Does that help?
I tried `lam_app(1)` in the following main: [CODE "apply_apply.cpp"] int main() { auto op = lam<1>( app(next,_1_1 ) ); int op_1 = op(1); auto lam_app = lam<1>( app(op,_1_1) ); int lam_app_1 = #if 1 lam_app(1); #else 2; #endif return op_1 + lam_app_1; } [/CODE "apply_apply.cpp"] However, this resulted in: [COMPILE_ERRORS "apply_apply.cpp"] /home/evansl/download/gcc/4.5.1-release/install/bin/g++ -I../include -I/home/evansl/prog_dev/boost-svn/ro/boost_1_44_0 -std=gnu++0x ./apply_apply.cpp -o ../bin/apply_apply.exe In file included from /home/evansl/prog_dev/boost-svn/ro/boost_1_44_0/boost/fusion/functional/invocation/invoke.hpp:41:0, from /home/evansl/prog_dev/boost-svn/ro/boost_1_44_0/boost/fusion/functional/adapter/fused.hpp:16, from /home/evansl/prog_dev/boost-svn/ro/boost_1_44_0/boost/fusion/include/fused.hpp:10, from ../include/DeBruijnBind.hpp:24, from ./apply_apply.cpp:8: /home/evansl/prog_dev/boost-svn/ro/boost_1_44_0/boost/utility/result_of.hpp: In instantiation of 'boost::detail::result_of_nested_result<Abs<1, App<int (*)(int), boost::fusion::transform_view<const boost::fusion::vector1<arg<1, 1> >, CReduce1<boost::fusion::pair<mpl_::int_<2>, boost::fusion::vector1<int>
, boost::fusion::void_> > >, Abs<1, App<int (*)(int), boost::fusion::transform_view<const boost::fusion::vector1<arg<1, 1> >, CReduce1<boost::fusion::pair<mpl_::int_<2>, boost::fusion::vector1<int> , boost::fusion::void_> > >(int&)>': /home/evansl/prog_dev/boost-svn/ro/boost_1_44_0/boost/utility/result_of.hpp:87:1: instantiated from 'boost::detail::tr1_result_of_impl<Abs<1, App<int (*)(int), boost::fusion::transform_view<const boost::fusion::vector1<arg<1, 1> >, CReduce1<boost::fusion::pair<mpl_::int_<2>, boost::fusion::vector1<int> , boost::fusion::void_> > >, Abs<1, App<int (*)(int), boost::fusion::transform_view<const boost::fusion::vector1<arg<1, 1> >, CReduce1<boost::fusion::pair<mpl_::int_<2>, boost::fusion::vector1<int> , boost::fusion::void_> > >(int&), false>' /home/evansl/prog_dev/boost-svn/ro/boost_1_44_0/boost/utility/detail/result_of_iterate.hpp:33:65: instantiated from 'boost::tr1_result_of<Abs<1, App<int (*)(int), boost::fusion::transform_view<const boost::fusion::vector1<arg<1, 1> >, CReduce1<boost::fusion::pair<mpl_::int_<2>, boost::fusion::vector1<int> , boost::fusion::void_> > >(int&)>' /home/evansl/prog_dev/boost-svn/ro/boost_1_44_0/boost/utility/detail/result_of_iterate.hpp:81:46: instantiated from 'boost::result_of<Abs<1, App<int (*)(int), boost::fusion::transform_view<const boost::fusion::vector1<arg<1, 1> >, CReduce1<boost::fusion::pair<mpl_::int_<2>, boost::fusion::vector1<int> , boost::fusion::void_> > >(int&)>' /home/evansl/prog_dev/boost-svn/ro/boost_1_44_0/boost/fusion/functional/invocation/invoke.hpp:199:60: instantiated from 'boost::fusion::detail::invoke_impl<Abs<1, App<int (*)(int), boost::fusion::transform_view<const boost::fusion::vector1<arg<1, 1> >, CReduce1<boost::fusion::pair<mpl_::int_<2>, boost::fusion::vector1<int> , boost::fusion::void_> > >, boost::fusion::transform_view<const boost::fusion::vector1<arg<1, 1> >, CReduce1<boost::fusion::pair<mpl_::int_<1>, boost::fusion::vector1<int> , boost::fusion::void_>, 1, false, true>' /home/evansl/prog_dev/boost-svn/ro/boost_1_44_0/boost/fusion/functional/invocation/invoke.hpp:159:30: instantiated from 'boost::fusion::result_of::invoke<Abs<1, App<int (*)(int), boost::fusion::transform_view<const boost::fusion::vector1<arg<1, 1> >, CReduce1<boost::fusion::pair<mpl_::int_<2>, boost::fusion::vector1<int> , boost::fusion::void_> > >&, boost::fusion::transform_view<const boost::fusion::vector1<arg<1, 1> >, CReduce1<boost::fusion::pair<mpl_::int_<1>, boost::fusion::vector1<int> , boost::fusion::void_> >' ./apply_apply.cpp:65:18: instantiated from here /home/evansl/prog_dev/boost-svn/ro/boost_1_44_0/boost/utility/result_of.hpp:80:1: error: no class template named 'result' in 'struct Abs<1, App<int (*)(int), boost::fusion::transform_view<const boost::fusion::vector1<arg<1, 1> >, CReduce1<boost::fusion::pair<mpl_::int_<2>, boost::fusion::vector1<int> , boost::fusion::void_> > >' In file included from /home/evansl/prog_dev/boost-svn/ro/boost_1_44_0/boost/preprocessor/iteration/detail/iter/forward1.hpp:52:0, from /home/evansl/prog_dev/boost-svn/ro/boost_1_44_0/boost/fusion/functional/invocation/invoke.hpp:93, from /home/evansl/prog_dev/boost-svn/ro/boost_1_44_0/boost/fusion/functional/adapter/fused.hpp:16, from /home/evansl/prog_dev/boost-svn/ro/boost_1_44_0/boost/fusion/include/fused.hpp:10, from ../include/DeBruijnBind.hpp:24, from ./apply_apply.cpp:8: /home/evansl/prog_dev/boost-svn/ro/boost_1_44_0/boost/fusion/functional/invocation/invoke.hpp: In instantiation of 'boost::fusion::detail::invoke_impl<Abs<1, App<int (*)(int), boost::fusion::transform_view<const boost::fusion::vector1<arg<1, 1> >, CReduce1<boost::fusion::pair<mpl_::int_<2>, boost::fusion::vector1<int> , boost::fusion::void_> > >, boost::fusion::transform_view<const boost::fusion::vector1<arg<1, 1> >, CReduce1<boost::fusion::pair<mpl_::int_<1>, boost::fusion::vector1<int> , boost::fusion::void_>, 1, false, true>': /home/evansl/prog_dev/boost-svn/ro/boost_1_44_0/boost/fusion/functional/invocation/invoke.hpp:159:30: instantiated from 'boost::fusion::result_of::invoke<Abs<1, App<int (*)(int), boost::fusion::transform_view<const boost::fusion::vector1<arg<1, 1> >, CReduce1<boost::fusion::pair<mpl_::int_<2>, boost::fusion::vector1<int> , boost::fusion::void_> > >&, boost::fusion::transform_view<const boost::fusion::vector1<arg<1, 1> >, CReduce1<boost::fusion::pair<mpl_::int_<1>, boost::fusion::vector1<int> , boost::fusion::void_> >' ./apply_apply.cpp:65:18: instantiated from here /home/evansl/prog_dev/boost-svn/ro/boost_1_44_0/boost/fusion/functional/invocation/invoke.hpp:199:60: error: no type named 'type' in 'struct boost::result_of<Abs<1, App<int (*)(int), boost::fusion::transform_view<const boost::fusion::vector1<arg<1, 1> >, CReduce1<boost::fusion::pair<mpl_::int_<2>, boost::fusion::vector1<int> , boost::fusion::void_> > >(int&)>' In file included from /home/evansl/prog_dev/boost-svn/ro/boost_1_44_0/boost/fusion/functional/invocation/invoke.hpp:41:0, from /home/evansl/prog_dev/boost-svn/ro/boost_1_44_0/boost/fusion/functional/adapter/fused.hpp:16, from /home/evansl/prog_dev/boost-svn/ro/boost_1_44_0/boost/fusion/include/fused.hpp:10, from ../include/DeBruijnBind.hpp:24, from ./apply_apply.cpp:8: /home/evansl/prog_dev/boost-svn/ro/boost_1_44_0/boost/utility/result_of.hpp: In instantiation of 'boost::detail::result_of_nested_result<const Abs<1, App<int (*)(int), boost::fusion::transform_view<const boost::fusion::vector1<arg<1, 1> >, CReduce1<boost::fusion::pair<mpl_::int_<2>, boost::fusion::vector1<int> , boost::fusion::void_> > >, const Abs<1, App<int (*)(int), boost::fusion::transform_view<const boost::fusion::vector1<arg<1, 1> >, CReduce1<boost::fusion::pair<mpl_::int_<2>, boost::fusion::vector1<int> , boost::fusion::void_> > >(int&)>': /home/evansl/prog_dev/boost-svn/ro/boost_1_44_0/boost/utility/result_of.hpp:87:1: instantiated from 'boost::detail ... [/COMPILE_ERRORS "apply_apply.cpp"]
and the errors go on for about 40 more lines. Any ideas? TIA. -Larry

On Tue, Sep 7, 2010 at 3:42 PM, Larry Evans <cppljevans@suddenlink.net>wrote:
On 09/07/10 10:41, David Sankel wrote:
On Sun, Sep 5, 2010 at 7:56 PM, Larry Evans <cppljevans@suddenlink.net wrote: [snip]
However, compiling my first try( the attached) gives: [snip] What am I doing wrong?
int result = app( next, 1 );
With the reference implementation, app should always be used within a lam. In this case you don't need a lam:
int result = next( 1 ).
But if you really wanted to make a nullary function (note the extra '()' at the end):
int result = lam<0>( app( next, 1 ) )();
Looking at the subsequent expression:
int result = app(lam_app,1);
Again we can apply the function directly here:
int result = lam_app(1);
Does that help?
I tried `lam_app(1)` in the following main:
[CODE "apply_apply.cpp"] int main() { auto op = lam<1>( app(next,_1_1 ) ); int op_1 = op(1); auto lam_app = lam<1>( app(op,_1_1) ); int lam_app_1 = #if 1 lam_app(1); #else 2; #endif return op_1 + lam_app_1; } [/CODE "apply_apply.cpp"]
However, this resulted in:
[COMPILE_ERRORS "apply_apply.cpp"] [/COMPILE_ERRORS "apply_apply.cpp"]
and the errors go on for about 40 more lines.
Any ideas?
Hrm, it works for me. I wonder if you're using an older version. I've added your example to the repository's main.cpp. Are you able to compile the repository's version? -David -- David Sankel Sankel Software www.sankelsoftware.com 585 617 4748 (Office)

On 09/07/10 20:40, David Sankel wrote:
On Tue, Sep 7, 2010 at 3:42 PM, Larry Evans <cppljevans@suddenlink.net>wrote: [snip]
[COMPILE_ERRORS "apply_apply.cpp"] [/COMPILE_ERRORS "apply_apply.cpp"]
and the errors go on for about 40 more lines.
Any ideas?
Hrm, it works for me. I wonder if you're using an older version. I've added your example to the repository's main.cpp. Are you able to compile the repository's version? I must not have the proper repository because attached output of:
hg diff src/main.cpp shows no diff's reflecting my example is now included. I'm new to hg; hence, I may be doing something wrong. However, IIRC, all I did was an `hg clone <debruijn-repository-url>`. Also: hg diff include/DeBruijnBind.hpp shows no differences. .hg/hgrc contains: [paths] default = http://bitbucket.org/camior/de-bruijn-bind Any ideas why I'm not seeing the proper diffs? -Larry

On Tue, Sep 7, 2010 at 11:11 PM, Larry Evans <cppljevans@suddenlink.net>wrote:
On 09/07/10 20:40, David Sankel wrote:
On Tue, Sep 7, 2010 at 3:42 PM, Larry Evans <cppljevans@suddenlink.net wrote: [snip]
[COMPILE_ERRORS "apply_apply.cpp"] [/COMPILE_ERRORS "apply_apply.cpp"]
and the errors go on for about 40 more lines.
Any ideas?
Hrm, it works for me. I wonder if you're using an older version. I've added your example to the repository's main.cpp. Are you able to compile the repository's version? I must not have the proper repository because attached output of:
hg diff src/main.cpp
hg diff shows the differences between the version in your folder and the one you have checked out. Since you haven't modified anything, hg diff doesn't show anything. To update to the latest version, go to any folder in your copy of the repository and do: hg pull hg update I use hgtk to see the history interactively (hgtk hist). The bitbucket site (http://bitbucket.org/camior/de-bruijn-bind) also allows you to easily see the diffs of the different revisions. Does this help? David -- David Sankel Sankel Software www.sankelsoftware.com 585 617 4748 (Office)

On 09/07/10 22:26, David Sankel wrote: [snip]
I must not have the proper repository because attached output of:
hg diff src/main.cpp
hg diff shows the differences between the version in your folder and the one you have checked out. Since you haven't modified anything, hg diff doesn't show anything.
To update to the latest version, go to any folder in your copy of the repository and do:
hg pull hg update
I use hgtk to see the history interactively (hgtk hist). The bitbucket site (http://bitbucket.org/camior/de-bruijn-bind) also allows you to easily see the diffs of the different revisions.
Does this help? Yes! src/main.cpp now compiles and runs. Thanks.
-Larry

On Fri, Sep 3, 2010 at 1:09 PM, Dave Abrahams <dave@boostpro.com> wrote:
Can someone explain to me, in English, what this technique allows me to express that I couldn't otherwise?
Is this just about allowing generic composition of lambdas?
I'll take a stab at this too. De Bruijn (pronounced like day-brown) bind allows for the declaration of higher-order (has the possibility of taking functions as arguments and returning them), polymorphic (compile-time variety), functions as C++ expressions (on the right hand side of the equal sign). This has overlap with both boost bind, boost lambda, and phoenix. What is unique to De Bruijn is: 1. There is a direct and simple translation to and from lambda calculus. bind and lambda cover only a small portion of lambda calculus in comparison (I don't know about phoenix). So De Bruijn bind can express more functions than those other libraries. 2. The syntax is extremely simple and easy to understand when compared to bind's boost::protect semantics and boost.lambda's operator overloads. This makes De Bruijn an ideal candidate for a back-end evaluator for more complex DSELs. 3. The simplicity of the semantics will give users a relatively light (for example, when compared to bind) path to unlocking all of the library's potential. 4. The use of De Bruijn indices is a clever way to side-step the whole function argument name issue, retain the full power of lambda calculus, and have easily readable and writable code. David -- David Sankel Sankel Software www.sankelsoftware.com 585 617 4748 (Office)

David Sankel wrote:
[snip an explanation of the benefits] Might this be to the existing lambda libraries what proto is to DSELs? That is, a new, simpler foundation that can be extended in numerous other ways, too? _____ Rob Stewart robert.stewart@sig.com Software Engineer, Core Software using std::disclaimer; Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

On Fri, Sep 3, 2010 at 2:34 PM, Stewart, Robert <Robert.Stewart@sig.com>wrote:
David Sankel wrote:
[snip an explanation of the benefits]
Might this be to the existing lambda libraries what proto is to DSELs? That is, a new, simpler foundation that can be extended in numerous other ways, too?
Yes! -- David Sankel Sankel Software www.sankelsoftware.com 585 617 4748 (Office)

On Fri, Sep 3, 2010 at 10:09, Dave Abrahams <dave@boostpro.com> wrote:
Can someone explain to me, in English, what this technique allows me to express that I couldn't otherwise?
Is this just about allowing generic composition of lambdas?
It feels like it to me. What I've understood so far is that _0_n and _n are essentially the same, but that _0_n requires a lambda to scope them explicitly (sortof like protect). The advantage of _y_n is then that you can refer to the _ns in enclosing lambdas (aka outside of y protects). Am I on the right track, David? Thanks, ~ Scott

On Sat, Sep 4, 2010 at 2:09 AM, Scott McMurray <me22.ca+boost@gmail.com>wrote:
On Fri, Sep 3, 2010 at 10:09, Dave Abrahams <dave@boostpro.com> wrote:
Can someone explain to me, in English, what this technique allows me to express that I couldn't otherwise?
Is this just about allowing generic composition of lambdas?
It feels like it to me.
What I've understood so far is that _0_n and _n are essentially the same,
I've changed to 1 based indexing for both the abstraction number and the argument number. _n in a boost.bind that isn't enclosed in another boost.bind is the same as _1_n in DBB. _n in an unprotected boost.bind that is enclosed in 1 other boost bind is the same as _2_n in DBB. _n in an unprotected boost.bind that is enclosed in an unprotected boost bind that is enclosed in yet another unprotected boost bind is the same as _3_n in DBB.
but that _0_n requires a lambda to scope them explicitly (sortof like protect).
Right now for the particular case of _1_n, an enclosing lam is required, but that need not necessarily be the case. It is similar to protect only in that they enclose other terms, that's where the similarity ends.
The advantage of _y_n is then that you can refer to the _ns in enclosing lambdas (aka outside of y protects).
Am I on the right track, David?
Yes, that is the advantage of De Bruijn indices when compared to protects. The enclosing lambdas' arguments can be both referred to and mixed. David -- David Sankel Sankel Software www.sankelsoftware.com 585 617 4748 (Office)

At Thu, 2 Sep 2010 15:41:35 -0400, David Sankel wrote:
On Thu, Sep 2, 2010 at 3:21 PM, Dave Abrahams <dave@boostpro.com> wrote:
On Thu, Sep 2, 2010 at 3:10 PM, David Sankel <camior@gmail.com> wrote:
On the other hand, arg seems pretty reasonable.
and how is _1 different from arg<0>?
I'm not sure what you mean here. var (or arg if you like) in this library takes two template arguments.
Sorry, typo. I meant arrrrgh. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

On 09/02/10 12:10, Stewart, Robert wrote:
David Sankel wrote:
I've been working on an alternate bind syntax based on De Bruijn indices[1].
I started to look at the Wikipedia page, but realized there was way too much for me to learn to even begin to understand what you mean. Therefore, this reference is of no value to anyone without your background.
The syntax is very simple, yet the terms are very powerful.
I must take your word for it.
Here is an example of a function const that takes in an argument c and returns another function that, for all input, returns c:
//λx.λy.x = λλ1 with De Bruijn indices. auto const_ = abs<1>( abs<1>( var<1,0>() ) );
Presumably, "abs" doesn't mean absolute value as it would in pretty much any other programming context. That seems unwise at best.
Hmm... That puzzles me too, but maybe it's an alias for lambda. I think its meant to suggest ABSstraction? The abs<1> term maybe corresponds to the \1.body (Where \ means the lambda greek letter) and (\1.body)(x) => body with x substituted for the 1 "variable" because, as explained on the wiki page, variables are represented by positive natural numbers. This is sorta like with the current: mpl::apply(mpl::arg<I>,arg1,arg2,...,argn)::type => argI I think this use of DeBruijn notation would avoid some of the problems I had using mpl when the expressions got a little too complicated and I had to resort to using lambda and I think something else to avoid, what I guess was, variable capture causing problems. Good luck David! -regards, Larry

On 09/02/10 10:57, David Sankel wrote:
Hello all,
I've been working on an alternate bind syntax based on De Bruijn indices[1]. The syntax is very simple, yet the terms are very powerful.
Here is an example of a function const that takes in an argument c and returns another function that, for all input, returns c:
//λx.λy.x = λλ1 with De Bruijn indices. auto const_ = abs<1>( abs<1>( var<1,0>() ) );
More examples, further explanation, and an implementation are available here[2].
I'm thinking that this library could also be useful as a core for more syntax heavy bind variations.
David
[1] http://en.wikipedia.org/wiki/De_Bruijn_index [2] http://bitbucket.org/camior/de-bruijn-bind/
I would be very interested. I had been thinking along those same lines. IIRC, I've also seen some haskell code that does that. Here it is: http://www.comlab.ox.ac.uk/richard.bird/online/BirdPaterson99DeBruijn.pdf Might be interesting to try this using functors and monads and other category theory tools.

On 09/02/10 10:57, David Sankel wrote:
Hello all,
I've been working on an alternate bind syntax based on De Bruijn indices[1]. The syntax is very simple, yet the terms are very powerful.
David, I tried the problem on: http://en.wikipedia.org/wiki/De_Bruijn_index however, I'm getting the compile errors shown in the attachment which also shows the source via a cat command. Any ideas what may be wrong? TIA. -Larry

On Sun, Sep 19, 2010 at 2:51 PM, Larry Evans <cppljevans@suddenlink.net>wrote:
On 09/02/10 10:57, David Sankel wrote:
Hello all,
I've been working on an alternate bind syntax based on De Bruijn indices[1]. The syntax is very simple, yet the terms are very powerful.
David,
I tried the problem on:
http://en.wikipedia.org/wiki/De_Bruijn_index
however, I'm getting the compile errors shown in the attachment which also shows the source via a cat command.
Any ideas what may be wrong?
Larry, Looks like you passed a template function, z, to another function. This doesn't work work in C++. template functions are second class citizens. There is a workaround by making a struct with a templated operator(): struct Z { typedef int result_type; template < typename F > int operator() ( int x , F f ) const { return f(x); }; } const z = Z(); Looks like there are more issues too. I'll continue digging. -David -- David Sankel Sankel Software www.sankelsoftware.com 585 617 4748 (Office)

On Mon, Sep 20, 2010 at 10:57 AM, David Sankel <camior@gmail.com> wrote:
On Sun, Sep 19, 2010 at 2:51 PM, Larry Evans <cppljevans@suddenlink.net>wrote:
On 09/02/10 10:57, David Sankel wrote:
Hello all,
I've been working on an alternate bind syntax based on De Bruijn indices[1]. The syntax is very simple, yet the terms are very powerful.
David,
I tried the problem on:
http://en.wikipedia.org/wiki/De_Bruijn_index
however, I'm getting the compile errors shown in the attachment which also shows the source via a cat command.
Any ideas what may be wrong?
Larry,
Looks like you passed a template function, z, to another function. <snip> Looks like there are more issues too. I'll continue digging.
The example, (λ λ z 2 (λ 1 3)) (λ w 1) is a tough one to comprehend, but certainly implementable: auto t = lam<1>( lam<1>( app( z2 , _2_1 , lam<1>( app( _1_1 , _3_1 ) ) ) ) ) ( lam<1>( app( w, _1_1 ) ) ); int result = t("ignored"); If I implemented the "To make our eager evaluator consistent" TODO, I could use the app(a,b) syntax instead of the a(b) syntax for the final application. This gives the expression more consistency: auto t = app( lam<1>( lam<1>( app( z2 , _2_1 , lam<1>( app( _1_1 , _3_1 ) ) ) ) ) , lam<1>( app( w, _1_1 ) ) ); The z function in the example is a weird beast. I gave it a trivial implementation: struct Z2 { typedef int result_type; template < typename F, typename G > int operator()( F f , G g ) const { return 3; }; } const z2 = Z2(); To make a non-trivial implementation of z, you could use the fact that its first argument has kind * → *. That is, its first argument must be a function from something to something. Its second argument has kind ((* → *) → *) → *). That is, its second argument must be a function that takes in a function as its first parameter that takes in a function as its first parameter. A fun mind bender :). David -- David Sankel Sankel Software www.sankelsoftware.com 585 617 4748 (Office)

On 09/20/10 10:43, David Sankel wrote: [snip]
The z function in the example is a weird beast. I gave it a trivial implementation:
struct Z2 { typedef int result_type; template < typename F, typename G > int operator()( F f , G g ) const { return 3; };
} const z2 = Z2();
To make a non-trivial implementation of z, you could use the fact that its first argument has kind * → *. That is, its first argument must be a function from something to something. Its second argument has kind ((* → *) → *) → *). That is, its second argument must be a function that takes in a function as its first parameter that takes in a function as its first parameter. A fun mind bender :).
David
Thanks David. On reason I got interested in wiki is because I was having problems doing it for types instead of values. This type de_bruijn code is here: http://svn.boost.org/svn/boost/sandbox/variadic_templates/libs /mpl/sandbox/de_bruijn/eval.cpp As indicated by the Status: comment, I'm having some problems. I just thought I'd upload it in case anyone else has some idea where I'm going wrong. I'll look at your code, with the modifications you suggest above, to see if that'll give me some ideas. BTW, the context in your value de_bruijn looks like the args in my type de_bruijn. IOW, it keeps track of the current args and the depth in the expression tree. I have yet figured out what any further comparision between the value and type de_bruijn are, but I'd be curious. -regards, Larry
participants (11)
-
Dave Abrahams
-
David Abrahams
-
David Sankel
-
Joel de Guzman
-
Larry Evans
-
Manjunath Kudlur
-
OvermindDL1
-
Scott McMurray
-
Smith, Jacob N
-
Stefan Strasser
-
Stewart, Robert