www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Re: Lambda syntax, etc

reply Kagamin <spam here.lot> writes:
bearophile Wrote:

 C#2 has lambdas, and C#3 adds closures and more type inferencing, so C#3+
supports the following syntaxes:
 (int i) => { return i % 3 == 1; } // C#2
 i => i % 3 == 1 // C#3
 i => { return i % 3 == 1; } // C#3, with statements too
 To define a delegate o delegate closure:
 Func<int> foo = i => { return i % 3 == 1; };
 Func<int> foo = i => i % 3 == 1;
 Func<int> bar = () => 2;
 But this isn't allowed:
 Func<void> bar = () => 2;

Yeah, C# lambdas are the killer feature. Slick, readable, C-compatible. Anders knows his job. Let's face it: delegate literals suck a little, mixins as delegates suck a lot, the former is too verbose, the latter just sucks.
Feb 05 2009
next sibling parent reply "Denis Koroskin" <2korden gmail.com> writes:
On Thu, 05 Feb 2009 12:32:15 +0300, Kagamin <spam here.lot> wrote:

 bearophile Wrote:

 C#2 has lambdas, and C#3 adds closures and more type inferencing, so  
 C#3+ supports the following syntaxes:
 (int i) => { return i % 3 == 1; } // C#2
 i => i % 3 == 1 // C#3
 i => { return i % 3 == 1; } // C#3, with statements too
 To define a delegate o delegate closure:
 Func<int> foo = i => { return i % 3 == 1; };
 Func<int> foo = i => i % 3 == 1;
 Func<int> bar = () => 2;
 But this isn't allowed:
 Func<void> bar = () => 2;

Yeah, C# lambdas are the killer feature. Slick, readable, C-compatible. Anders knows his job. Let's face it: delegate literals suck a little, mixins as delegates suck a lot, the former is too verbose, the latter just sucks.

I don't like C# lambda syntax (although it is not half as bad as C++ lambda syntax). I believe D delegate syntax is superior due to its natural and unambiguous syntax. But yes, it could be made shorter by improving type deduction: int delegate(int) inc = (i) { i + 1; } Which would be the same as int delegate(int) inc = (int i) { return i + 1; } where i's type is deduced from inc's type and the only expression (i + 1) made a return value: auto x = inc(5); // yields 6 Here is an another example: void foo(void delegate(ref int i) inc); Could be used as follows: foo( (i) { ++i; } ); as opposed to foo( (ref int i) { ++i; } ); I can put this enhancement request into bugzilla if anyone likes it.
Feb 05 2009
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Denis Koroskin wrote:
 On Thu, 05 Feb 2009 12:32:15 +0300, Kagamin <spam here.lot> wrote:
 
 bearophile Wrote:

 C#2 has lambdas, and C#3 adds closures and more type inferencing, so 
 C#3+ supports the following syntaxes:
 (int i) => { return i % 3 == 1; } // C#2
 i => i % 3 == 1 // C#3
 i => { return i % 3 == 1; } // C#3, with statements too
 To define a delegate o delegate closure:
 Func<int> foo = i => { return i % 3 == 1; };
 Func<int> foo = i => i % 3 == 1;
 Func<int> bar = () => 2;
 But this isn't allowed:
 Func<void> bar = () => 2;

Yeah, C# lambdas are the killer feature. Slick, readable, C-compatible. Anders knows his job. Let's face it: delegate literals suck a little, mixins as delegates suck a lot, the former is too verbose, the latter just sucks.

I don't like C# lambda syntax (although it is not half as bad as C++ lambda syntax). I believe D delegate syntax is superior due to its natural and unambiguous syntax. But yes, it could be made shorter by improving type deduction: int delegate(int) inc = (i) { i + 1; } Which would be the same as int delegate(int) inc = (int i) { return i + 1; }

What if you wanted to just execute one expression and return void? This is relevant when e.g. large objects are involved that shouldn't be copied unwittingly.
 where i's type is deduced from inc's type and the only expression (i + 
 1) made a return value:
 
 auto x = inc(5); // yields 6
 
 Here is an another example:
 
 void foo(void delegate(ref int i) inc);
 
 Could be used as follows:
 
 foo( (i) { ++i; } );
 
 as opposed to
 
 foo( (ref int i) { ++i; } );

Aha! So here you are using a void-returning function. Now what if there was another overload of foo in place: void foo(int delegate(ref int i) inc); Which foo is to be called? The one that infers a return type of int or the one that assumes the code just returns void?
 I can put this enhancement request into bugzilla if anyone likes it.

It would be great to add the parameter type deduction stuff; that is already talked about and doesn't seem to have many issues. It does have one, which I'm sure people here will see rather quickly. Andrei
Feb 05 2009
parent reply Kagamin <spam here.lot> writes:
Denis Koroskin Wrote:

 Could be used as follows:
 
 foo( (i) { ++i; } );

Holy shi- Now feel some real power, Luke. :) foo( i => ++i );
     foo((i){ ++i; }); // error
     foo((i){ ++i; return;}); // unambiguous
     foo((i){ return ++i;}); // unambiguous

Feb 09 2009
next sibling parent Kagamin <spam here.lot> writes:
Denis Koroskin Wrote:

 That was just an example. Those short lambdas are often used as predicates.
Compare:
 
 findAll(array, (i) { i > 3; });
 findAll(array, (int i) { return i > 3; });
 

Feb 09 2009
prev sibling parent Kagamin <spam here.lot> writes:
Denis Koroskin Wrote:

 Could be used as follows:

 foo( (i) { ++i; } );

Holy shi- Now feel some real power, Luke. :) foo( i => ++i );
     foo((i){ ++i; }); // error
     foo((i){ ++i; return;}); // unambiguous
     foo((i){ return ++i;}); // unambiguous


That was just an example. Those short lambdas are often used as predicates. Compare: findAll(array, (i) { i > 3; }); findAll(array, (int i) { return i > 3; });

array.filter((x){x>3}) array.filter(x => x>3) array.map(dep => dep.director) I've run into one article. It points out that C# operator => can be viewed as convertion, so operations working with such convertions look rather natural and expressive.
Feb 19 2009
prev sibling next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Thu, 05 Feb 2009 17:25:38 +0300, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:

 Denis Koroskin wrote:
 On Thu, 05 Feb 2009 12:32:15 +0300, Kagamin <spam here.lot> wrote:

 bearophile Wrote:

 C#2 has lambdas, and C#3 adds closures and more type inferencing, so  
 C#3+ supports the following syntaxes:
 (int i) => { return i % 3 == 1; } // C#2
 i => i % 3 == 1 // C#3
 i => { return i % 3 == 1; } // C#3, with statements too
 To define a delegate o delegate closure:
 Func<int> foo = i => { return i % 3 == 1; };
 Func<int> foo = i => i % 3 == 1;
 Func<int> bar = () => 2;
 But this isn't allowed:
 Func<void> bar = () => 2;

Yeah, C# lambdas are the killer feature. Slick, readable, C-compatible. Anders knows his job. Let's face it: delegate literals suck a little, mixins as delegates suck a lot, the former is too verbose, the latter just sucks.

lambda syntax). I believe D delegate syntax is superior due to its natural and unambiguous syntax. But yes, it could be made shorter by improving type deduction: int delegate(int) inc = (i) { i + 1; } Which would be the same as int delegate(int) inc = (int i) { return i + 1; }

What if you wanted to just execute one expression and return void? This is relevant when e.g. large objects are involved that shouldn't be copied unwittingly.

No problem: void delegate(int) inc = (i) { i + 1; }; which would be transformed into void delegate(int) inc = (int i) { i + 1; }; or void delegate(int) inc = (int i) { return i + 1; }; Both are valid D code according to specs[1]. The second one doesn't compile as of now, but this is a DMD bug, I assume.
 where i's type is deduced from inc's type and the only expression (i +  
 1) made a return value:
  auto x = inc(5); // yields 6
  Here is an another example:
  void foo(void delegate(ref int i) inc);
  Could be used as follows:
  foo( (i) { ++i; } );
  as opposed to
  foo( (ref int i) { ++i; } );

Aha! So here you are using a void-returning function. Now what if there was another overload of foo in place: void foo(int delegate(ref int i) inc); Which foo is to be called? The one that infers a return type of int or the one that assumes the code just returns void?

1) Compiler can flag an ambiguity error at compile time so that user resolve it: foo((i){ ++i; }); // error foo((i){ ++i; return;}); // unambiguous foo((i){ return ++i;}); // unambiguous 2) (The one I prefer) Make (i) { ++i; } return int. The following could be used to force void return type: foo((i){ ++i;; }); // note the double semicolon. It returns void for two reasons: a) second ; evaluates to void b) it is not a single-statement delegate anymore. Implicit return should only be allowed for single statement delegates that are very frequently used (mostly as a predicate) and almost never return void. I doubt it will lead to errors. The very example is rather artificial.
 I can put this enhancement request into bugzilla if anyone likes it.

It would be great to add the parameter type deduction stuff; that is already talked about and doesn't seem to have many issues. It does have one, which I'm sure people here will see rather quickly. Andrei

--- [1]statement.html: ReturnStatement: return; return Expression; Expression is allowed even if the function specifies a void return type. The Expression will be evaluated, but nothing will be returned.
Feb 05 2009
prev sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Mon, 09 Feb 2009 13:24:46 +0300, Kagamin <spam here.lot> wrote:

 Denis Koroskin Wrote:

 Could be used as follows:

 foo( (i) { ++i; } );

Holy shi- Now feel some real power, Luke. :) foo( i => ++i );
     foo((i){ ++i; }); // error
     foo((i){ ++i; return;}); // unambiguous
     foo((i){ return ++i;}); // unambiguous


That was just an example. Those short lambdas are often used as predicates. Compare: findAll(array, (i) { i > 3; }); findAll(array, (int i) { return i > 3; });
Feb 09 2009
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Kagamin wrote:
 bearophile Wrote:
 
 C#2 has lambdas, and C#3 adds closures and more type inferencing, so C#3+
supports the following syntaxes:
 (int i) => { return i % 3 == 1; } // C#2
 i => i % 3 == 1 // C#3
 i => { return i % 3 == 1; } // C#3, with statements too
 To define a delegate o delegate closure:
 Func<int> foo = i => { return i % 3 == 1; };
 Func<int> foo = i => i % 3 == 1;
 Func<int> bar = () => 2;
 But this isn't allowed:
 Func<void> bar = () => 2;

Yeah, C# lambdas are the killer feature. Slick, readable, C-compatible. Anders knows his job.

Without knowing the person, I disagree you could infer that from C#. What I see above is a smörgåsbord of syntaxes that shoot all over the proverbial barn door in the hope that one of them would strike someone's fancy. That strikes me as a rather lousily done job. Also, it is my perception (and not only mine) that C#'s creator completely missed the power of templates and generative programming.
 Let's face it: delegate literals suck a little, mixins as delegates suck a
lot, the former is too verbose, the latter just sucks.

The logic doesn't quite ring, but passons :o). For what it's worth Walter has got positive feedback left and right from his talk discussing the matter. A simplification in defining function literals is being discussed (omitting the type from the parameters) that would simplify template definitions considerably. Beyond that, I don't feel that changes like moving the parameters on one side or the other of "{" would be Earth-shattering. Andrei
Feb 05 2009
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Andrei Alexandrescu>Beyond that, I don't feel that changes like moving the
parameters on one side or the other of "{" would be Earth-shattering.<

As you have seen there are several possible syntaxes for anonymous
functions/delegates/closures. And the current syntax already works, so I think
none of such changes can be Earth-shattering.

On the other hand, extensive usage of lambdas with my dlibs has shown me that
having an uncluttered and un-noisy syntax is quite important if you want to use
such functional style a lot, because otherwise noise builds up quickly and in
production code you are forced to split things in several lines, otherwise no
one can read and debug the code you write.

Regarding such noise I can show you an almost extreme example, this is an easy
programming task:
http://projecteuler.net/index.php?section=problems&id=4
Find the largest palindrome made from the product of two 3-digit numbers<

With Python:
 max(i*j for i in xrange(100,1000) for j in xrange(100,1000) if str(i*j) ==
str(i*j)[::-1])



D1 with my dlibs: import d.all; void main() { putr( max(select(i*j, i, xrange(100,1000), j, xrange(100,1000), str(i*j) == str(i*j).reverse)) ); } Notice in this case Python is clearly better, not just because it's more readable, but also because that select() generates items eagerly, while the Python oneliner contains a lazy generator expression. dlibs contain xmap too, but that code is so much more noisy that I don't want to show it :-) I know this isn't an example of code that's normal in production, but sometimes you need extreme example to show your point :-) Now, back to the topic: putting arguments into a single () or {} instead of a (){ return ;} (or with the => of C#) helps the eye see a single visual object isntead of two, this helps the human parsing of the code, improving visual chunking and reducing noise. Bye, bearophile
Feb 05 2009
parent reply Bartosz Milewski <bartosz relisoft.com> writes:
bearophile Wrote:
 Now, back to the topic: putting arguments into a single () or {} instead of a
(){ return ;} (or with the => of C#) helps the eye see a single visual object
isntead of two, this helps the human parsing of the code, improving visual
chunking and reducing noise.

I also believe that, for readability reasons, lambdas should have some distinct token(s) in the beginning. C++ starts lambdas with brackets, []; C# has the distinct =>. I like the Haskell syntax that uses a backslash, which looks very much like lambda. Here's an example of such syntax: auto r = find!( \(a) { return a.Weight > 100; })(range); It goes without saying that any decent D editor would display the backslash as a Greek lambda.
Feb 09 2009
parent reply bearophile <bearophileHUGS lycos.com> writes:
Jarrett Billingsley:
 auto r = find!(\a -> a.Weight > 100)(range);
 [...]
 
 something(\a, b
 {
     stmt1;
     stmt2;
 });

This looks better to me (but I don't know if this is the best syntax, it's just an idea): // syntax for lambda that contains a single expression, implicit return (this is the most common case): auto r = find!(a => a.Weight > 100)(range); // syntax for lambda that contains one or more statements, no return, side effects only: something({ a, b :: stmt1; stmt2; }); // syntax for lambda that contains one or more statements, with side effects and return: something({ a, b :: stmt1; return foo(); }); Bye, bearophile
Feb 09 2009
parent reply grauzone <none example.net> writes:
 // syntax for lambda that contains a single expression, implicit return (this
is the most common case):
 auto r = find!(a => a.Weight > 100)(range);

I really like this one, but I'd prefer something like
 auto r = find(range, {a -> a.Weight > 100});

Note the -> instead of the =>, to avoid any ambiguities with the comparison operator.
Feb 09 2009
parent reply bearophile <bearophileHUGS lycos.com> writes:
grauzone Wrote:
 I really like this one, but I'd prefer something like
  > auto r = find(range, {a -> a.Weight > 100});
 
 Note the -> instead of the =>, to avoid any ambiguities with the 
 comparison operator.

Let's play more; then what do you think about (all the following are legal): auto r1 = range.find({ x -> x.weight > 100 }); auto r2 = range.find({ x :: return x.weight > 100; }); auto r3 = range.find({ x :: stmt1(x); stmt2; }); auto r4 = range.find({ x, y :: stmt1; return foo(y); }); I like those enough, they seem balanced, uniform, not too much error-prone, they have only one visual chunk, short enough and easy to write :-) Note that this syntax: auto r1 = range.find({ x -> x.weight > 100 }); using my dlibs is equivalent to the following: auto r1 = range.find((ArrayType1!(typeof(range)) x) { return x.weight > 100; }); Bye, bearophile
Feb 09 2009
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
bearophile:
 auto r1 = range.find((ArrayType1!(typeof(range)) x) { return x.weight > 100;
});

Better: auto r1 = range.find((BaseType1!(typeof(range)) x) { return x.weight > 100; }); Of course, you have to specify range twice there, and that's not good. Bye, bearophile
Feb 09 2009
prev sibling parent reply grauzone <none example.net> writes:
bearophile wrote:
 grauzone Wrote:
 I really like this one, but I'd prefer something like
  > auto r = find(range, {a -> a.Weight > 100});

 Note the -> instead of the =>, to avoid any ambiguities with the 
 comparison operator.

Let's play more; then what do you think about (all the following are legal): auto r1 = range.find({ x -> x.weight > 100 }); auto r2 = range.find({ x :: return x.weight > 100; }); auto r3 = range.find({ x :: stmt1(x); stmt2; }); auto r4 = range.find({ x, y :: stmt1; return foo(y); }); I like those enough, they seem balanced, uniform, not too much error-prone, they have only one visual chunk, short enough and easy to write :-)

Agreed. Especially I like that "normal" and "functional" uses have distinct syntax. This is much better than the proposal, to allow omission of the return statement, and to return last value of the last expression statement instead (like in (x){if(x>0) x+=1; x;}).
 Note that this syntax:
 auto r1 = range.find({ x -> x.weight > 100 });
 using my dlibs is equivalent to the following:
 auto r1 = range.find((ArrayType1!(typeof(range)) x) { return x.weight > 100;
});
 
 Bye,
 bearophile

Feb 09 2009
parent "Nick Sabalausky" <a a.a> writes:
"grauzone" <none example.net> wrote in message 
news:gmpgod$fej$1 digitalmars.com...
 bearophile wrote:
 Let's play more; then what do you think about (all the following are 
 legal):

 auto r1 = range.find({ x -> x.weight > 100 });
 auto r2 = range.find({ x :: return x.weight > 100; });
 auto r3 = range.find({ x :: stmt1(x); stmt2; });
 auto r4 = range.find({ x, y :: stmt1; return foo(y); });

 I like those enough, they seem balanced, uniform, not too much 
 error-prone, they have only one visual chunk, short enough and easy to 
 write :-)

Agreed. Especially I like that "normal" and "functional" uses have distinct syntax. This is much better than the proposal, to allow omission of the return statement, and to return last value of the last expression statement instead (like in (x){if(x>0) x+=1; x;}).

I'd rather have everything use "::" (consistent), and then say that the right-hand side of :: can be either A: one or more statements ("normal") or B: an expression ("functional").
Feb 09 2009
prev sibling next sibling parent reply grauzone <none example.net> writes:
 What I see above is a smörgåsbord of syntaxes that shoot all over the 
 proverbial barn door in the hope that one of them would strike someone's 
 fancy. That strikes me as a rather lousily done job. Also, it is my 

I find this statement rather ironic, because you also seem to be quite happy with your code-as-string-literal approach. Your approach doesn't even enforce any syntax, instead, everyone is free to invent his own.
Feb 05 2009
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
grauzone wrote:
 What I see above is a smörgåsbord of syntaxes that shoot all over the 
 proverbial barn door in the hope that one of them would strike 
 someone's fancy. That strikes me as a rather lousily done job. Also, 
 it is my 

I find this statement rather ironic, because you also seem to be quite happy with your code-as-string-literal approach.

I do understand you dislike strings as lambdas, but please do not use that dislike as a presupposition of a truth. Besides, there is no correlation. D offers two syntaxes, a decent lambda syntax (that is being improved) and a string syntax for short predicates. They have clear and distinct tradeoffs and charters. In contrast, C# seems to have decided to allow pretty much all syntax variations that parse unambiguously. You can only wonder what C#4 has in store.
 Your approach doesn't 
 even enforce any syntax, instead, everyone is free to invent his own.

There's only so many ways to write an expression involving one or two given symbols. Andrei
Feb 05 2009
prev sibling next sibling parent BCS <ao pathlink.com> writes:
Reply to Andrei,

 Kagamin wrote:

 Also,
 it is my perception (and not only mine) that C#'s creator completely
 missed the power of templates and generative programming.

I have never met the man, but it is my impression that Anders didn't miss the power, he just doesn't care for the paradigm. I would speculate that because there is nothing that it adds that can't be done at runtime (ignoring things like perf), he left it out. C# seems to take the "do stuff as late as you can" approach across the board.
Feb 05 2009
prev sibling parent Jarrett Billingsley <jarrett.billingsley gmail.com> writes:
On Mon, Feb 9, 2009 at 3:19 AM, Bartosz Milewski <bartosz relisoft.com> wro=
te:
 bearophile Wrote:
 Now, back to the topic: putting arguments into a single () or {} instead=


ual object isntead of two, this helps the human parsing of the code, improv= ing visual chunking and reducing noise.
 I also believe that, for readability reasons, lambdas should have some di=

has the distinct =3D>. I like the Haskell syntax that uses a backslash, whi= ch looks very much like lambda. Here's an example of such syntax:
 auto r =3D find!( \(a) { return a.Weight > 100; })(range);

Far too noisy. auto r =3D find!(\a -> a.Weight > 100)(range); Of course I _may_ be a bit biased in this regard, considering this is the syntax MiniD uses ;) And of course, for multi-statement lambdas: something(\a, b { stmt1; stmt2; });
Feb 09 2009
prev sibling parent reply Christopher Wright <dhasenan gmail.com> writes:
Kagamin wrote:
 Yeah, C# lambdas are the killer feature. Slick, readable, C-compatible. Anders
knows his job. Let's face it: delegate literals suck a little, mixins as
delegates suck a lot, the former is too verbose, the latter just sucks.

C# delegates in C# 2.0 are annoying. I try not to use them. The reason: D: void foo(void delegate(int) dg); C#: delegate void SomeName(int i); void foo(SomeName dg); Does C# 3 fix this? I've seen the new syntax for defining delegates, but not for using them.
Feb 05 2009
next sibling parent "Nick Sabalausky" <a a.a> writes:
"Christopher Wright" <dhasenan gmail.com> wrote in message 
news:gmfsqa$311e$1 digitalmars.com...
 Kagamin wrote:
 Yeah, C# lambdas are the killer feature. Slick, readable, C-compatible. 
 Anders knows his job. Let's face it: delegate literals suck a little, 
 mixins as delegates suck a lot, the former is too verbose, the latter 
 just sucks.

C# delegates in C# 2.0 are annoying. I try not to use them. The reason: D: void foo(void delegate(int) dg); C#: delegate void SomeName(int i); void foo(SomeName dg); Does C# 3 fix this? I've seen the new syntax for defining delegates, but not for using them.

C# provided my first introduction to delegates (not counting function-pointers in C), and that oddity actually made it harder for me to really wrap my head around them. Once I did though, I came around to D and thought "Wow! That's so simple!", and it actually helped me understand C#'s delegates better. My only issue with D's delegate-declaration syntax is the semi-messy ordering of "return type, delegate keyword, paramaters, name". Something closer to following would seem much more intuitive to me: void foo(delegate void(int) dg); //or void foo(void dg(int)); //or void foo(delegate(void <- int) dg); //etc... Although, I realize the current way is done to be consistent with function definitions. But, as I mentioned in another branch of the thread, I wouldn't mind function definitions like this: func foo(int <- int x) {return x+1;} //or func foo {(int <- int x) return x+1;} //or func foo {int <- int x :: return x+1;}
Feb 05 2009
prev sibling parent Robert Fraser <fraserofthenight gmail.com> writes:
Christopher Wright wrote:
 C#:
 delegate void SomeName(int i);
 void foo(SomeName dg);

Ugh, don't remind me!
Feb 05 2009