www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Curious thoughts, regarding functional programming

reply Gor Gyolchanyan <gor.f.gyolchanyan gmail.com> writes:
I find myself constantly dreaming about a syntax sugar, which would
make me (and probably, many others) very happy.
An important and popular aspect of functional programming is the
ability to pass delegate literals to functions.
The downside to this is the ugliness of the resulting code, which
discourages doing so:
std.concurrency.spawn({ foreach(i, 0.100) {  } });
This piece of code has way too much punctuation. Worst of all, the
entire delegate literal is crammed inside the function call
parentheses.
It looks especially ugly when the delegate literal is not a one-liner.
The way I saw it in my dreams is:

spawn()
{
    foreach(i; 0..100) { }
};

What happened here is, that the delegate moved outside the parentheses.
This looks awfully like a function definition, right?
Wrong!
This does not have a return type, the parameters are not definitions,
they are values and there is a semicolon at the end (which quickly
gives a visual clue of what this thing actually is).
Hey, wait a minute! What about parameters to delegates and what about
other delegate parameters?
Well, this will work with only one delegate (or function) parameter
and the parameters could be specified in a second set of parentheses
(again this would look like a template, but it actually won't):

spawn(5, 4, 65)(int x, int y, int z)
{
   /*...*/
};

IMO, this looks WAY more beautiful, then what we have now.
I don't see any reason why this would break existing code.
I know, I know this is hard to implement and we got better things to do.
I'm just saying. This would look good.
Oct 12 2011
next sibling parent reply "Nick Sabalausky" <a a.a> writes:
"Gor Gyolchanyan" <gor.f.gyolchanyan gmail.com> wrote in message 
news:mailman.62.1318407737.24802.digitalmars-d puremagic.com...
 spawn(5, 4, 65)(int x, int y, int z)
 {
   /*...*/
 };

 IMO, this looks WAY more beautiful, then what we have now.
 I don't see any reason why this would break existing code.
 I know, I know this is hard to implement and we got better things to do.
 I'm just saying. This would look good.
That's actually a pretty common feature request for D.
Oct 12 2011
parent simendsjo <simendsjo gmail.com> writes:
On 12.10.2011 11:57, Nick Sabalausky wrote:
 "Gor Gyolchanyan"<gor.f.gyolchanyan gmail.com>  wrote in message
 news:mailman.62.1318407737.24802.digitalmars-d puremagic.com...
 spawn(5, 4, 65)(int x, int y, int z)
 {
    /*...*/
 };

 IMO, this looks WAY more beautiful, then what we have now.
 I don't see any reason why this would break existing code.
 I know, I know this is hard to implement and we got better things to do.
 I'm just saying. This would look good.
That's actually a pretty common feature request for D.
I don't find this very ugly: spawn(5,4,65)( (int x, int y, int z) { /*...*/ }); I would much rather push uniformed function call syntax (correct definition?) forward.
Oct 12 2011
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2011-10-12 10:21, Gor Gyolchanyan wrote:
 I find myself constantly dreaming about a syntax sugar, which would
 make me (and probably, many others) very happy.
 An important and popular aspect of functional programming is the
 ability to pass delegate literals to functions.
 The downside to this is the ugliness of the resulting code, which
 discourages doing so:
 std.concurrency.spawn({ foreach(i, 0.100) {  } });
 This piece of code has way too much punctuation. Worst of all, the
 entire delegate literal is crammed inside the function call
 parentheses.
 It looks especially ugly when the delegate literal is not a one-liner.
 The way I saw it in my dreams is:

 spawn()
 {
      foreach(i; 0..100) { }
 };

 What happened here is, that the delegate moved outside the parentheses.
 This looks awfully like a function definition, right?
 Wrong!
 This does not have a return type, the parameters are not definitions,
 they are values and there is a semicolon at the end (which quickly
 gives a visual clue of what this thing actually is).
 Hey, wait a minute! What about parameters to delegates and what about
 other delegate parameters?
 Well, this will work with only one delegate (or function) parameter
 and the parameters could be specified in a second set of parentheses
 (again this would look like a template, but it actually won't):

 spawn(5, 4, 65)(int x, int y, int z)
 {
     /*...*/
 };

 IMO, this looks WAY more beautiful, then what we have now.
 I don't see any reason why this would break existing code.
 I know, I know this is hard to implement and we got better things to do.
 I'm just saying. This would look good.
This has been proposed several times before. Something like, if a function takes a delegate as its last parameter then this syntax would work: void unless (bool condition, void delegate () dg) { if (condition) dg(); } unless(a == b) { // delegate } And passing arguments to the delegate: foo(1 ; 2){} Any arguments to the left of the semicolon would be passed to the delegate. When this feature is talked about you usually want more things, like, what I would like to call, "soft" and "hard" returns. void iterate (int start, int end, void delegate (int a) dg) { foreach (a ; start .. end) dg(); } When this delegate is called you want to both be able to just return from the delegate but also return from "foo". iterate(1, 10 ; int a) { if (a == 2) yield; // soft return, just returns from the delegate else if (a == 4) return; // hard return, return from both the delegate and the function that called the delegate } Currently we only have "soft" returns from delegates. -- /Jacob Carlborg
Oct 12 2011
next sibling parent reply Gor Gyolchanyan <gor.f.gyolchanyan gmail.com> writes:
The semicolon technique has occurred to me, but seemed a bit too
radical (I loved it nonetheless).
The "hard" return should not be available IMO. The delegate should
never have control over who calls it.
It's totally wrong... IMO.

On Wed, Oct 12, 2011 at 4:13 PM, Jacob Carlborg <doob me.com> wrote:
 On 2011-10-12 10:21, Gor Gyolchanyan wrote:
 I find myself constantly dreaming about a syntax sugar, which would
 make me (and probably, many others) very happy.
 An important and popular aspect of functional programming is the
 ability to pass delegate literals to functions.
 The downside to this is the ugliness of the resulting code, which
 discourages doing so:
 std.concurrency.spawn({ foreach(i, 0.100) { =A0} });
 This piece of code has way too much punctuation. Worst of all, the
 entire delegate literal is crammed inside the function call
 parentheses.
 It looks especially ugly when the delegate literal is not a one-liner.
 The way I saw it in my dreams is:

 spawn()
 {
 =A0 =A0 foreach(i; 0..100) { }
 };

 What happened here is, that the delegate moved outside the parentheses.
 This looks awfully like a function definition, right?
 Wrong!
 This does not have a return type, the parameters are not definitions,
 they are values and there is a semicolon at the end (which quickly
 gives a visual clue of what this thing actually is).
 Hey, wait a minute! What about parameters to delegates and what about
 other delegate parameters?
 Well, this will work with only one delegate (or function) parameter
 and the parameters could be specified in a second set of parentheses
 (again this would look like a template, but it actually won't):

 spawn(5, 4, 65)(int x, int y, int z)
 {
 =A0 =A0/*...*/
 };

 IMO, this looks WAY more beautiful, then what we have now.
 I don't see any reason why this would break existing code.
 I know, I know this is hard to implement and we got better things to do.
 I'm just saying. This would look good.
This has been proposed several times before. Something like, if a functio=
n
 takes a delegate as its last parameter then this syntax would work:

 void unless (bool condition, void delegate () dg)
 {
 =A0 =A0if (condition)
 =A0 =A0 =A0 =A0dg();
 }

 unless(a =3D=3D b)
 {
 =A0 =A0// delegate
 }

 And passing arguments to the delegate:

 foo(1 ; 2){}

 Any arguments to the left of the semicolon would be passed to the delegat=
e.
 When this feature is talked about you usually want more things, like, wha=
t I
 would like to call, "soft" and "hard" returns.

 void iterate (int start, int end, void delegate (int a) dg)
 {
 =A0 =A0foreach (a ; start .. end)
 =A0 =A0 =A0 =A0dg();
 }

 When this delegate is called you want to both be able to just return from
 the delegate but also return from "foo".

 iterate(1, 10 ; int a)
 {
 =A0 =A0if (a =3D=3D 2)
 =A0 =A0 =A0 =A0yield; // soft return, just returns from the delegate

 =A0 =A0else if (a =3D=3D 4)
 =A0 =A0 =A0 =A0return; // hard return, return from both the delegate and =
the
 function that called the delegate
 }

 Currently we only have "soft" returns from delegates.

 --
 /Jacob Carlborg
Oct 12 2011
parent reply Jacob Carlborg <doob me.com> writes:
On 2011-10-12 14:35, Gor Gyolchanyan wrote:
 The semicolon technique has occurred to me, but seemed a bit too
 radical (I loved it nonetheless).
 The "hard" return should not be available IMO. The delegate should
 never have control over who calls it.
 It's totally wrong... IMO.
It has it uses and it's possible to do so in Ruby.
 On Wed, Oct 12, 2011 at 4:13 PM, Jacob Carlborg<doob me.com>  wrote:
 On 2011-10-12 10:21, Gor Gyolchanyan wrote:
 I find myself constantly dreaming about a syntax sugar, which would
 make me (and probably, many others) very happy.
 An important and popular aspect of functional programming is the
 ability to pass delegate literals to functions.
 The downside to this is the ugliness of the resulting code, which
 discourages doing so:
 std.concurrency.spawn({ foreach(i, 0.100) {  } });
 This piece of code has way too much punctuation. Worst of all, the
 entire delegate literal is crammed inside the function call
 parentheses.
 It looks especially ugly when the delegate literal is not a one-liner.
 The way I saw it in my dreams is:

 spawn()
 {
      foreach(i; 0..100) { }
 };

 What happened here is, that the delegate moved outside the parentheses.
 This looks awfully like a function definition, right?
 Wrong!
 This does not have a return type, the parameters are not definitions,
 they are values and there is a semicolon at the end (which quickly
 gives a visual clue of what this thing actually is).
 Hey, wait a minute! What about parameters to delegates and what about
 other delegate parameters?
 Well, this will work with only one delegate (or function) parameter
 and the parameters could be specified in a second set of parentheses
 (again this would look like a template, but it actually won't):

 spawn(5, 4, 65)(int x, int y, int z)
 {
     /*...*/
 };

 IMO, this looks WAY more beautiful, then what we have now.
 I don't see any reason why this would break existing code.
 I know, I know this is hard to implement and we got better things to do.
 I'm just saying. This would look good.
This has been proposed several times before. Something like, if a function takes a delegate as its last parameter then this syntax would work: void unless (bool condition, void delegate () dg) { if (condition) dg(); } unless(a == b) { // delegate } And passing arguments to the delegate: foo(1 ; 2){} Any arguments to the left of the semicolon would be passed to the delegate. When this feature is talked about you usually want more things, like, what I would like to call, "soft" and "hard" returns. void iterate (int start, int end, void delegate (int a) dg) { foreach (a ; start .. end) dg(); } When this delegate is called you want to both be able to just return from the delegate but also return from "foo". iterate(1, 10 ; int a) { if (a == 2) yield; // soft return, just returns from the delegate else if (a == 4) return; // hard return, return from both the delegate and the function that called the delegate } Currently we only have "soft" returns from delegates. -- /Jacob Carlborg
-- /Jacob Carlborg
Oct 12 2011
parent reply Gor Gyolchanyan <gor.f.gyolchanyan gmail.com> writes:
You never know when exactly and in which conditions will that delegate
get called. You can't decide to quit something you don't know anything
about.

On Wed, Oct 12, 2011 at 8:08 PM, Jacob Carlborg <doob me.com> wrote:
 On 2011-10-12 14:35, Gor Gyolchanyan wrote:
 The semicolon technique has occurred to me, but seemed a bit too
 radical (I loved it nonetheless).
 The "hard" return should not be available IMO. The delegate should
 never have control over who calls it.
 It's totally wrong... IMO.
It has it uses and it's possible to do so in Ruby.
 On Wed, Oct 12, 2011 at 4:13 PM, Jacob Carlborg<doob me.com> =A0wrote:
 On 2011-10-12 10:21, Gor Gyolchanyan wrote:
 I find myself constantly dreaming about a syntax sugar, which would
 make me (and probably, many others) very happy.
 An important and popular aspect of functional programming is the
 ability to pass delegate literals to functions.
 The downside to this is the ugliness of the resulting code, which
 discourages doing so:
 std.concurrency.spawn({ foreach(i, 0.100) { =A0} });
 This piece of code has way too much punctuation. Worst of all, the
 entire delegate literal is crammed inside the function call
 parentheses.
 It looks especially ugly when the delegate literal is not a one-liner.
 The way I saw it in my dreams is:

 spawn()
 {
 =A0 =A0 foreach(i; 0..100) { }
 };

 What happened here is, that the delegate moved outside the parentheses=
.
 This looks awfully like a function definition, right?
 Wrong!
 This does not have a return type, the parameters are not definitions,
 they are values and there is a semicolon at the end (which quickly
 gives a visual clue of what this thing actually is).
 Hey, wait a minute! What about parameters to delegates and what about
 other delegate parameters?
 Well, this will work with only one delegate (or function) parameter
 and the parameters could be specified in a second set of parentheses
 (again this would look like a template, but it actually won't):

 spawn(5, 4, 65)(int x, int y, int z)
 {
 =A0 =A0/*...*/
 };

 IMO, this looks WAY more beautiful, then what we have now.
 I don't see any reason why this would break existing code.
 I know, I know this is hard to implement and we got better things to d=
o.
 I'm just saying. This would look good.
This has been proposed several times before. Something like, if a function takes a delegate as its last parameter then this syntax would work: void unless (bool condition, void delegate () dg) { =A0 =A0if (condition) =A0 =A0 =A0 =A0dg(); } unless(a =3D=3D b) { =A0 =A0// delegate } And passing arguments to the delegate: foo(1 ; 2){} Any arguments to the left of the semicolon would be passed to the delegate. When this feature is talked about you usually want more things, like, what I would like to call, "soft" and "hard" returns. void iterate (int start, int end, void delegate (int a) dg) { =A0 =A0foreach (a ; start .. end) =A0 =A0 =A0 =A0dg(); } When this delegate is called you want to both be able to just return fr=
om
 the delegate but also return from "foo".

 iterate(1, 10 ; int a)
 {
 =A0 =A0if (a =3D=3D 2)
 =A0 =A0 =A0 =A0yield; // soft return, just returns from the delegate

 =A0 =A0else if (a =3D=3D 4)
 =A0 =A0 =A0 =A0return; // hard return, return from both the delegate an=
d the
 function that called the delegate
 }

 Currently we only have "soft" returns from delegates.

 --
 /Jacob Carlborg
-- /Jacob Carlborg
Oct 12 2011
parent reply Jacob Carlborg <doob me.com> writes:
On 2011-10-12 18:18, Gor Gyolchanyan wrote:
 You never know when exactly and in which conditions will that delegate
 get called. You can't decide to quit something you don't know anything
 about.
If a function implements something similar to a loop I would like to be able to abort it with a return just as you can with a loop built into the language. void loop (void delegate () dg) { while (true) dg(); } loop { if (someCondition) return; // stops the loop } -- /Jacob Carlborg
Oct 12 2011
parent reply "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
On Wed, 12 Oct 2011 19:00:56 +0200, Jacob Carlborg <doob me.com> wrote:

 On 2011-10-12 18:18, Gor Gyolchanyan wrote:
 You never know when exactly and in which conditions will that delegate
 get called. You can't decide to quit something you don't know anything
 about.
If a function implements something similar to a loop I would like to be able to abort it with a return just as you can with a loop built into the language. void loop (void delegate () dg) { while (true) dg(); } loop { if (someCondition) return; // stops the loop }
However, a lot of code using this syntax would be more complex than what you indicate. Should it work simply as a break in the loop? What if it is a recursive function? There is a reason why opApply is designed the way it is. -- Simen
Oct 12 2011
parent Jacob Carlborg <doob me.com> writes:
On 2011-10-12 19:40, Simen Kjaeraas wrote:
 On Wed, 12 Oct 2011 19:00:56 +0200, Jacob Carlborg <doob me.com> wrote:

 On 2011-10-12 18:18, Gor Gyolchanyan wrote:
 You never know when exactly and in which conditions will that delegate
 get called. You can't decide to quit something you don't know anything
 about.
If a function implements something similar to a loop I would like to be able to abort it with a return just as you can with a loop built into the language. void loop (void delegate () dg) { while (true) dg(); } loop { if (someCondition) return; // stops the loop }
However, a lot of code using this syntax would be more complex than what you indicate. Should it work simply as a break in the loop? What if it is a recursive function? There is a reason why opApply is designed the way it is.
This answer to a stack overflow question explains how it works in Ruby: http://stackoverflow.com/questions/1402757/how-to-break-out-from-a-ruby-block#answer-1402764 -- /Jacob Carlborg
Oct 12 2011
prev sibling parent reply "Nick Sabalausky" <a a.a> writes:
"Jacob Carlborg" <doob me.com> wrote in message 
news:j740a6$2t8m$1 digitalmars.com...
 When this delegate is called you want to both be able to just return from 
 the delegate but also return from "foo".

 iterate(1, 10 ; int a)
 {
     if (a == 2)
 yield; // soft return, just returns from the delegate

     else if (a == 4)
         return; // hard return, return from both the delegate and the 
 function that called the delegate
 }

 Currently we only have "soft" returns from delegates.
Better (IMHO): void foo() { iterate(int a; 1, 10) { if (a == 2) continue; // return from just the delegate else if (a == 4) break; // return from both delegate and iterate else if (a == 6) return; // return from the delegate, iterate, and foo } } Ie, same syntax and semantics as foreach. Also, a couple new things that foreach doesn't have to deal with: auto x = map(i; 1, 10) { //continue; // Error: map's dg can't return void continue i*2; // OK } assert(x == [2, 4, 6, etc...]); // Conventiently ignoring ranges just for the sake of illustration Of course, maybe it would be better to require "yield" in such a case (and maybe make "yield" synonymous with "continue" for void delegates?), but there's a lot of resistance against new keywords. And, one last thing to take care of: auto x = iterate(i; 1, 10) { if(i == 4) { //break; // Error: need a return value break i*2; // OK } } assert(x == 8);
Oct 12 2011
next sibling parent kennytm <kennytm gmail.com> writes:
"Nick Sabalausky" <a a.a> wrote:
 "Jacob Carlborg" <doob me.com> wrote in message 
 news:j740a6$2t8m$1 digitalmars.com...
 
 When this delegate is called you want to both be able to just return from 
 the delegate but also return from "foo".
 
 iterate(1, 10 ; int a)
 {
     if (a == 2)
 yield; // soft return, just returns from the delegate
 
     else if (a == 4)
         return; // hard return, return from both the delegate and the 
 function that called the delegate
 }
 
 Currently we only have "soft" returns from delegates.
 
Better (IMHO): void foo() { iterate(int a; 1, 10) { if (a == 2) continue; // return from just the delegate else if (a == 4) break; // return from both delegate and iterate else if (a == 6) return; // return from the delegate, iterate, and foo } } Ie, same syntax and semantics as foreach. Also, a couple new things that foreach doesn't have to deal with: auto x = map(i; 1, 10) { //continue; // Error: map's dg can't return void continue i*2; // OK } assert(x == [2, 4, 6, etc...]); // Conventiently ignoring ranges just for the sake of illustration Of course, maybe it would be better to require "yield" in such a case (and maybe make "yield" synonymous with "continue" for void delegates?), but there's a lot of resistance against new keywords. And, one last thing to take care of: auto x = iterate(i; 1, 10) { if(i == 4) { //break; // Error: need a return value break i*2; // OK } } assert(x == 8);
The 'break <expr>' syntax conflicts with the 'break <label>' syntax, so -1 to this. i: auto x = iterate(i; 1, 10) { foreach (j; 0 .. i) break i; // what should it do? break i; } Perhaps you need to require parenthesis, like 'break (i);' and 'break (i*2);'.
Oct 12 2011
prev sibling parent Jacob Carlborg <doob me.com> writes:
On 2011-10-12 22:56, Nick Sabalausky wrote:
 "Jacob Carlborg"<doob me.com>  wrote in message
 news:j740a6$2t8m$1 digitalmars.com...
 When this delegate is called you want to both be able to just return from
 the delegate but also return from "foo".

 iterate(1, 10 ; int a)
 {
      if (a == 2)
 yield; // soft return, just returns from the delegate

      else if (a == 4)
          return; // hard return, return from both the delegate and the
 function that called the delegate
 }

 Currently we only have "soft" returns from delegates.
Better (IMHO): void foo() { iterate(int a; 1, 10) { if (a == 2) continue; // return from just the delegate else if (a == 4) break; // return from both delegate and iterate else if (a == 6) return; // return from the delegate, iterate, and foo } }
That's actually how it works in Ruby as well. Ruby also has both lambdas and blocks, the only difference between them is that you can't return/break from a lambda but you can from a block (I hope I got this right). This answer to a stack overflow question explains how it works in Ruby: http://stackoverflow.com/questions/1402757/how-to-break-out-from-a-ruby- lock#answer-1402764
 Ie, same syntax and semantics as foreach. Also, a couple new things that
 foreach doesn't have to deal with:

 auto x = map(i; 1, 10)
 {
      //continue; // Error: map's dg can't return void
      continue i*2; // OK
 }
 assert(x == [2, 4, 6, etc...]); // Conventiently ignoring ranges just for
 the sake of illustration

 Of course, maybe it would be better to require "yield" in such a case (and
 maybe make "yield" synonymous with "continue" for void delegates?), but
 there's a lot of resistance against new keywords.
Yeah, I know that.
 And, one last thing to take care of:

 auto x = iterate(i; 1, 10)
 {
      if(i == 4)
      {
          //break; // Error: need a return value
          break i*2; // OK
      }
 }
 assert(x == 8);
Yeah, assuming "iterate" takes a delegate that returns something. But that would be nice, to create, what look like, statements that return a values. -- /Jacob Carlborg
Oct 12 2011