digitalmars.D - lazy redux
- Andrei Alexandrescu (3/3) Dec 05 2009 Should we sack lazy? I'd like it to have a reasonable replacement. Ideas...
- Tim Matthews (19/23) Dec 05 2009 According to the doc page:
- Andrei Alexandrescu (29/60) Dec 06 2009 Well the story is, I suggested something very different, namely
- Michel Fortin (25/67) Dec 06 2009 That's what I'd do too.
- Michal Minich (7/11) Dec 07 2009 I like the idea that of restricting what is passed into function;
- =?UTF-8?B?UGVsbGUgTcOlbnNzb24=?= (7/11) Dec 06 2009 I think they are broken as they are not really lazy, but just convenient...
- bearophile (67/78) Dec 06 2009 I am not yet able to suggest you a replacement.
- ZY.Zhou (8/10) Dec 06 2009 Once I had a bug, it's like:
- Michal Minich (29/34) Dec 07 2009 there are 3 sides from which to look at lazy function parameter.
- bearophile (6/9) Dec 07 2009 It's a special case, and special cases help to kill languages. It's not ...
- Michal Minich (27/39) Dec 07 2009 Yes, it works well in C#, and it is one of the best extension of this la...
- Denis Koroskin (5/42) Dec 07 2009 It already works, just try it (but don't forget to put a semicolon at th...
- Michal Minich (16/34) Dec 07 2009 it works with two differences:
- Denis Koroskin (12/44) Dec 07 2009 It complicates semantic pass quite significantly: you can't semantically...
- klickverbot (5/11) Dec 07 2009 If someone should step forward to implement this: I would really like to...
- Nick Sabalausky (29/44) Dec 07 2009 I've had an idea for a more expanded version of what you propose:
- retard (3/15) Dec 07 2009 I think the counterargument was: semicolons help spotting bugs everywher...
- Leandro Lucarella (39/76) Dec 07 2009 It doesn't do implicit returning either:
- Andrei Alexandrescu (5/82) Dec 07 2009 A nit - it's the second argument of enforce that must be lazy, e.g.
- Leandro Lucarella (22/58) Dec 07 2009 I think this syntax is pretty neat, because it's make the caller
- retard (9/61) Dec 07 2009 You surely understand that Walter doesn't have enough time to change thi...
- =?UTF-8?B?UGVsbGUgTcOlbnNzb24=?= (3/71) Dec 07 2009 Actually, it can, and will, infer the types for (a, b) { ... }
- Lutger (6/32) Dec 07 2009 why not ? (a, b) { return a + b; } already works for template alias
- retard (3/38) Dec 07 2009 Nope, but it isn't done in other parts of the compiler, either, for that...
- Andrei Alexandrescu (19/45) Dec 07 2009 I think the same. But I seem to recall that at least one person on
- Michal Minich (15/72) Dec 08 2009 You are right, I didn't noticed that. I always perceived "()" in "foo()"...
Should we sack lazy? I'd like it to have a reasonable replacement. Ideas are welcome! Andrei
Dec 05 2009
Andrei Alexandrescu wrote:Should we sack lazy? I'd like it to have a reasonable replacement. Ideas are welcome! AndreiAccording 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
Tim Matthews wrote:Andrei Alexandrescu wrote: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.Should we sack lazy? I'd like it to have a reasonable replacement. Ideas are welcome! AndreiAccording 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[] expI 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
On 2009-12-06 10:44:17 -0500, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> said:Tim Matthews wrote: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/Andrei Alexandrescu wrote: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.Should we sack lazy? I'd like it to have a reasonable replacement. Ideas are welcome! AndreiAccording 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? :)
Dec 06 2009
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
Andrei Alexandrescu wrote:Should we sack lazy? I'd like it to have a reasonable replacement. Ideas are welcome! AndreiI think they are broken as they are not really lazy, but just convenient 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
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:40 or 4[1, 2][] or [1,2] or [3](5,)(5,) or [1,2]...x = 1 def foo(): global x; x += 1[5][5] or foo()1x2 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[] or foo() x
Dec 06 2009
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
Hello Andrei,Should we sack lazy? I'd like it to have a reasonable replacement. Ideas are welcome! Andreithere 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
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. Evaluations lazy arguments only 0 or 1 times sounds like a nice idea. Bye, bearophile
Dec 07 2009
Hello bearophile,Michal Minich:(only adding generics was better). // 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.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. Evaluations lazy arguments only 0 or 1 times sounds like a nice idea. Bye, bearophile
Dec 07 2009
On Mon, 07 Dec 2009 16:17:10 +0300, Michal Minich <michal.minich gmail.com> wrote:Hello bearophile,It already works, just try it (but don't forget to put a semicolon at the end).Michal Minich:language (only adding generics was better). // 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 } );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. Evaluations lazy arguments only 0 or 1 times sounds like a nice idea. Bye, bearophile2. 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
Hello Denis,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...?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
On Mon, 07 Dec 2009 16:51:29 +0300, Michal Minich <michal.minich gmail.com> wrote:Hello Denis,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?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...?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
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
"klickverbot" <klickverbot gmail.com> wrote in message news:hfj6eb$kss$1 digitalmars.com...Denis Koroskin wrote: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.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
Mon, 07 Dec 2009 16:19:37 +0100, klickverbot wrote:Denis Koroskin wrote:I think the counterargument was: semicolons help spotting bugs everywhere since the parser immediately sees this as syntax error.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
Michal Minich, el 7 de diciembre a las 13:51 me escribiste:Hello Denis,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 uglyit 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); } ); }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
Leandro Lucarella wrote:Michal Minich, el 7 de diciembre a las 13:51 me escribiste: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. AndreiHello Denis,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.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); } ); }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
Michal Minich, el 7 de diciembre a las 13:17 me escribiste:Hello bearophile,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)Michal Minich:this language (only adding generics was better). // 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 } );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. Evaluations lazy arguments only 0 or 1 times sounds like a nice idea. Bye, bearophile
Dec 07 2009
Mon, 07 Dec 2009 13:17:10 +0000, Michal Minich wrote:Hello bearophile,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 needMichal Minich:language (only adding generics was better). // 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 );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. Evaluations lazy arguments only 0 or 1 times sounds like a nice idea. Bye, bearophileFoo ( (auto a, auto b) => a + b );orFoo ( [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
retard wrote:Mon, 07 Dec 2009 13:17:10 +0000, Michal Minich wrote:Actually, it can, and will, infer the types for (a, b) { ... } It not doing so right now is on the bugzilla.Hello bearophile,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 needMichal Minich:language (only adding generics was better). // 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 );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. Evaluations lazy arguments only 0 or 1 times sounds like a nice idea. Bye, bearophileFoo ( (auto a, auto b) => a + b );orFoo ( [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
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 needwhy not ? (a, b) { return a + b; } already works for template alias parameters.Foo ( (auto a, auto b) => a + b );orI don't think inlining is done in the backend.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.
Dec 07 2009
Tue, 08 Dec 2009 01:02:04 +0100, Lutger wrote:retard wrote: ...Nope, but it isn't done in other parts of the compiler, either, for that matter.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 needwhy not ? (a, b) { return a + b; } already works for template alias parameters.Foo ( (auto a, auto b) => a + b );orI don't think inlining is done in the backend.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.
Dec 07 2009
Michal Minich wrote:Hello Andrei,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.Should we sack lazy? I'd like it to have a reasonable replacement. Ideas are welcome! Andreithere 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).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
Hello Andrei,Michal Minich wrote:what he would think of Lisp then :-)Hello Andrei,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.Should we sack lazy? I'd like it to have a reasonable replacement. Ideas are welcome! Andreithere 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.You are right, I didn't noticed that. I always perceived "()" in "foo()" 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.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.expr above does not have this prob.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).AndreiI 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