www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - lazy redux

reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Should we sack lazy? I'd like it to have a reasonable replacement. Ideas 
are welcome!

Andrei
Dec 05 2009
next sibling parent reply Tim Matthews <tim.matthews7 gmail.com> writes:
Andrei Alexandrescu wrote:
 Should we sack lazy? I'd like it to have a reasonable replacement. Ideas 
 are welcome!
 
 Andrei

According to the doc page: http://www.digitalmars.com/d/2.0/lazy-evaluation.html you (and/or Tomasz Stachowiak) were the one who suggested it in the first place. How'd it go for you? :) In haskell land they love lazy evaluation. Lazy evaluation can have many advantages but depending on the situation can be wrote using alternates that d provides: mixins -- allows turning a string into expression at a later time delegates -- only called when used explicitly logical ('&&' and '||') operators already only evaluate what is needed If it stays though I would prefer a different syntax so where we already have: char[] delegate() dg char[] function() fp then something like this would be more consistent and intuitive: char[] expression exp rather than the current: lazy char[] exp
Dec 05 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Tim Matthews wrote:
 Andrei Alexandrescu wrote:
 Should we sack lazy? I'd like it to have a reasonable replacement. 
 Ideas are welcome!

 Andrei

According to the doc page: http://www.digitalmars.com/d/2.0/lazy-evaluation.html you (and/or Tomasz Stachowiak) were the one who suggested it in the first place. How'd it go for you? :)

Well the story is, I suggested something very different, namely automatic conversion of expressions to delegates, i.e.: void fun(int delegate() dg) { ... } int a; fun(a += 5); // works, same as fun({ a += 5; }); I am not sure about how good that idea is, but anyway on top of it Tomasz suggested defining a storage class for that, which (1) takes matters to a completely place, (2) marks a sharp decline in the quality of the feature. I protested the addition of "lazy" very strongly in a subsequent post. Since I was relatively new in the newsgroup and the rant bordered on an ad hominem attack against Tomasz, it earned me a good amount of negative goodwill (and for good reasons). If I remember correctly, the flamewar that ensued in my leaving the newsgroup for a good while. I'm very glad the atmosphere has improved so much since - back then it was often some sort of a turf war. Anyhow, if we leave the feature as an implicit conversion expression -> delegate or function, I think the whole thing is sound (contingent on probably taking care of a couple of corner cases). The remaining problem is that fun(gun()) does not always evaluate gun(), even though the reader who is unaware or forgot about the conversion thinks otherwise. If we require fun({gun();}), the notation is more awkward but also clarifies to the reader what's happening. My take is this: since assert() establishes a precedent, I'd hate that to be magic inaccessible to mere mortals. Functions like enforce() and logging frameworks can make good use of the feature.
 In haskell land they love lazy evaluation. Lazy evaluation can have many 
 advantages but depending on the situation can be wrote using alternates 
 that d provides:
 
 mixins -- allows turning a string into expression at a later time
 delegates -- only called when used explicitly
 logical ('&&' and '||') operators already only evaluate what is needed
 
 If it stays though I would prefer a different syntax so where we already 
 have:
 
 char[] delegate() dg
 char[] function() fp
 
 then something like this would be more consistent and intuitive:
 
 char[] expression exp
 
 rather than the current:
 
 lazy char[] exp

I think defining a new type is a lot of work, even more work than "lazy" itself which is just a storage class. Andrei
Dec 06 2009
parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2009-12-06 10:44:17 -0500, Andrei Alexandrescu 
<SeeWebsiteForEmail erdani.org> said:

 Tim Matthews wrote:
 Andrei Alexandrescu wrote:
 Should we sack lazy? I'd like it to have a reasonable replacement. 
 Ideas are welcome!
 
 Andrei

According to the doc page: http://www.digitalmars.com/d/2.0/lazy-evaluation.html you (and/or Tomasz Stachowiak) were the one who suggested it in the first place. How'd it go for you? :)

Well the story is, I suggested something very different, namely automatic conversion of expressions to delegates, i.e.: void fun(int delegate() dg) { ... } int a; fun(a += 5); // works, same as fun({ a += 5; }); I am not sure about how good that idea is, but anyway on top of it Tomasz suggested defining a storage class for that, which (1) takes matters to a completely place, (2) marks a sharp decline in the quality of the feature. I protested the addition of "lazy" very strongly in a subsequent post. Since I was relatively new in the newsgroup and the rant bordered on an ad hominem attack against Tomasz, it earned me a good amount of negative goodwill (and for good reasons). If I remember correctly, the flamewar that ensued in my leaving the newsgroup for a good while. I'm very glad the atmosphere has improved so much since - back then it was often some sort of a turf war. Anyhow, if we leave the feature as an implicit conversion expression -> delegate or function, I think the whole thing is sound (contingent on probably taking care of a couple of corner cases). The remaining problem is that fun(gun()) does not always evaluate gun(), even though the reader who is unaware or forgot about the conversion thinks otherwise. If we require fun({gun();}), the notation is more awkward but also clarifies to the reader what's happening. My take is this: since assert() establishes a precedent, I'd hate that to be magic inaccessible to mere mortals. Functions like enforce() and logging frameworks can make good use of the feature.

That's what I'd do too. But I think we could improve lazy by splitting it in two. The first would work as it does today, allowing things like enforce(). The second could be less intrusive by having no semantic implications for the caller. The idea is simple: force purity on it. This way, the caller doesn't have to think about whether or not an argument is a lazy one or not. Suggested syntax: void logIfFalse(bool condition, pure lazy string message); logIfFalse(i == 1, createMessage()); If the expression "createMessage()" is pure, then it'll be evaluated lazily. Otherwise it should be evaluated in advance (like normal parameters) and a delegate returning the result should be given to the function. This would make it mostly a cross-function manual optimization mechanism. The called function can assume its "pure lazy" delegate is pure and optimize things accordingly. If you have a couple of functions passing themselves a "pure lazy" argument like that, a good optimizer could make sure it is never is evaluated more than once even across function boundaries. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Dec 06 2009
parent Michal Minich <michal.minich gmail.com> writes:
Hello Michel,

 
 void logIfFalse(bool condition, pure lazy string message);
 
 logIfFalse(i == 1, createMessage());

I like the idea that of restricting what is passed into function; void logIfFalse(bool condition, lazy pure nothrow safe string message); In wich case, expression passed needs to be checked for these restrictions. But I would leave the semantics of current lazy as is, or with one adjustment: It would be better if the compiler could make sure it is executed only zero or one time (but I don't think it has much practical advantage).
Dec 07 2009
prev sibling next sibling parent =?UTF-8?B?UGVsbGUgTcOlbnNzb24=?= <pelle.mansson gmail.com> writes:
Andrei Alexandrescu wrote:
 Should we sack lazy? I'd like it to have a reasonable replacement. Ideas 
 are welcome!
 
 Andrei

syntax for passing delegates. In my mind, a lazy parameter should evaluate just once, and save that value. In case of further usage, it should use the saved value instead. This is actually how I thought they worked until I saw Walter's example with writef(x++).
Dec 06 2009
prev sibling next sibling parent bearophile <bearophileHUGS lycos.com> writes:
Andrei Alexandrescu:

 Should we sack lazy? I'd like it to have a reasonable replacement. Ideas 
 are welcome!

I am not yet able to suggest you a replacement. This is a small post I have recently read about limits and problems of laziness in Scala, that looks similar to laziness in D: http://pchiusano.blogspot.com/2009/05/optional-laziness-doesnt-quite-cut-it.html I can also show you two usages of mine of lazy. I have used lazy in D1 for two main purposes: The first problem come from translating Python code to D. In Python the 'or' operator is lazy, and it returns the first not false object it sees (in Python empty collections are false), few examples:
 0 or 4



 [] or [1,2] or [3]



 (5,) or [1,2]



 x = 1
 def foo(): global x; x += 1



 [5] or foo()



 x



 [] or foo()
 x



It's easy to implement a n-way version of that, this is a reduced 2-way (not tested): T1 lazyOr(T1, T2)(T1 x, lazy T2 y) { static assert(CastableType!(T1, T2), "..."); if (boolean(x)) return x; else return y; } Another small problem of lazy in D is that the caller doesn't know that an expression will be used in a lazy way. This is sometimes handy, but also is not very explicit, so it can lead to troubles. A simile solution to this is to require the 'lazy' on the calling site too: auto x = lazyOr(a, lazy b); I don't like that a lot, but it's more explicit. I have used lazy to implement a poor's man version of the array comps of Python, that I've now seen I can't live without in D. The starting code that I have worked on was from Henning Hasemann in 2007. This is one of those versions, I use more complex versions too: TA[] select(TA, TI, TC, TP)(lazy TA mapper, ref TI iter1, TC items1, lazy TP where) { ArrayBuilder!(TA) result; auto aux1 = iter1; // save original iteration variable static if (IsAA!(TC)) { foreach (k, v; items1) { iter1 = k; if (where()) result ~= mapper(); } } else { foreach (el; items1) { iter1 = el; if (where()) result ~= mapper(); } } iter1 = aux1; // restore original iteration variable return result.toarray; } A small usage example: int i; select(toString(i), i, [1,2,3,4,5,6,7], i % 2 == 0) ==> ["2", "4", "6"] The disadvantages of that code are big: - It looks like magic, mostly because of lazy. - It needs an already defined loop variable (here 'i'). - Even the LDC compiler is not able to compile that code well, so it's not top efficiency, despite the usage of ArrayBuilder inside it. - It syntax is quite less readable than the Python version: [str(i) for i in [1,2,3,4,5,6,7] if i % 2 == 0] - It can be used to produce an actual array only, it can't be used for lazy generators as in Python: (str(i) for i in [1,2,3,4,5,6,7] if i % 2 == 0) - It's not a built-in thing, some D programmers may not understand or like it. But its advantages are bigger than those big disadvantages: - For not-performance critical parts of the code it's fast enough. Where the profile tells me that some code is slow it's easy to lower the level of the code. - I have understood only now the main advantage of array comps. Master chess players are able to play many games at the same time and they are able to memorize the configuration of the pieces on many chessboards. Newbie chess players aren't able to remember so many boards. Experiments have shown that if the pieces are put randomly on the board, then master players are able to remember about as many chess positions as newbies or just a little more. During true games masters are able to memorize several chessboards because they don't memorize the position of each piece, they divide the boards in pieces that have a semantic meaning, and then memorize those few chunks. Such 'chunking' is essential during their play too, they think mostly in terms of those chunks, those gestalts, and often not with the movements of single pieces. This chunking is useful because human brains aren't able to manage more than a handful of separated items when they think (about seven), but such items can be complex, they are chunks. Python list comps allow to cut a piece of a code and think of it as a single chunk, allowing me to program at a higher level and better. This is why Python3 has added two more kinds of comps, for sets and dicts: a_set = {x for x in xrange(10) if x & 1} a_dict = {x*x : x for x in xrange(100)} Bye, bearophile
Dec 06 2009
prev sibling next sibling parent ZY.Zhou <rinick goozo.net> writes:
Andrei Alexandrescu Wrote:
 Should we sack lazy? I'd like it to have a reasonable replacement. Ideas 
 are welcome!

Once I had a bug, it's like: foo(step1()); step2(); I forget foo() uses lazy parameter, so step1 never get called. Now I prefer to use delegate, I just hope this can compile: void foo(int delegate() dg){} foo({step1()});
Dec 06 2009
prev sibling parent reply Michal Minich <michal.minich gmail.com> writes:
Hello Andrei,

 Should we sack lazy? I'd like it to have a reasonable replacement.
 Ideas are welcome!
 
 Andrei
 

there are 3 sides from which to look at lazy function parameter. 1. Usage - being able to send expressions to function is very important for writing clear and nice looking code. I think just by requiring enclosure in curly braces "fun({gun();})" would make this feature quite less appealing and used. This syntactic feature is very pleasing - by whichever feature at definition side it is achieved (macro/expression type), it should stay here. 2. Writing - On the function definition side, I don't see much difference in *writing* "lazy int dg" or "int delegate () dg". The functions that take lazy parameter are not written daily - their usage is much more frequent (enforce, logging). One problem I see currently with "lazy" that by specification it can be evaluated zero or more times. For me, "lazy" means zero or one time (compiler should handle this internally). This is not particularly important, because it is probably not so hard for programmer to write correct function - evaluation as many times as needed (which I think is usually 0 or 1 anyway). Just, the name "lazy" does not seems correct to me. 3. Contract - It was mentioned that programmer may expect "foo" to be always called in "bar (foo())". In case bar takes lazy argument, it may not be called. We need to have function which takes expression as parameter, while being able to separately express that function takes delegate. This should minimize cases of possible mismatched parameters. If we would replace "lazy" with explicit delegate parameter specification in function declaration, we would lost this possibility, and I think there would be more bugs cased by mismatched parameters. So I think we should leave "lazy" in, until we have macros. But introduction "{ epx }" as delegate/function literal for functions with no arguments, which implicitly returns result of the expression, seems to me as a good idea.
Dec 07 2009
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Michal Minich:
 But introduction 
 "{ epx }" as delegate/function literal for functions with no arguments, which 
 implicitly returns result of the expression, seems to me as a good idea.

It's a special case, and special cases help to kill languages. It's not important enough. But a general shorter syntax for lambdas is possible, like the C# one. Evaluations lazy arguments only 0 or 1 times sounds like a nice idea. Bye, bearophile
Dec 07 2009
next sibling parent reply Michal Minich <michal.minich gmail.com> writes:
Hello bearophile,

 Michal Minich:
 
 But introduction "{ epx }" as delegate/function literal for functions
 with no arguments, which implicitly returns result of the expression,
 seems to me as a good idea.
 

not important enough. But a general shorter syntax for lambdas is possible, like the C# one. Evaluations lazy arguments only 0 or 1 times sounds like a nice idea. Bye, bearophile

Yes, it works well in C#, and it is one of the best extension of this language (only adding generics was better). Consider how it works in C#, and how it could in D // 1. lambda with no parameter int a; var t = new Thread ( () => a=42 ); // 2. lambda with one parameter string[] arr; Array.FindAll (arr, item => item.Contains ("abc")); // 3. lambda with more parameters Foo ( (a, b) => a + b ); // 4. lambda with statement (previous examples were expressions) Array.FindAll (arr, item => { return item.Contains ("abc"); } ); // curly braces, semicolon and return are required when statement is used. D could use: 1. auto t = new Thread ( { a=42 } ); or auto t = new Thread ( () { a=42 } ); 2. array.findAll (arr, (item) { item.contains ("abc") } ); 3. foo ( (a, b) { a + b } ); 4. array.findAll (arr, (item) { return item.contains ("abc"); } ); I'm not proposing this syntax (maybe I probably should, but I have feeling I would not be first). It may not even be possible to parse it, but seems to me more similar to how currently functions are written. In this setting {exp} or {stm} is not *special* case.
Dec 07 2009
next sibling parent =?UTF-8?B?UGVsbGUgTcOlbnNzb24=?= <pelle.mansson gmail.com> writes:
retard wrote:
 Mon, 07 Dec 2009 13:17:10 +0000, Michal Minich wrote:
 
 Hello bearophile,

 Michal Minich:

 But introduction "{ epx }" as delegate/function literal for functions
 with no arguments, which implicitly returns result of the expression,
 seems to me as a good idea.

important enough. But a general shorter syntax for lambdas is possible, like the C# one. Evaluations lazy arguments only 0 or 1 times sounds like a nice idea. Bye, bearophile

language (only adding generics was better). Consider how it works in C#, and how it could in D // 1. lambda with no parameter int a; var t = new Thread ( () => a=42 ); // 2. lambda with one parameter string[] arr; Array.FindAll (arr, item => item.Contains ("abc")); // 3. lambda with more parameters Foo ( (a, b) => a + b );

You surely understand that Walter doesn't have enough time to change this before the Andrei's book is out. So D2 won't be getting this. Besides, he hasn't even said that he likes the syntax. And D can't infer the types that way, you would need
 Foo (  (auto a, auto b) => a + b );

or
 Foo (  [T,S](T a, S b) => a + b );

 // 4. lambda with statement (previous examples were expressions)
 Array.FindAll (arr, item =>  { return item.Contains ("abc"); } ); //
 curly braces, semicolon and return are required when statement is used.

 D could use:

 1. auto t = new Thread ( { a=42 } );
 or auto t = new Thread ( () { a=42 } );

 2. array.findAll (arr, (item) { item.contains ("abc") } );

Andrei invented the string template parameter hack to avoid this. This would work too slowly since the dmd backend from the 1960s cannot inline anonymous functions. It can only inline named functions.
           
 3. foo ( (a, b) { a + b } );

 4. array.findAll (arr, (item) { return item.contains ("abc"); } );

 I'm not proposing this syntax (maybe I probably should, but I have
 feeling I would not be first). It may not even be possible to parse it,
 but seems to me more similar to how currently functions are written. In
 this setting {exp} or {stm} is not *special* case.


It not doing so right now is on the bugzilla.
Dec 07 2009
prev sibling parent Lutger <lutger.blijdestijn gmail.com> writes:
retard wrote:
...
 You surely understand that Walter doesn't have enough time to change this
 before the Andrei's book is out. So D2 won't be getting this. Besides, he
 hasn't even said that he likes the syntax. And D can't infer the types
 that way, you would need
 
 Foo (  (auto a, auto b) => a + b );


why not ? (a, b) { return a + b; } already works for template alias parameters.
 or
 
 Foo (  [T,S](T a, S b) => a + b );

 
 // 4. lambda with statement (previous examples were expressions)
 Array.FindAll (arr, item =>  { return item.Contains ("abc"); } ); //
 curly braces, semicolon and return are required when statement is used.
 
 D could use:
 
 1. auto t = new Thread ( { a=42 } );
 or auto t = new Thread ( () { a=42 } );
 
 2. array.findAll (arr, (item) { item.contains ("abc") } );

Andrei invented the string template parameter hack to avoid this. This would work too slowly since the dmd backend from the 1960s cannot inline anonymous functions. It can only inline named functions.

I don't think inlining is done in the backend.
Dec 07 2009
prev sibling next sibling parent reply "Denis Koroskin" <2korden gmail.com> writes:
On Mon, 07 Dec 2009 16:17:10 +0300, Michal Minich  
<michal.minich gmail.com> wrote:

 Hello bearophile,

 Michal Minich:

 But introduction "{ epx }" as delegate/function literal for functions
 with no arguments, which implicitly returns result of the expression,
 seems to me as a good idea.

not important enough. But a general shorter syntax for lambdas is possible, like the C# one. Evaluations lazy arguments only 0 or 1 times sounds like a nice idea. Bye, bearophile

Yes, it works well in C#, and it is one of the best extension of this language (only adding generics was better). Consider how it works in C#, and how it could in D // 1. lambda with no parameter int a; var t = new Thread ( () => a=42 ); // 2. lambda with one parameter string[] arr; Array.FindAll (arr, item => item.Contains ("abc")); // 3. lambda with more parameters Foo ( (a, b) => a + b ); // 4. lambda with statement (previous examples were expressions) Array.FindAll (arr, item => { return item.Contains ("abc"); } ); // curly braces, semicolon and return are required when statement is used. D could use: 1. auto t = new Thread ( { a=42 } ); or auto t = new Thread ( () { a=42 } );

It already works, just try it (but don't forget to put a semicolon at the end).
 2. array.findAll (arr, (item) { item.contains ("abc") } );
 3. foo ( (a, b) { a + b } );

 4. array.findAll (arr, (item) { return item.contains ("abc"); } );

 I'm not proposing this syntax (maybe I probably should, but I have  
 feeling I would not be first). It may not even be possible to parse it,  
 but seems to me more similar to how currently functions are written. In  
 this setting {exp} or {stm} is not *special* case.

I believe it would work. And yes, it was already proposed by many others.
Dec 07 2009
next sibling parent reply Michal Minich <michal.minich gmail.com> writes:
Hello Denis,

 1. auto t = new Thread ( { a=42 } );
 or auto t = new Thread ( () { a=42 } );

the end).
 2. array.findAll (arr, (item) { item.contains ("abc") } ); 3. foo (
 (a, b) { a + b } );
 
 4. array.findAll (arr, (item) { return item.contains ("abc"); } );
 
 I'm not proposing this syntax (maybe I probably should, but I have
 feeling I would not be first). It may not even be possible to parse
 it,  but seems to me more similar to how currently functions are
 written. In  this setting {exp} or {stm} is not *special* case.
 

others.

it works with two differences: 1. the semicolon is required, even if the body consist only of one expression. This is a minor detail. 2. more importantly - parameter types must be specified explicitly. I don't understand why type of b cannot be inferred in this example: void foo (void delegate (int a) dg) { dg(1); } void main () { foo ( (int b) { writeln (b); } ); } Does the type inference has problem with template code or some other combination of features...?
Dec 07 2009
next sibling parent reply klickverbot <klickverbot gmail.com> writes:
Denis Koroskin wrote:
 Although I believe it is implementable and worth the trouble, there is a
 little gain in this feature and that's probably why it is low in the list.
 I think that Walter will give a green light if someone implements the
 feature and provides a complete patch.
 
 Any volunteers?

If someone should step forward to implement this: I would really like to be able to omit the semicolon inside one-statement delegates/lambdas. It makes using them feel much more natural (at least to me) – just look at some Ruby code.
Dec 07 2009
parent "Nick Sabalausky" <a a.a> writes:
"klickverbot" <klickverbot gmail.com> wrote in message 
news:hfj6eb$kss$1 digitalmars.com...
 Denis Koroskin wrote:
 Although I believe it is implementable and worth the trouble, there is a
 little gain in this feature and that's probably why it is low in the 
 list.
 I think that Walter will give a green light if someone implements the
 feature and provides a complete patch.

 Any volunteers?

If someone should step forward to implement this: I would really like to be able to omit the semicolon inside one-statement delegates/lambdas. It makes using them feel much more natural (at least to me) - just look at some Ruby code.

I've had an idea for a more expanded version of what you propose: Many operators tend to fall into one of two categories: Separators vs Terminators (separators being ones that are put between two operands, and terminators being ones that are put after each operand). Terminators (like semicolon) have the advantage of being more consistent, easier to edit, and easier to programmatically generate. Separators (like addition) have the advantages of being more convenient and less visually-noisy when there are just a few operands and often just making more sense for the given task (imagine "1+2" being written instead as "1+2+"). Traditionally, every such operator is cemented in stone by the language as being either a separator or a terminator. But D has already noticed, at least in a small way, that sometimes it's better to leave the separator vs terminator choice up to the programmer on a case-by-case basis. Specifically, the commas from array literals and [real] enum declarations can be used either way. This, of course, is done by making the final one optional. Although I'm one of the people who isn't typically too keen on optional semicolons, I've wondered if it may be a good idea to extend this "optional-final-operator" concept to semicolons. The primary practical reason would be increased convenience and readability for short lambdas (or short functions and classes). And, at least in theory, this could maybe even be extended to all separators and terminators, including things like arithmetic and logic. Though that wouldn't initially seem to make much sense, the trick would be to just apply the operator's identity value (0 for addition, 1 for multiplication, true for logical and, false for logical or). But of course, the downside in these cases would be a silent error if you forgot to type the last value.
Dec 07 2009
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Leandro Lucarella wrote:
 Michal Minich, el  7 de diciembre a las 13:51 me escribiste:
 Hello Denis,

 1. auto t = new Thread ( { a=42 } );
 or auto t = new Thread ( () { a=42 } );

the end).
 2. array.findAll (arr, (item) { item.contains ("abc") } ); 3. foo (
 (a, b) { a + b } );

 4. array.findAll (arr, (item) { return item.contains ("abc"); } );

 I'm not proposing this syntax (maybe I probably should, but I have
 feeling I would not be first). It may not even be possible to parse
 it,  but seems to me more similar to how currently functions are
 written. In  this setting {exp} or {stm} is not *special* case.

others.

1. the semicolon is required, even if the body consist only of one expression. This is a minor detail. 2. more importantly - parameter types must be specified explicitly. I don't understand why type of b cannot be inferred in this example: void foo (void delegate (int a) dg) { dg(1); } void main () { foo ( (int b) { writeln (b); } ); }

It doesn't do implicit returning either: $ cat -n x.d 1 2 import std.stdio; 3 4 void foo (int delegate() dg) 5 { 6 writeln(dg()); 7 } 8 9 void main() 10 { 11 int a = 5; 12 foo({a;}); 13 } 14 $ dmd x.d x.d(12): Error: var has no effect in expression (a) x.d(12): Error: function x.foo (int delegate() dg) is not callable using argument types (void delegate()) x.d(12): Error: cannot implicitly convert expression (__dgliteral1) of type void delegate() to int delegate() To make it work you have to do it like this: 12 foo({return a;}); Which is considerably uglier than 12 foo({a}); At least when talking about replacing "lazy" :) Nobody wants to write: enforce({ return a == 5; }); instead of: enforce(a == 5); But: enforce({a == 5}); Could be acceptable.

A nit - it's the second argument of enforce that must be lazy, e.g. enforce(a == 5, {return text("a is not 5, it's ", a);}); Not looking good anyway. Andrei
Dec 07 2009
prev sibling next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Mon, 07 Dec 2009 16:51:29 +0300, Michal Minich  
<michal.minich gmail.com> wrote:

 Hello Denis,

 1. auto t = new Thread ( { a=42 } );
 or auto t = new Thread ( () { a=42 } );

the end).
 2. array.findAll (arr, (item) { item.contains ("abc") } ); 3. foo (
 (a, b) { a + b } );
  4. array.findAll (arr, (item) { return item.contains ("abc"); } );
  I'm not proposing this syntax (maybe I probably should, but I have
 feeling I would not be first). It may not even be possible to parse
 it,  but seems to me more similar to how currently functions are
 written. In  this setting {exp} or {stm} is not *special* case.

others.

it works with two differences: 1. the semicolon is required, even if the body consist only of one expression. This is a minor detail. 2. more importantly - parameter types must be specified explicitly. I don't understand why type of b cannot be inferred in this example: void foo (void delegate (int a) dg) { dg(1); } void main () { foo ( (int b) { writeln (b); } ); } Does the type inference has problem with template code or some other combination of features...?

It complicates semantic pass quite significantly: you can't semantically analyze delegate until you analyze "foo". It also can't infer arguments types in some cases: void foo(T)(void delegate(T) dg); foo( (a) { a.bar(); } ); Although I believe it is implementable and worth the trouble, there is a little gain in this feature and that's probably why it is low in the list. I think that Walter will give a green light if someone implements the feature and provides a complete patch. Any volunteers?
Dec 07 2009
prev sibling next sibling parent Leandro Lucarella <llucax gmail.com> writes:
Michal Minich, el  7 de diciembre a las 13:51 me escribiste:
 Hello Denis,
 
1. auto t = new Thread ( { a=42 } );
or auto t = new Thread ( () { a=42 } );

the end).
2. array.findAll (arr, (item) { item.contains ("abc") } ); 3. foo (
(a, b) { a + b } );

4. array.findAll (arr, (item) { return item.contains ("abc"); } );

I'm not proposing this syntax (maybe I probably should, but I have
feeling I would not be first). It may not even be possible to parse
it,  but seems to me more similar to how currently functions are
written. In  this setting {exp} or {stm} is not *special* case.

others.

it works with two differences: 1. the semicolon is required, even if the body consist only of one expression. This is a minor detail. 2. more importantly - parameter types must be specified explicitly. I don't understand why type of b cannot be inferred in this example: void foo (void delegate (int a) dg) { dg(1); } void main () { foo ( (int b) { writeln (b); } ); }

It doesn't do implicit returning either: $ cat -n x.d 1 2 import std.stdio; 3 4 void foo (int delegate() dg) 5 { 6 writeln(dg()); 7 } 8 9 void main() 10 { 11 int a = 5; 12 foo({a;}); 13 } 14 $ dmd x.d x.d(12): Error: var has no effect in expression (a) x.d(12): Error: function x.foo (int delegate() dg) is not callable using argument types (void delegate()) x.d(12): Error: cannot implicitly convert expression (__dgliteral1) of type void delegate() to int delegate() To make it work you have to do it like this: 12 foo({return a;}); Which is considerably uglier than 12 foo({a}); At least when talking about replacing "lazy" :) Nobody wants to write: enforce({ return a == 5; }); instead of: enforce(a == 5); But: enforce({a == 5}); Could be acceptable. -- Leandro Lucarella (AKA luca) http://llucax.com.ar/ ---------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------- Ambition makes you look pretty ugly
Dec 07 2009
prev sibling parent retard <re tard.com.invalid> writes:
Mon, 07 Dec 2009 16:19:37 +0100, klickverbot wrote:

 Denis Koroskin wrote:
 Although I believe it is implementable and worth the trouble, there is
 a little gain in this feature and that's probably why it is low in the
 list. I think that Walter will give a green light if someone implements
 the feature and provides a complete patch.
 
 Any volunteers?

If someone should step forward to implement this: I would really like to be able to omit the semicolon inside one-statement delegates/lambdas. It makes using them feel much more natural (at least to me) – just look at some Ruby code.

I think the counterargument was: semicolons help spotting bugs everywhere since the parser immediately sees this as syntax error.
Dec 07 2009
prev sibling next sibling parent Leandro Lucarella <llucax gmail.com> writes:
Michal Minich, el  7 de diciembre a las 13:17 me escribiste:
 Hello bearophile,
 
Michal Minich:

But introduction "{ epx }" as delegate/function literal for functions
with no arguments, which implicitly returns result of the expression,
seems to me as a good idea.

not important enough. But a general shorter syntax for lambdas is possible, like the C# one. Evaluations lazy arguments only 0 or 1 times sounds like a nice idea. Bye, bearophile

Yes, it works well in C#, and it is one of the best extension of this language (only adding generics was better). Consider how it works in C#, and how it could in D // 1. lambda with no parameter int a; var t = new Thread ( () => a=42 ); // 2. lambda with one parameter string[] arr; Array.FindAll (arr, item => item.Contains ("abc")); // 3. lambda with more parameters Foo ( (a, b) => a + b ); // 4. lambda with statement (previous examples were expressions) Array.FindAll (arr, item => { return item.Contains ("abc"); } ); // curly braces, semicolon and return are required when statement is used. D could use: 1. auto t = new Thread ( { a=42 } );

I think this syntax is pretty neat, because it's make the caller intentions explicit (and it's very compact). For example, if you write: foo({bar()}); Is more obvious that bar() might not be even called. Is like explicit reference passing (which D doesn't have unfortunately). I don't know if it's doable though, maybe it's ambiguous? -- Leandro Lucarella (AKA luca) http://llucax.com.ar/ ---------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------- MP: Cómo está, estimado Bellini? B: Muy bien, Mario, oraculizando. MP: Qué tengo? B: El auto mal estacionado. MP: No, en mi mano, Bellini... B: Una murga! MP: No, escuche bien. Es de lona. B: Un ring, Mario. MP: No Bellini. Tiene cordones. B: La vereda. MP: No Bellini! Muy fácil, eh! Es B: Una modelo, Mario! imprescindible para jugar al B: Un negro, Mario. basquet. MP: No, Bellini, no y no! -- El Gran Bellini (Mario Podestá con unas zapatillas de basquet)
Dec 07 2009
prev sibling next sibling parent retard <re tard.com.invalid> writes:
Mon, 07 Dec 2009 13:17:10 +0000, Michal Minich wrote:

 Hello bearophile,
 
 Michal Minich:
 
 But introduction "{ epx }" as delegate/function literal for functions
 with no arguments, which implicitly returns result of the expression,
 seems to me as a good idea.
 

important enough. But a general shorter syntax for lambdas is possible, like the C# one. Evaluations lazy arguments only 0 or 1 times sounds like a nice idea. Bye, bearophile

Yes, it works well in C#, and it is one of the best extension of this language (only adding generics was better). Consider how it works in C#, and how it could in D // 1. lambda with no parameter int a; var t = new Thread ( () => a=42 ); // 2. lambda with one parameter string[] arr; Array.FindAll (arr, item => item.Contains ("abc")); // 3. lambda with more parameters Foo ( (a, b) => a + b );

You surely understand that Walter doesn't have enough time to change this before the Andrei's book is out. So D2 won't be getting this. Besides, he hasn't even said that he likes the syntax. And D can't infer the types that way, you would need
 Foo (  (auto a, auto b) => a + b );

or
 Foo (  [T,S](T a, S b) => a + b );

 
 // 4. lambda with statement (previous examples were expressions)
 Array.FindAll (arr, item =>  { return item.Contains ("abc"); } ); //
 curly braces, semicolon and return are required when statement is used.
 
 D could use:
 
 1. auto t = new Thread ( { a=42 } );
 or auto t = new Thread ( () { a=42 } );
 
 2. array.findAll (arr, (item) { item.contains ("abc") } );

Andrei invented the string template parameter hack to avoid this. This would work too slowly since the dmd backend from the 1960s cannot inline anonymous functions. It can only inline named functions.
           
 3. foo ( (a, b) { a + b } );
 
 4. array.findAll (arr, (item) { return item.contains ("abc"); } );
 
 I'm not proposing this syntax (maybe I probably should, but I have
 feeling I would not be first). It may not even be possible to parse it,
 but seems to me more similar to how currently functions are written. In
 this setting {exp} or {stm} is not *special* case.

Dec 07 2009
prev sibling parent retard <re tard.com.invalid> writes:
Tue, 08 Dec 2009 01:02:04 +0100, Lutger wrote:

 retard wrote:
 ...
 You surely understand that Walter doesn't have enough time to change
 this before the Andrei's book is out. So D2 won't be getting this.
 Besides, he hasn't even said that he likes the syntax. And D can't
 infer the types that way, you would need
 
 Foo (  (auto a, auto b) => a + b );


why not ? (a, b) { return a + b; } already works for template alias parameters.
 or
 
 Foo (  [T,S](T a, S b) => a + b );

 // 4. lambda with statement (previous examples were expressions)
 Array.FindAll (arr, item =>  { return item.Contains ("abc"); } ); //
 curly braces, semicolon and return are required when statement is
 used.
 
 D could use:
 
 1. auto t = new Thread ( { a=42 } );
 or auto t = new Thread ( () { a=42 } );
 
 2. array.findAll (arr, (item) { item.contains ("abc") } );

Andrei invented the string template parameter hack to avoid this. This would work too slowly since the dmd backend from the 1960s cannot inline anonymous functions. It can only inline named functions.


Nope, but it isn't done in other parts of the compiler, either, for that matter.
Dec 07 2009
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Michal Minich wrote:
 Hello Andrei,
 
 Should we sack lazy? I'd like it to have a reasonable replacement.
 Ideas are welcome!

 Andrei

there are 3 sides from which to look at lazy function parameter. 1. Usage - being able to send expressions to function is very important for writing clear and nice looking code. I think just by requiring enclosure in curly braces "fun({gun();})" would make this feature quite less appealing and used. This syntactic feature is very pleasing - by whichever feature at definition side it is achieved (macro/expression type), it should stay here.

I think the same. But I seem to recall that at least one person on reddit thought it's a major loss of a guarantee.
 2. Writing - On the function definition side, I don't see much 
 difference in *writing* "lazy int dg" or "int delegate () dg". The 
 functions that take lazy parameter are not written daily - their usage 
 is much more frequent (enforce, logging).

Nononono. There's a huge difference. If you have "lazy int x", then that's perceived as an int with some sort of storage class. Then a putative user would expect auto y = x; to define another int. In fact it's unclear whether y should be an int or a delegate. The presence of the delegate type clarifies what's going on. I could come up with several other examples that reveal "lazy int x" to be a complete crock.
 One problem I see currently with "lazy" that by specification it can be 
 evaluated zero or more times. For me, "lazy" means zero or one time 
 (compiler should handle this internally). This is not particularly 
 important, because it is probably not so hard for programmer to write 
 correct function - evaluation as many times as needed (which I think is 
 usually 0 or 1 anyway). Just, the name "lazy" does not seems correct to me.

I agree. That's why I say we yank it and at most allow function and delegate parameters to accept expressions. That approach has its own problems. Consider: void fun(int delegate() dg) { ... } int delegate() gun() { ... } fun(gun()); In this case gun does get evaluated :o). Andrei
Dec 07 2009
parent Michal Minich <michal.minich gmail.com> writes:
Hello Andrei,

 Michal Minich wrote:
 
 Hello Andrei,
 
 Should we sack lazy? I'd like it to have a reasonable replacement.
 Ideas are welcome!
 
 Andrei
 

1. Usage - being able to send expressions to function is very important for writing clear and nice looking code. I think just by requiring enclosure in curly braces "fun({gun();})" would make this feature quite less appealing and used. This syntactic feature is very pleasing - by whichever feature at definition side it is achieved (macro/expression type), it should stay here.

reddit thought it's a major loss of a guarantee.

 2. Writing - On the function definition side, I don't see much
 difference in *writing* "lazy int dg" or "int delegate () dg". The
 functions that take lazy parameter are not written daily - their
 usage is much more frequent (enforce, logging).
 

that's perceived as an int with some sort of storage class. Then a putative user would expect auto y = x; to define another int. In fact it's unclear whether y should be an int or a delegate. The presence of the delegate type clarifies what's going on. I could come up with several other examples that reveal "lazy int x" to be a complete crock.

as forcing of computation. Properties or real lazy evaluation could solved this maybe, but ... Yank it. Although the possibility to specify if delegate accept expression or just function pointer really should be retained. How about: void fun ( expr int delegate () dg) {...} instead of lazy. While normal delegate parameters will work as they are now.
 One problem I see currently with "lazy" that by specification it can
 be evaluated zero or more times. For me, "lazy" means zero or one
 time (compiler should handle this internally). This is not
 particularly important, because it is probably not so hard for
 programmer to write correct function - evaluation as many times as
 needed (which I think is usually 0 or 1 anyway). Just, the name
 "lazy" does not seems correct to me.
 

delegate parameters to accept expressions. That approach has its own problems. Consider: void fun(int delegate() dg) { ... } int delegate() gun() { ... } fun(gun()); In this case gun does get evaluated :o).

expr above does not have this prob.
 
 Andrei
 

I think expr delegate is it. Another possible ussage could be: expr int delegate () x = 1 + 1; expr auto y = 1 + 1; // y has type "int delegate ()"
Dec 08 2009