| Thread overview | ||||||||
|---|---|---|---|---|---|---|---|---|
|
June 17, 2010 [phobos] D std.bind | ||||
|---|---|---|---|---|
| ||||
Andrei Alexandrescu <andrei at ...> writes:
>
> I wrote a curry in std.functional which I think could be a good starting point.
I'm new to D syntax, but if I read that right, std.curry just gives you an alias to a function call of two variables that fills in one for you. Deprecating bind would still leave holes for:
- Currying an arbitrary length parameter list
- storing a delegate to the curried function
- composing curries (could be replaced by previous 2 if done in a certain way)
- binding at arbitrary parameter indexes
and maybe a couple of other things. In short, bind gives support for all kinds of partial applications, where as std.curry only allows picking off one arg in a two-arg function. If bind were deprecated, is there a plan to replace the other functionality?
Seems like with the new functional capabilities of D 2, bind is MORE necessary, since I would imagine more higher order functions being written and extended. If that's true, then there's gonna be an awful lot of boiler plate code like:
auto p_2 = function ReturnType!plus (ParameterTypeTuple!plus[1] arg2,
ParameterTypeTuple!plus[2] arg3) { return plus(2, arg2, arg3); } ;
rather than
auto p_2 = bind(plus, 2, _0, _1);
I'd love to see partial applications supported in the language, but short of
that, I'd say there needs to be some easier way to curry in, say 5 argument
calls, or else the functional features of D really loose
power. Bind already provides that. Maybe it just needs a make-over?
Jason
| ||||
June 17, 2010 [phobos] D std.bind | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Jason Spencer | Jason Spencer wrote:
> Andrei Alexandrescu <andrei at ...> writes:
>> I wrote a curry in std.functional which I think could be a good starting point.
>
> I'm new to D syntax, but if I read that right, std.curry just gives you an alias to a function call of two variables that fills in one for you. Deprecating bind would still leave holes for:
>
> - Currying an arbitrary length parameter list
> - storing a delegate to the curried function
> - composing curries (could be replaced by previous 2 if done in a certain way)
> - binding at arbitrary parameter indexes
All of these are much easier done with delegate literals.
Andrei
| |||
June 17, 2010 [phobos] D std.bind | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | Andrei Alexandrescu <andrei at ...> writes: > > Jason Spencer wrote: > > - Currying an arbitrary length parameter list > > - storing a delegate to the curried function > > - composing curries (could be replaced by previous 2 if done in a certain way) > > - binding at arbitrary parameter indexes > > All of these are much easier done with delegate literals. As I said, I'm new, so hopefully you'll tell me if I'm missing something obvious. But it sounds like you're suggesting that the code below represents an easier way to these things than some templated bind or related idea. I can't say that feels easier. I also wonder what happens when I change the return type of g to short--can I make that change correctly in one pass (i.e. isn't manual maintenance of this sort of construct very error-prone)? I could well accept that there might be an easier template formulation for these constructs that can use the full power of delegate literals and closures built in now. Maybe bind goes away and std.functional picks up some of this capability with a fuller curry (like the sample Curry on the template page, even.) But I think to not provide SOME facility would add a lot of boiler plate code, be maintenance error-prone, and hinder the use of a fairly powerful feature. Again, I'm quite possibly missing the "easy" way. Jason Sample code: ----- import std.stdio; int g (int a, int b, int c, int d) { return a + b + c + d; } void main() { // bind a single argument auto g1 = function int(int b, int c, int d) { return g(2,b,c,d); }; // compose bindings of one argument auto g2 = delegate int(int c, int d) { return g1(3, c, d); }; // delegate that takes two arguments and returns a delegate of one argument auto curryG2x = delegate int delegate (int) (int b, int c) { return delegate int (int d) { return g(2, b, c, d); }; }; // full currying auto fullCurryG = delegate int delegate (int) delegate (int) delegate (int) (int a) { return delegate int delegate (int) delegate (int) (int b) { return delegate int delegate (int) (int c) { return delegate int (int d) { return g(a, b, c, d); }; }; }; }; writefln("g2(4,5) = %d, type = %s", g2(4,5), typeid(g2)); writefln("curryG2x(3)(4,5) = %d, type = %s", curryG2x(3,4)(5), typeid(curryG2x)); writefln("fullCurryG(2)(3)(4)(5) = %d, type = %s", fullCurryG(2)(3)(4)(5), typeid(fullCurryG)); writefln("fullCurryG(2)(3) = %s, type = %s", fullCurryG(2)(3), typeid(fullCurryG)); } | |||
June 17, 2010 [phobos] D std.bind | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Jason Spencer | I think you're close to the optimal. I'd probably drop function and write:
auto g1 = int(int b, int c, int d) { return g(2,b,c,d); };
All things considered, I'm not sure that that's worse than the arcana:
auto g1 = bind(&g, _1);
which only goes downhill when:
- g is overloaded
- the usage pattern is more complicated (e.g. you want g(2, -b, c, d)
- there are more than 10 arguments
- and by the way there's a bug above because I used _1 not _0 :o)
All in all, std.bind looks like a direct derivative of Boost's design, which is built to deal with C++'s issues, and does not avail itself of D's advantages.
Andrei
Jason Spencer wrote:
> Andrei Alexandrescu <andrei at ...> writes:
>
>> Jason Spencer wrote:
>>> - Currying an arbitrary length parameter list
>>> - storing a delegate to the curried function
>>> - composing curries (could be replaced by previous 2 if done in a certain
> way)
>>> - binding at arbitrary parameter indexes
>> All of these are much easier done with delegate literals.
>
> As I said, I'm new, so hopefully you'll tell me if I'm missing something obvious. But it sounds like you're suggesting that the code below represents an easier way to these things than some templated bind or related idea. I can't say that feels easier. I also wonder what happens when I change the return type of g to short--can I make that change correctly in one pass (i.e. isn't manual maintenance of this sort of construct very error-prone)?
>
> I could well accept that there might be an easier template formulation for these constructs that can use the full power of delegate literals and closures built in now. Maybe bind goes away and std.functional picks up some of this capability with a fuller curry (like the sample Curry on the template page, even.) But I think to not provide SOME facility would add a lot of boiler plate code, be maintenance error-prone, and hinder the use of a fairly powerful feature.
>
> Again, I'm quite possibly missing the "easy" way.
>
> Jason
>
> Sample code:
> -----
>
>
> import std.stdio;
>
> int g (int a, int b, int c, int d)
> {
> return a + b + c + d;
> }
>
> void main()
> {
> // bind a single argument
> auto g1 = function int(int b, int c, int d) { return g(2,b,c,d); };
>
> // compose bindings of one argument
> auto g2 = delegate int(int c, int d) { return g1(3, c, d); };
>
> // delegate that takes two arguments and returns a delegate of one argument
> auto curryG2x = delegate int delegate (int) (int b, int c)
> {
> return delegate int (int d)
> {
> return g(2, b, c, d);
> };
> };
> // full currying
> auto fullCurryG =
> delegate int delegate (int) delegate (int) delegate (int) (int a)
> {
> return delegate int delegate (int) delegate (int) (int b)
> {
> return delegate int delegate (int) (int c)
> {
> return delegate int (int d)
> {
> return g(a, b, c, d);
> };
> };
> };
> };
> writefln("g2(4,5) = %d, type = %s", g2(4,5), typeid(g2));
> writefln("curryG2x(3)(4,5) = %d, type = %s", curryG2x(3,4)(5),
> typeid(curryG2x));
> writefln("fullCurryG(2)(3)(4)(5) = %d, type = %s", fullCurryG(2)(3)(4)(5),
> typeid(fullCurryG));
> writefln("fullCurryG(2)(3) = %s, type = %s", fullCurryG(2)(3),
> typeid(fullCurryG));
> }
>
>
> _______________________________________________
> phobos mailing list
> phobos at puremagic.com
> http://lists.puremagic.com/mailman/listinfo/phobos
| |||
June 17, 2010 [phobos] D std.bind | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | ----- Original Message ---- > From: Andrei Alexandrescu <andrei at erdani.com> > I think you're close to the optimal. I'd probably drop function and write: > > auto g1 = int(int b, int c, int d) { return g(2,b,c,d); }; That actually fails to compile in 2.047: curry.d(13): found '(' when expecting '.' following int > All things considered, I'm not sure that that's worse than the arcana: > > auto g1 = bind(&g, _1); > > ... > > All in all, std.bind looks like a direct derivative of Boost's design, which is built to deal with C++'s issues, and does not avail itself of D's advantages. It is at least shorter and comprehensible when glancing at it, though. I'm not a great fan of the boost syntax or implementation ported to D either, but I also recognize the need for some easy way to automate that much boiler plate code. Maybe I'll futz around and see if I can come up with a nice way of doing partial application. If I can, could that be considered for inclusion in functional or something? Either way, I'd politely suggest that bind should either be fixed or removed. I assume it's been broken for 6 months now, and maybe 3 years, in D 2.0. Seems a shame to throw away something that at least fills a hole for the time being until something better comes along, but it may be better to keep confidence in stability up. Jason | |||
June 17, 2010 [phobos] D std.bind | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Jason Spencer | Jason Spencer wrote: > ----- Original Message ---- From: Andrei Alexandrescu > <andrei at erdani.com> > > >> I think you're close to the optimal. I'd probably drop function and >> write: >> >> auto g1 = int(int b, int c, int d) { return g(2,b,c,d); }; > > > That actually fails to compile in 2.047: curry.d(13): found '(' when > expecting '.' following int > > >> All things considered, I'm not sure that that's worse than the arcana: >> >> auto g1 = bind(&g, _1); >> >> ... >> >> All in all, std.bind looks like a direct derivative of Boost's design, which is built to deal with C++'s issues, and does not avail itself of D's advantages. > > It is at least shorter and comprehensible when glancing at it, though. I'm not a great fan of the boost syntax or implementation ported to D either, but I also recognize the need for some easy way to automate that much boiler plate code. Maybe I'll futz around and see if I can come up with a nice way of doing partial application. If I can, could that be considered for inclusion in functional or something? Sure. I suggest you follow the pattern in std.functional of passing aliases around instead of the std.bind style. The latter is less efficient. Also I think a non-variadic solution (_0 to _9 are abhorrent) wouldn't fare too well. > Either way, I'd politely suggest that bind should either be fixed or removed. I assume it's been broken for 6 months now, and maybe 3 years, in D 2.0. Seems a shame to throw away something that at least fills a hole for the time being until something better comes along, but it may be better to keep confidence in stability up. Walter wanted to yank it for a long time. I agreed with some reluctance (I agree with the pros you mention) but I think it's best to deprecate the thing. Andrei | |||
Copyright © 1999-2021 by the D Language Foundation
Permalink
Reply