digitalmars.D - Full closures for D
- Walter Bright (464/464) Nov 02 2007 D 2.007 brings full closures to D. I was tired of D being denigrated for...
- Denton Cockburn (2/5) Nov 02 2007 Nicely Done. Lisp is looking less special everyday.
- KlausO (6/13) Nov 05 2007 Awesome. Seems it's time to update
- Bruce Adams (5/21) Nov 05 2007 Someone with more a functional programming background may be able
- 0ffh (4/9) Nov 05 2007 Yup, IIRC the poor strictly functional guys needs them for I/O, but as
- David B. Held (6/15) Nov 05 2007 You might like to use monads in a "pure multithreading" environment
- Lars Noschinski (3/11) Nov 10 2007 Monads have other applications, too. You can use them for error handling
- 0ffh (3/6) Nov 05 2007 And it's time to distuinguish D1 vs. D2...
- Denton Cockburn (3/11) Nov 05 2007 Is the goal to be able to many of these things as possible, leaving no o...
- Nathan Reed (8/10) Nov 02 2007 Very cool. I have to admit I didn't think this would make it into the
- Xinok (21/34) Nov 02 2007 It seems that variables that are used by a nested function are allocated...
- Jarrett Billingsley (16/36) Nov 02 2007 Hm. Don't have a D2 compiler with me -- could you run the following and...
- Extrawurst (6/50) Nov 02 2007 Prints:
-
Jarrett Billingsley
(9/9)
Nov 02 2007
"Extrawurst"
wrote in message - 0ffh (3/5) Nov 02 2007 Sometimes I think you must really enjoy surprising everyone.... =)
- Walter Bright (5/10) Nov 02 2007 Yes, and just as I was about to upload this update, the digitalmars site...
- Walter Bright (18/18) Nov 02 2007 How do D closures work?
- Jarrett Billingsley (6/13) Nov 02 2007 Indeed.. of course there might not be any other way to really check it..
- Craig Black (2/4) Nov 02 2007 You could let the programmer specify explicitly whether or not to make a...
- Reiner Pope (15/22) Nov 03 2007 I would have thought 'scope' would have some role in this. It currently
- Reiner Pope (12/39) Nov 03 2007 Oh, and
- David Medlock (7/14) Nov 03 2007 First, great job Walter! I have been just lurking for some time but
- David Medlock (2/19) Nov 03 2007 Oops scratch that, you still need to determine *when* to do it..
- Martin d Anjou (41/45) Nov 06 2007 A programming language called Vera uses the "shadow" keyword to acheive ...
- Bruce Adams (3/58) Nov 06 2007 I see the analogy but this is actually about whether a variable should b...
- =?ISO-8859-1?Q?Julio_C=E9sar_Carrascal_Urquijo?= (7/9) Nov 02 2007 Yay!! I still can believe it. It's certainly the feature that I wanted
- Steve Teale (2/5) Nov 03 2007 OK, I get the full closure thing, but can someone come up with a typical...
- bearophile (5/6) Nov 03 2007 You are right, this may help:
- Mikola Lysenko (12/12) Nov 05 2007 I'm excited. For the first time it is now possible to do futures in 7 l...
- Craig Black (9/22) Nov 05 2007 Very cool stuff! Did you test this code to see if it actually works?
- BCS (15/51) Nov 05 2007 string mixins are fun:
- Jari-Matti =?ISO-8859-1?Q?M=E4kel=E4?= (25/43) Nov 05 2007 Yea, bummer :| I would have expected this to work:
- Daniel Keep (13/65) Nov 05 2007 It's not a bug that future can't see a or b. They're called "visibility
- BCS (2/7) Nov 05 2007 It would be nice to have a way to say "reach around these rules right no...
- Daniel Keep (3/11) Nov 05 2007 I believe this is what AST macros will be for. :)
- Jari-Matti =?ISO-8859-1?Q?M=E4kel=E4?= (10/78) Nov 06 2007 You probably didn't notice but I used alias parameters there. So it shou...
- Kevin Bealer (27/27) Nov 05 2007 My futurism library has support for the kind of thing you are describing...
- torhu (10/28) Nov 05 2007 I tested your code and got this:
- Bruce Adams (3/14) Nov 05 2007 Since they're on the language comparison feature chat it implies (to me ...
- Nathan Reed (15/18) Nov 05 2007 Here's a good introduction to monads in the context of Haskell:
- Bruce Adams (4/26) Nov 06 2007 But could there be any benefit to a 'non functional' language like D.
- Mikola Lysenko (4/13) Nov 06 2007 I'm inclined to agree with Bruce. My opinion is that for C-style langua...
- Mikola Lysenko (4/15) Nov 06 2007 Well, I suppose I should have prefixed that code with the statement that...
- Witold Baryluk (34/37) Nov 06 2007 Simpler curring?
- outersky (8/56) Nov 06 2007 Maybe the two inner variables can be omitted as :
- Russell Lewis (5/65) Nov 06 2007 Or even:
- outersky (4/71) Nov 06 2007 Great!
- Witold Baryluk (38/43) Nov 06 2007 Yes i was thinking about it, but didn't know if it should work.
- Bill Baxter (9/62) Nov 06 2007 Pedantic's note: currying an argument actually means creating a function...
- Bill Baxter (3/59) Nov 06 2007 Pedant's note: a pedantic person is called a "pedant".
- David B. Held (60/60) Nov 06 2007 Ok, everyone knows that closures are cool (and those that don't know
- Nathan Reed (10/17) Nov 06 2007 I think you're asking for too much intelligence form the compiler here.
- David B. Held (17/33) Nov 06 2007 In order to analyze this properly, we need to look at my (working)
- Nathan Reed (11/32) Nov 07 2007 Of course, in this case it's "easy enough to infer". However, function
- Simas (4/36) Nov 07 2007 On "http://www.digitalmars.com/d/" D is defined as "Its focus is on comb...
- Bill Baxter (11/50) Nov 07 2007 In python the lambdas still requires arguments to be named, and use of
- David B. Held (5/18) Nov 07 2007 I agree that anonymous lambda args are probably going a step too far.
- Nathan Reed (32/42) Nov 07 2007 Not in all cases.
- Bill Baxter (14/63) Nov 07 2007 More sophisticated type inference is certainly possible with a strong
- Nathan Reed (15/26) Nov 07 2007 I'm familiar with ML; in fact, I use it extensively. I never claimed
- Bill Baxter (28/53) Nov 07 2007 Ok, you probably know better than me, then. You just seemed to be
- David B. Held (15/31) Nov 07 2007 Guess what? Deducing argument types is no harder than choosing the
- Janice Caron (17/39) Nov 07 2007 Looks completely unreadable to me. It just doesn't make sense to my brai...
- David B. Held (32/83) Nov 07 2007 What if I said D is BASIC-like? Is that a good thing? The main failure...
- Robert Fraser (6/11) Nov 07 2007 At first blush, it sounded like a really good idea to me (I use Perl qui...
- Simas (2/10) Nov 07 2007 Thx for template. Really nice. Vote++
- Simas (4/25) Nov 07 2007 I think this is a helpful feature.
- Frank Benoit (11/11) Nov 11 2007 the lazy keyword was introduced with a hint to the logging use case.
D 2.007 brings full closures to D. I was tired of D being denigrated for not having "real" closures. Here are some test cases for it, so you can see what it does: ------------------------------------------- struct S { int a,b,c,d; } alias int delegate() dg_t; alias int delegate(int) dg1_t; void fill() { int[100] x; } /************************************/ dg_t foo() { int x = 7; int bar() { return x + 3; } return &bar; } void test1() { dg_t dg = foo(); fill(); printf("bar = %d\n", dg()); assert(dg() == 10); } /************************************/ dg_t foo2() { dg_t abc() { int x = 7; int bar() { return x + 3; } return &bar; } return abc(); } void test2() { dg_t dg = foo2(); fill(); printf("bar = %d\n", dg()); assert(dg() == 10); } /************************************/ dg_t foo3() { dg_t abc(int x) { int bar() { return x + 3; } return &bar; } return abc(7); } void test3() { dg_t dg = foo3(); fill(); printf("bar = %d\n", dg()); assert(dg() == 10); } /************************************/ dg_t foo4() { S s; s = S(4,5,6,7); dg_t abc(S t) { int bar() { return t.d + 3; } return &bar; } return abc(s); } void test4() { dg_t dg = foo4(); fill(); printf("bar = %d\n", dg()); assert(dg() == 10); } /************************************/ void test5() { int x = 7; dg_t abc(ref int y) { int bar() { y += 4; return y + 3; } return &bar; } dg_t dg = abc(x); fill(); assert(x == 7); auto i = dg(); assert(x == 11); assert(i == 14); x = 8; i = dg(); assert(x == 12); assert(i == 15); } /************************************/ void test6() { int x = 7; dg_t abc(out int y) { int bar() { y += 4; return y + 3; } return &bar; } dg_t dg = abc(x); fill(); assert(x == 0); auto i = dg(); assert(x == 4); assert(i == 7); x = 8; i = dg(); assert(x == 12); assert(i == 15); } /************************************/ void test7() { int[3] a = [10,11,12]; dg_t abc(int[3] y) { int bar() { y[2] += 4; return y[2] + 3; } return &bar; } dg_t dg = abc(a); fill(); assert(a[2] == 12); auto i = dg(); assert(a[2] == 16); assert(i == 19); } /************************************/ void test8() { S s = S(7,8,9,10); dg_t abc(ref S t) { int bar() { t.d += 4; return t.c + 3; } return &bar; } dg_t dg = abc(s); fill(); assert(s.d == 10); auto i = dg(); assert(s.d == 14); assert(i == 12); } /************************************/ S foo9(out dg_t dg) { S s1 = S(7,8,9,10); dg_t abc() { int bar() { s1.d += 4; return s1.c + 3; } return &bar; } dg = abc(); return s1; } void test9() { dg_t dg; S s = foo9(dg); fill(); assert(s.a == 7); assert(s.b == 8); assert(s.c == 9); assert(s.d == 10); auto i = dg(); assert(s.d == 10); assert(i == 12); } /************************************/ dg_t foo10() { dg_t abc() { int x = 7; int bar() { int def() { return x + 3; } return def(); } return &bar; } return abc(); } void test10() { dg_t dg = foo10(); fill(); printf("bar = %d\n", dg()); assert(dg() == 10); } /************************************/ dg_t foo11() { int x = 7; class T { int bar() { return x + 3; } } T t = new T; return &t.bar; } void test11() { dg_t dg = foo11(); fill(); printf("bar = %d\n", dg()); assert(dg() == 10); } /************************************/ dg_t foo12() { int x = 7; class T { int bar() { return x + 3; } int xyz() { return bar(); } } T t = new T; return &t.xyz; } void test12() { dg_t dg = foo12(); fill(); printf("bar = %d\n", dg()); assert(dg() == 10); } /************************************/ dg_t foo13() { int x = 7; class T { int xyz() { int bar() { return x + 3; } return bar(); } } T t = new T; return &t.xyz; } void test13() { dg_t dg = foo13(); fill(); printf("bar = %d\n", dg()); assert(dg() == 10); } /************************************/ dg_t foo14() { class T { int xyz() { int x = 7; int bar() { return x + 3; } return bar(); } } T t = new T; return &t.xyz; } void test14() { dg_t dg = foo14(); fill(); printf("bar = %d\n", dg()); assert(dg() == 10); } /************************************/ dg_t foo15() { class T { int x = 7; int xyz() { int bar() { return x + 3; } return bar(); } } T t = new T; return &t.xyz; } void test15() { dg_t dg = foo15(); fill(); printf("bar = %d\n", dg()); assert(dg() == 10); } /************************************/ dg_t foo16() { int a = 5; class T { int x = 7; int xyz() { int y = 8; int bar() { return a + x + y + 3; } return bar(); } } T t = new T; return &t.xyz; } void test16() { dg_t dg = foo16(); fill(); printf("bar = %d\n", dg()); assert(dg() == 23); } /************************************/ dg_t foo17() { int a = 5; class T { int x = 7; dg_t xyz() { int y = 8; int bar() { return a + x + y + 3; } return &bar; } } T t = new T; return t.xyz(); } void test17() { dg_t dg = foo17(); fill(); printf("bar = %d\n", dg()); assert(dg() == 23); } /************************************/ dg_t dg18; void bar18() { int a = 7; int foo() { return a + 3; } dg18 = &foo; int i = dg18(); assert(i == 10); } void test18() { bar18(); fill(); int i = dg18(); assert(i == 10); } /************************************/ void abc19(void delegate() dg) { dg(); dg(); dg(); } struct S19 { static S19 call(int v) { S19 result; result.v = v; void nest() { result.v += 1; } abc19(&nest); return result; } int a; int v; int x,y,z; } int foo19() { auto s = S19.call(5); return s.v; } void test19() { int i = foo19(); printf("%d\n", i); assert(i == 8); } /************************************/ int main() { test1(); test2(); test3(); test4(); test5(); test6(); test7(); test8(); test9(); test10(); test11(); test12(); test13(); test14(); test15(); test16(); test17(); test18(); test19(); printf("Success\n"); return 0; }
Nov 02 2007
On Fri, 02 Nov 2007 14:03:59 -0700, Walter Bright wrote:D 2.007 brings full closures to D. I was tired of D being denigrated for not having "real" closures.Nicely Done. Lisp is looking less special everyday.
Nov 02 2007
Denton Cockburn schrieb:On Fri, 02 Nov 2007 14:03:59 -0700, Walter Bright wrote:Awesome. Seems it's time to update http://www.prowiki.org/wiki4d/wiki.cgi?LanguagesVersusD and the next big thing may be first class continuations which has an interesting application in GUI code, see http://citeseer.ist.psu.edu/fuchs95escaping.htmlD 2.007 brings full closures to D. I was tired of D being denigrated for not having "real" closures.Nicely Done. Lisp is looking less special everyday.
Nov 05 2007
KlausO Wrote:Denton Cockburn schrieb:Someone with more a functional programming background may be able to enlighten us but doesn't the existence of proper closures allow us to implement monads as a library now? Though I can't quite see in what situation we would want to use them.On Fri, 02 Nov 2007 14:03:59 -0700, Walter Bright wrote:Awesome. Seems it's time to update http://www.prowiki.org/wiki4d/wiki.cgi?LanguagesVersusDD 2.007 brings full closures to D. I was tired of D being denigrated for not having "real" closures.Nicely Done. Lisp is looking less special everyday.and the next big thing may be first class continuations which has an interesting application in GUI code, see http://citeseer.ist.psu.edu/fuchs95escaping.htmlIs this not possible via a library too, even without closures? Excuse me if I'm more than half asleep today.
Nov 05 2007
Bruce Adams wrote:KlausO Wrote: Someone with more a functional programming background may be able to enlighten us but doesn't the existence of proper closures allow us to implement monads as a library now? Though I can't quite see in what situation we would want to use them.Yup, IIRC the poor strictly functional guys needs them for I/O, but as we're imperative anyway I don't see any applications for monads either. Regards, Frank
Nov 05 2007
0ffh wrote:Bruce Adams wrote:You might like to use monads in a "pure multithreading" environment where the threads are written in pure functional style and are thus safely lock-free. A monad essentially allows you to defer any necessary locking to a convenient place and time. DaveKlausO Wrote: Someone with more a functional programming background may be able to enlighten us but doesn't the existence of proper closures allow us to implement monads as a library now? Though I can't quite see in what situation we would want to use them.Yup, IIRC the poor strictly functional guys needs them for I/O, but as we're imperative anyway I don't see any applications for monads either.
Nov 05 2007
* 0ffh <spam frankhirsch.net> [07-11-05 22:41]:Bruce Adams wrote:Monads have other applications, too. You can use them for error handling (in a similar way NaN works in floating point operations) for example.KlausO Wrote: Someone with more a functional programming background may be able to enlighten us but doesn't the existence of proper closures allow us to implement monads as a library now? Though I can't quite see in what situation we would want to use them.Yup, IIRC the poor strictly functional guys needs them for I/O, but as we're imperative anyway I don't see any applications for monads either.
Nov 10 2007
KlausO wrote:Awesome. Seems it's time to update http://www.prowiki.org/wiki4d/wiki.cgi?LanguagesVersusDAnd it's time to distuinguish D1 vs. D2... Regards, Frank
Nov 05 2007
On Mon, 05 Nov 2007 22:48:20 +0100, 0ffh wrote:KlausO wrote:Is the goal to be able to many of these things as possible, leaving no one with a reason to use another language? :)Awesome. Seems it's time to update http://www.prowiki.org/wiki4d/wiki.cgi?LanguagesVersusDAnd it's time to distuinguish D1 vs. D2... Regards, Frank
Nov 05 2007
Walter Bright wrote:D 2.007 brings full closures to D. I was tired of D being denigrated for not having "real" closures.Very cool. I have to admit I didn't think this would make it into the language. :) I'm curious about how they work. I'd guess you dup the stack frame onto the heap when a function returns a delegate, and update the delegate's context pointer? Thanks, Nathan Reed
Nov 02 2007
It seems that variables that are used by a nested function are allocated on the heap rather than the stack. This was my test code: void delegate() foo(){ int v = 60; int c = 35; writefln(&c); writefln(&v); return {writefln(&v);}; } void main(){ void delegate() one = foo(); one(); } Prints: 12FF18 8B2FF4 8B2FF4 The address of 'v' doesn't change. What you do notice is the great difference of the memory addresses between int v and int c, which suggests that 'v' is allocated on the heap rather than the stack. Nathan Reed wrote:Walter Bright wrote:D 2.007 brings full closures to D. I was tired of D being denigrated for not having "real" closures.Very cool. I have to admit I didn't think this would make it into the language. :) I'm curious about how they work. I'd guess you dup the stack frame onto the heap when a function returns a delegate, and update the delegate's context pointer? Thanks, Nathan Reed
Nov 02 2007
"Xinok" <xnknet gmail.com> wrote in message news:fgg9q8$jlr$1 digitalmars.com...It seems that variables that are used by a nested function are allocated on the heap rather than the stack. This was my test code: void delegate() foo(){ int v = 60; int c = 35; writefln(&c); writefln(&v); return {writefln(&v);}; } void main(){ void delegate() one = foo(); one(); } Prints: 12FF18 8B2FF4 8B2FF4 The address of 'v' doesn't change. What you do notice is the great difference of the memory addresses between int v and int c, which suggests that 'v' is allocated on the heap rather than the stack.Hm. Don't have a D2 compiler with me -- could you run the following and tell me what it prints? void main() { int v, c; void foo() { writefln(&c); } writefln(&v); writefln(&c); } I'm wondering if the compiler is smart enough not to allocate variables on the heap if it doesn't have to. (I'm not returning foo.)
Nov 02 2007
Jarrett Billingsley schrieb:"Xinok" <xnknet gmail.com> wrote in message news:fgg9q8$jlr$1 digitalmars.com...Prints: 13FF28 13FF2C ...whatever that means... ~ExtrawurstIt seems that variables that are used by a nested function are allocated on the heap rather than the stack. This was my test code: void delegate() foo(){ int v = 60; int c = 35; writefln(&c); writefln(&v); return {writefln(&v);}; } void main(){ void delegate() one = foo(); one(); } Prints: 12FF18 8B2FF4 8B2FF4 The address of 'v' doesn't change. What you do notice is the great difference of the memory addresses between int v and int c, which suggests that 'v' is allocated on the heap rather than the stack.Hm. Don't have a D2 compiler with me -- could you run the following and tell me what it prints? void main() { int v, c; void foo() { writefln(&c); } writefln(&v); writefln(&c); } I'm wondering if the compiler is smart enough not to allocate variables on the heap if it doesn't have to. (I'm not returning foo.)
Nov 02 2007
"Extrawurst" <spam extrawurst.org> wrote in message Prints: 13FF28 13FF2C ...whatever that means... ~Extrawurst AGH your rich-text post is evul. But yay, they're both on the stack, meaning the compiler at least attempts to be smart about it :D No unnecessary heap allocations.
Nov 02 2007
Walter Bright wrote:D 2.007 brings full closures to D. I was tired of D being denigrated for not having "real" closures.Sometimes I think you must really enjoy surprising everyone.... =) Regards, Frank
Nov 02 2007
0ffh wrote:Walter Bright wrote:Yes, and just as I was about to upload this update, the digitalmars site went down. So I sat around chewing nails for a day till it came back up <g>. BTW, the problem with the site was the ISP connection went down. The server was fine.D 2.007 brings full closures to D. I was tired of D being denigrated for not having "real" closures.Sometimes I think you must really enjoy surprising everyone.... =)
Nov 02 2007
How do D closures work? 1) The compiler makes a distinction between a nested function that 'escapes' the scope, and one that does not. It uses a very simple, but conservative, heuristic - did someone take the address of the function? If yes, it assumes the function escapes. 2) The compiler logs all local variables that are referenced by any nested function. 3) If any of those variables are referenced by an escaping nested function, or a nested function that encloses an escaping function, then upon function entry all the referenced variables are allocated on a heap-allocated chunk of memory rather than on the stack. Any referenced parameters are copied into that chunk. That chunk is linked into the nested context frames rather than the stack frame. 4) Variables not referenced by nested functions are still allocated on the stack. Note you can see this at work by running obj2asm on the test code I posted. It's not an optimal solution because the "does the function escape" heuristic captures too many functions.
Nov 02 2007
"Walter Bright" <newshound1 digitalmars.com> wrote in message news:fggfqq$sh5$1 digitalmars.com...How do D closures work? 1) The compiler makes a distinction between a nested function that 'escapes' the scope, and one that does not. It uses a very simple, but conservative, heuristic - did someone take the address of the function? If yes, it assumes the function escapes. It's not an optimal solution because the "does the function escape" heuristic captures too many functions.Indeed.. of course there might not be any other way to really check it.. forward reference nested functions, and instead have to put their addresses in local delegates. Get rid of that, and there's one problem gone..
Nov 02 2007
It's not an optimal solution because the "does the function escape" heuristic captures too many functions.You could let the programmer specify explicitly whether or not to make a function a closure or not, maybe with a key word or something.
Nov 02 2007
Craig Black wrote:I would have thought 'scope' would have some role in this. It currently means 'allocate on the stack', and (is this implemented yet?) when used as a storage class for function parameters, it is supposed to mean, "I won't escape this variable". You could extend the "does the function escape" heuristic to say, "the function escapes if it is assigned to a non-scope variable, or is used as a non-scope argument." This would behave correctly for what I believe is one of the major uses of functions: sort/search/etc with a predicate. These things almost never escape the predicate, so they can be written: T find(T)(ref T[] array, scope bool delegate(T t) matches) { ... } -- ReinerIt's not an optimal solution because the "does the function escape" heuristic captures too many functions.You could let the programmer specify explicitly whether or not to make a function a closure or not, maybe with a key word or something.
Nov 03 2007
Reiner Pope wrote:Craig Black wrote:Oh, and void foo() { int n = ...; ... scope dg = { return n+2; }; ... } gives you a way to tell the compiler, "dg doesn't need to be a lexical closure". -- ReinerI would have thought 'scope' would have some role in this. It currently means 'allocate on the stack', and (is this implemented yet?) when used as a storage class for function parameters, it is supposed to mean, "I won't escape this variable". You could extend the "does the function escape" heuristic to say, "the function escapes if it is assigned to a non-scope variable, or is used as a non-scope argument." This would behave correctly for what I believe is one of the major uses of functions: sort/search/etc with a predicate. These things almost never escape the predicate, so they can be written: T find(T)(ref T[] array, scope bool delegate(T t) matches) { ... } -- ReinerIt's not an optimal solution because the "does the function escape" heuristic captures too many functions.You could let the programmer specify explicitly whether or not to make a function a closure or not, maybe with a key word or something.
Nov 03 2007
Craig Black wrote:First, great job Walter! I have been just lurking for some time but you are polishing and improving things nicely. Long live D! As far as closures go, would it have been possible to just copy the whole stack frame to the heap and update the references? GC would eventually reclaim it, no? -DavidIt's not an optimal solution because the "does the function escape" heuristic captures too many functions.You could let the programmer specify explicitly whether or not to make a function a closure or not, maybe with a key word or something.
Nov 03 2007
David Medlock wrote:Craig Black wrote:Oops scratch that, you still need to determine *when* to do it..First, great job Walter! I have been just lurking for some time but you are polishing and improving things nicely. Long live D! As far as closures go, would it have been possible to just copy the whole stack frame to the heap and update the references? GC would eventually reclaim it, no? -DavidIt's not an optimal solution because the "does the function escape" heuristic captures too many functions.You could let the programmer specify explicitly whether or not to make a function a closure or not, maybe with a key word or something.
Nov 03 2007
A programming language called Vera uses the "shadow" keyword to acheive a somewhat related effect. From http://www.asic-world.com/vera/concurrency_control1.html: program shadow_variable { spawn_process() ; delay(100) ; spawn_process_with_shawdow(); delay(100) ; } task print(integer i) { printf(" n = %0d\n", i); } task spawn_process () { integer n; printf("In spawn_process\n"); for(n=0; n<3 ; n++) { fork print(n) ; join none } } task spawn_process_with_shawdow () { shadow integer n; printf("In spawn_process_with_shadow_variable\n"); for(n=0; n<3 ; n++) { fork print(n) ; join none } } Executing this code gives: In spawn_process n = 3 n = 3 n = 3 In spawn_process_with_shadow_variable n = 0 n = 1 n = 2 I am not a language expert, but this seemed related. MartinIt's not an optimal solution because the "does the function escape" heuristic captures too many functions.You could let the programmer specify explicitly whether or not to make a function a closure or not, maybe with a key word or something.
Nov 06 2007
Martin d Anjou Wrote:I see the analogy but this is actually about whether a variable should be shared between 2 threads / processes. I don't think there is a native D equivalent but "shared" has been proposed several times. This would be more like "export".A programming language called Vera uses the "shadow" keyword to acheive a somewhat related effect. From http://www.asic-world.com/vera/concurrency_control1.html: program shadow_variable { spawn_process() ; delay(100) ; spawn_process_with_shawdow(); delay(100) ; } task print(integer i) { printf(" n = %0d\n", i); } task spawn_process () { integer n; printf("In spawn_process\n"); for(n=0; n<3 ; n++) { fork print(n) ; join none } } task spawn_process_with_shawdow () { shadow integer n; printf("In spawn_process_with_shadow_variable\n"); for(n=0; n<3 ; n++) { fork print(n) ; join none } } Executing this code gives: In spawn_process n = 3 n = 3 n = 3 In spawn_process_with_shadow_variable n = 0 n = 1 n = 2 I am not a language expert, but this seemed related. MartinIt's not an optimal solution because the "does the function escape" heuristic captures too many functions.You could let the programmer specify explicitly whether or not to make a function a closure or not, maybe with a key word or something.
Nov 06 2007
Walter Bright wrote:D 2.007 brings full closures to D. I was tired of D being denigrated for not having "real" closures.Yay!! I still can believe it. It's certainly the feature that I wanted to see in the language the most. Thank you Walter. -- Julio César Carrascal Urquijo http://jcesar.artelogico.com/
Nov 02 2007
Walter Bright Wrote:D 2.007 brings full closures to D. I was tired of D being denigrated for not having "real" closures. ...OK, I get the full closure thing, but can someone come up with a typical example of its use please?
Nov 03 2007
Steve Teale:OK, I get the full closure thing, but can someone come up with a typical example of its use please?You are right, this may help: http://en.wikipedia.org/wiki/Closure_%28computer_science%29 Bye, bearophile
Nov 03 2007
I'm excited. For the first time it is now possible to do futures in 7 lines of code: T delegate() future(T)(lazy T expr) { T res; auto t = new Thread({res = expr();}); t.start; return { t.wait; return res; } } Which could then be used as follows: auto hard_result = future( hard_expression ); // Do stuff ... use_result(hard_result());
Nov 05 2007
"Mikola Lysenko" <mclysenk mtu.edu> wrote in message news:fgns3v$1b13$1 digitalmars.com...I'm excited. For the first time it is now possible to do futures in 7 lines of code: T delegate() future(T)(lazy T expr) { T res; auto t = new Thread({res = expr();}); t.start; return { t.wait; return res; } } Which could then be used as follows: auto hard_result = future( hard_expression ); // Do stuff ... use_result(hard_result());Very cool stuff! Did you test this code to see if it actually works? A little off topic, but I was looking at your code and wondering if "lazy" always means that the expression becomes a delegate? When a template is used, the compiler could embed the expression directly rather than creating a delegate. Since D is big on compile-time optimization, I was wondering if it does this. -Craig
Nov 05 2007
Craig Black wrote:"Mikola Lysenko" <mclysenk mtu.edu> wrote in message news:fgns3v$1b13$1 digitalmars.com...string mixins are fun: typeof(mixin(expr)) delegate() future(char[] expr)() { typeof(mixin(expr)) res; auto t = new Thread({res = mixin(expr);}); t.start; return { t.wait; return res; } } auto hard_result = future!("hard_expression"); // Do stuff ... use_result(hard_result()); NOTE: this runs into the issue that there is no way to get a template to play with the local variables of a function. If there were a way to do this I can think of a number of very cool things that could be done.I'm excited. For the first time it is now possible to do futures in 7 lines of code: T delegate() future(T)(lazy T expr) { T res; auto t = new Thread({res = expr();}); t.start; return { t.wait; return res; } } Which could then be used as follows: auto hard_result = future( hard_expression ); // Do stuff ... use_result(hard_result());Very cool stuff! Did you test this code to see if it actually works? A little off topic, but I was looking at your code and wondering if "lazy" always means that the expression becomes a delegate? When a template is used, the compiler could embed the expression directly rather than creating a delegate. Since D is big on compile-time optimization, I was wondering if it does this. -Craig
Nov 05 2007
BCS wrote:string mixins are fun: typeof(mixin(expr)) delegate() future(char[] expr)() { typeof(mixin(expr)) res; auto t = new Thread({res = mixin(expr);}); t.start; return { t.wait; return res; } } auto hard_result = future!("hard_expression"); // Do stuff ... use_result(hard_result()); NOTE: this runs into the issue that there is no way to get a template to play with the local variables of a function. If there were a way to do this I can think of a number of very cool things that could be done.Yea, bummer :| I would have expected this to work: char[] bar(char[] foo) { char[] tmp; tmp ~= "alias "; foreach(p; foo) if (p == ',') tmp ~= ", alias "; else tmp ~= p; return tmp; } template future(char[] aliases, char[] expr) { mixin(typeof(mixin(expr)).stringof ~ ` delegate() future(` ~ bar(aliases) ~ `)() { ` ~ typeof(mixin(expr)).stringof ~ ` res; auto t = new Thread({res = mixin(expr);}); t.start; return { t.wait; return res; }; }`); } void main() { int a = 1, b = 2; auto hard_result = future!("a,b", "a+b+39")!()(); } Seems like Walter has at least two bugs to solve before that happens..
Nov 05 2007
Jari-Matti Mäkelä wrote:BCS wrote:It's not a bug that future can't see a or b. They're called "visibility rules" and we have them for a reason, boy. Without rules, there's just *chaos* and chaos is... ugh, I don't even want to *think* about it. You could cheat and do this, though: void main() { int a = 1, b = 2; auto hard_result = mixin(future("a,b","a+b+39")); } Since the string mixin gets expanded into the context of the function. But, as you can see, it's ugly and clumsy. Just stick to lazy arguments for Pete's sake :P -- Danielstring mixins are fun: typeof(mixin(expr)) delegate() future(char[] expr)() { typeof(mixin(expr)) res; auto t = new Thread({res = mixin(expr);}); t.start; return { t.wait; return res; } } auto hard_result = future!("hard_expression"); // Do stuff ... use_result(hard_result()); NOTE: this runs into the issue that there is no way to get a template to play with the local variables of a function. If there were a way to do this I can think of a number of very cool things that could be done.Yea, bummer :| I would have expected this to work: char[] bar(char[] foo) { char[] tmp; tmp ~= "alias "; foreach(p; foo) if (p == ',') tmp ~= ", alias "; else tmp ~= p; return tmp; } template future(char[] aliases, char[] expr) { mixin(typeof(mixin(expr)).stringof ~ ` delegate() future(` ~ bar(aliases) ~ `)() { ` ~ typeof(mixin(expr)).stringof ~ ` res; auto t = new Thread({res = mixin(expr);}); t.start; return { t.wait; return res; }; }`); } void main() { int a = 1, b = 2; auto hard_result = future!("a,b", "a+b+39")!()(); } Seems like Walter has at least two bugs to solve before that happens..
Nov 05 2007
Daniel Keep wrote:It's not a bug that future can't see a or b. They're called "visibility rules" and we have them for a reason, boy. Without rules, there's just *chaos* and chaos is... ugh, I don't even want to *think* about it.It would be nice to have a way to say "reach around these rules right now"
Nov 05 2007
BCS wrote:Daniel Keep wrote:I believe this is what AST macros will be for. :) -- DanielIt's not a bug that future can't see a or b. They're called "visibility rules" and we have them for a reason, boy. Without rules, there's just *chaos* and chaos is... ugh, I don't even want to *think* about it.It would be nice to have a way to say "reach around these rules right now"
Nov 05 2007
Daniel Keep wrote:Jari-Matti Mäkelä wrote:You probably didn't notice but I used alias parameters there. So it should be very much possible to refer to the variables after that. Made a small bug though, should have beenBCS wrote:It's not a bug that future can't see a or b. They're called "visibility rules" and we have them for a reason, boy. Without rules, there's just *chaos* and chaos is... ugh, I don't even want to *think* about it.string mixins are fun: typeof(mixin(expr)) delegate() future(char[] expr)() { typeof(mixin(expr)) res; auto t = new Thread({res = mixin(expr);}); t.start; return { t.wait; return res; } } auto hard_result = future!("hard_expression"); // Do stuff ... use_result(hard_result()); NOTE: this runs into the issue that there is no way to get a template to play with the local variables of a function. If there were a way to do this I can think of a number of very cool things that could be done.Yea, bummer :| I would have expected this to work: char[] bar(char[] foo) { char[] tmp; tmp ~= "alias "; foreach(p; foo) if (p == ',') tmp ~= ", alias "; else tmp ~= p; return tmp; } template future(char[] aliases, char[] expr) { mixin(typeof(mixin(expr)).stringof ~ ` delegate() future(` ~ bar(aliases) ~ `)() { ` ~ typeof(mixin(expr)).stringof ~ ` res; auto t = new Thread({res = mixin(expr);}); t.start; return { t.wait; return res; }; }`); } void main() { int a = 1, b = 2; auto hard_result = future!("a,b", "a+b+39")!()(); } Seems like Walter has at least two bugs to solve before that happens..or perhaps char[] Future(char[] a, char[] b) { return `future!("` ~ a ~ `","` ~ b ~ `")!(` ~ a ~ `)`; } auto hard_result = mixin(Future("a,b", "a+b+39"))();auto hard_result = future!("a,b", "a+b+39")!(a,b)();You could cheat and do this, though: void main() { int a = 1, b = 2; auto hard_result = mixin(future("a,b","a+b+39")); } Since the string mixin gets expanded into the context of the function.Alias parameters work much better.
Nov 06 2007
My futurism library has support for the kind of thing you are describing. Two of the several syntaxes it supports are shown here; both of these are for reading two disk files in parallel. // Read the contents of two files in parallel: char[] read_file(char[] fname) { return cast(char[]) read(fname1); } auto f1 = make_future(& read_file, fname1); auto f2 = make_future(& read_file, fname2); // Get the results - will block until each read() is done. char[] fv1 = f1.value; char[] fv2 = f2.value; And this: alias Future!(char[]) FString; char[] fname1 = "a.txt", fname2 = "b.txt"; FString f1 = new FString({ return cast(char[]) read(fname1); }); FString f2 = new FString({ return cast(char[]) read(fname2); }); // Get the results - will block until each read() is done. char[] fv1 = f1.value; char[] fv2 = f2.value; You can find it at dsource.org/projects/futurism. It's a few more than 7 lines, but a lot of it is thread pool management and so on. It took a bit of trial and error and some newsgroup chat to nail down all the syntax for capturing arguments like the fname1 shown here. (Maybe some of that would not be needed now that full closures exist.) Kevin
Nov 05 2007
Mikola Lysenko wrote:I'm excited. For the first time it is now possible to do futures in 7 lines of code: T delegate() future(T)(lazy T expr) { T res; auto t = new Thread({res = expr();}); t.start; return { t.wait; return res; } } Which could then be used as follows: auto hard_result = future( hard_expression ); // Do stuff ... use_result(hard_result());I tested your code and got this: Unhandled Exception: EXCEPTION_ACCESS_VIOLATION(0xc0000005) at (0x00930f80) ->us -> Something's wrong, but I'm not sure what. res is correctly put on the heap, so it's not that.
Nov 05 2007
0ffh Wrote:Bruce Adams wrote:Since they're on the language comparison feature chat it implies (to me at least) that there is some perceived benefit to the concept. Any poor strictly functional guys care to explain what it is?KlausO Wrote: Someone with more a functional programming background may be able to enlighten us but doesn't the existence of proper closures allow us to implement monads as a library now? Though I can't quite see in what situation we would want to use them.Yup, IIRC the poor strictly functional guys needs them for I/O, but as we're imperative anyway I don't see any applications for monads either. Regards, Frank
Nov 05 2007
Bruce Adams wrote:Since they're on the language comparison feature chat it implies (to me at least) that there is some perceived benefit to the concept. Any poor strictly functional guys care to explain what it is?Here's a good introduction to monads in the context of Haskell: http://research.microsoft.com/%7Esimonpj/Papers/marktoberdorf/mark.pdf Basically, a monad is a type whose values represent actions. An action is something that, when done, has an effect on some global state and then possibly returns a value. There are operators that allow actions to be composed in various ways. So, in a pure functional programming language, you never actually *do* anything imperatively - you just construct a huge action, which contains everything your program should do, by gluing together lots of itty-bitty actions (and pure functions). The construction of this action is done purely-fuctionally. Then the runtime executes the action, but this occurs outside of the language proper, so it doesn't hurt your beautiful clean pure-functionality. Thanks, Nathan Reed
Nov 05 2007
Nathan Reed Wrote:Bruce Adams wrote:But could there be any benefit to a 'non functional' language like D. I just read a paper demonstrating how continuations can be viewed as a special case/use of monads for example. Or perhaps in lazy evaluation of complex expressions?Since they're on the language comparison feature chat it implies (to me at least) that there is some perceived benefit to the concept. Any poor strictly functional guys care to explain what it is?Here's a good introduction to monads in the context of Haskell: http://research.microsoft.com/%7Esimonpj/Papers/marktoberdorf/mark.pdf Basically, a monad is a type whose values represent actions. An action is something that, when done, has an effect on some global state and then possibly returns a value. There are operators that allow actions to be composed in various ways. So, in a pure functional programming language, you never actually *do* anything imperatively - you just construct a huge action, which contains everything your program should do, by gluing together lots of itty-bitty actions (and pure functions). The construction of this action is done purely-fuctionally. Then the runtime executes the action, but this occurs outside of the language proper, so it doesn't hurt your beautiful clean pure-functionality. Thanks, Nathan Reed
Nov 06 2007
Bruce Adams Wrote:Nathan Reed Wrote:I'm inclined to agree with Bruce. My opinion is that for C-style languages, monads are just a bunch of category-theoretic red tape. The primary function of monadic types is to induce ordering and side-effects within the evaluation of functional programs. D already has a well defined order for executing statements, so adding support for monads seems redundant in a most painful way. Monadic type modifiers are also inscrutably arcane. Nothing is going to scare off newbies more easily than several pages of incomprehensible mathematical text describing how to print a line of text to the console (see: Haskell). -MikBruce Adams wrote:But could there be any benefit to a 'non functional' language like D.Since they're on the language comparison feature chat it implies (to me at least) that there is some perceived benefit to the concept. Any poor strictly functional guys care to explain what it is?
Nov 06 2007
torhu Wrote:I tested your code and got this: Unhandled Exception: EXCEPTION_ACCESS_VIOLATION(0xc0000005) at (0x00930f80) ->us -> Something's wrong, but I'm not sure what. res is correctly put on the heap, so it's not that.Well, I suppose I should have prefixed that code with the statement that it was untested. At the time, I was overcome by exuberance and posted the snippet from the school CS labs without access to a DMD 2 compiler. I won't have time to examine it more carefully until I get back from a trip this Friday. -Mik
Nov 06 2007
Dnia Fri, 02 Nov 2007 14:03:59 -0700 Walter Bright <newshound1 digitalmars.com> napisa=B3/a:D 2.007 brings full closures to D. I was tired of D being denigrated for not having "real" closures. =20Simpler curring? // not tested C delegate(B) curry(A, C, B...)(C delegate(A, B) dg_, A a_) { auto a =3D a_; auto dg =3D dg_; C add(B b) { return dg(a, b); } return &add; } instand of: // Old code: http://www.digitalmars.com/d/template.html R delegate(U) Curry(R, A, U...)(R delegate(A, U) dg, A arg) { struct Foo { typeof(dg) dg_m; typeof(arg) arg_m; R bar(U u) { return dg_m(arg_m, u); } } Foo* f =3D new Foo; f.dg_m =3D dg; f.arg_m =3D arg; return &f.bar; } Also other functional stuff like infinite lazy lists or monads can be done much much simpler now :) --=20 Witold Baryluk, aleph0
Nov 06 2007
Maybe the two inner variables can be omitted as : C delegate(B) curry(A, C, B...)(C delegate(A, B) dg_, A a_) { C add(B b) { return dg_(a_, b); }; return &add; } Witold Baryluk ćé:Dnia Fri, 02 Nov 2007 14:03:59 -0700 Walter Bright <newshound1 digitalmars.com> napisaĆ/a:D 2.007 brings full closures to D. I was tired of D being denigrated for not having "real" closures.Simpler curring? // not tested C delegate(B) curry(A, C, B...)(C delegate(A, B) dg_, A a_) { auto a = a_; auto dg = dg_; C add(B b) { return dg(a, b); } return &add; } instand of: // Old code: http://www.digitalmars.com/d/template.html R delegate(U) Curry(R, A, U...)(R delegate(A, U) dg, A arg) { struct Foo { typeof(dg) dg_m; typeof(arg) arg_m; R bar(U u) { return dg_m(arg_m, u); } } Foo* f = new Foo; f.dg_m = dg; f.arg_m = arg; return &f.bar; } Also other functional stuff like infinite lazy lists or monads can be done much much simpler now :)
Nov 06 2007
Or even: C delegate(B) curry(A, C, B...)(C delegate(A, B) dg_, A a_) { return delegate C(B b) { return dg_(a_, b); }; } outersky wrote:Maybe the two inner variables can be omitted as : C delegate(B) curry(A, C, B...)(C delegate(A, B) dg_, A a_) { C add(B b) { return dg_(a_, b); }; return &add; } Witold Baryluk ćé:Dnia Fri, 02 Nov 2007 14:03:59 -0700 Walter Bright <newshound1 digitalmars.com> napisaĆ/a:D 2.007 brings full closures to D. I was tired of D being denigrated for not having "real" closures.Simpler curring? // not tested C delegate(B) curry(A, C, B...)(C delegate(A, B) dg_, A a_) { auto a = a_; auto dg = dg_; C add(B b) { return dg(a, b); } return &add; } instand of: // Old code: http://www.digitalmars.com/d/template.html R delegate(U) Curry(R, A, U...)(R delegate(A, U) dg, A arg) { struct Foo { typeof(dg) dg_m; typeof(arg) arg_m; R bar(U u) { return dg_m(arg_m, u); } } Foo* f = new Foo; f.dg_m = dg; f.arg_m = arg; return &f.bar; } Also other functional stuff like infinite lazy lists or monads can be done much much simpler now :)
Nov 06 2007
Great! Since it's a delegate, can it be replaced as a Closure ? If closure can support parameters. Russell Lewis ćé:Or even: C delegate(B) curry(A, C, B...)(C delegate(A, B) dg_, A a_) { return delegate C(B b) { return dg_(a_, b); }; } outersky wrote:Maybe the two inner variables can be omitted as : C delegate(B) curry(A, C, B...)(C delegate(A, B) dg_, A a_) { C add(B b) { return dg_(a_, b); }; return &add; } Witold Baryluk ćé:Dnia Fri, 02 Nov 2007 14:03:59 -0700 Walter Bright <newshound1 digitalmars.com> napisaĆ/a:D 2.007 brings full closures to D. I was tired of D being denigrated for not having "real" closures.Simpler curring? // not tested C delegate(B) curry(A, C, B...)(C delegate(A, B) dg_, A a_) { auto a = a_; auto dg = dg_; C add(B b) { return dg(a, b); } return &add; } instand of: // Old code: http://www.digitalmars.com/d/template.html R delegate(U) Curry(R, A, U...)(R delegate(A, U) dg, A arg) { struct Foo { typeof(dg) dg_m; typeof(arg) arg_m; R bar(U u) { return dg_m(arg_m, u); } } Foo* f = new Foo; f.dg_m = dg; f.arg_m = arg; return &f.bar; } Also other functional stuff like infinite lazy lists or monads can be done much much simpler now :)
Nov 06 2007
Dnia Tue, 06 Nov 2007 20:17:58 -0700 Russell Lewis <webmaster villagersonline.com> napisa=B3/a:Or even: =20 C delegate(B) curry(A, C, B...)(C delegate(A, B) dg_, A a_) { return delegate C(B b) { return dg_(a_, b); }; }Yes i was thinking about it, but didn't know if it should work. I just installed 2.007, and it is working! :) import std.stdio; C delegate(B) curry1(A, C, B...)(C delegate(A, B) dg_, A a_) { auto a =3D a_; auto dg =3D dg_; C add(B b) { return dg(a, b); } return &add; } C delegate(B) curry2(A, C, B...)(C delegate(A, B) dg, A a) { C add(B b) { return dg(a, b); } return &add; } C delegate(B) curry3(A, C, B...)(C delegate(A, B) dg, A a) { return delegate C(B b) { return dg(a, b); }; } int main(char[][] args) { int sum(int a, int b, int c) { return a+b+c; } auto s1 =3D curry1(&sum, 11000); assert(s1(200,30) =3D=3D 11230); auto s2 =3D curry2(&sum, 21000); assert(s2(200,30) =3D=3D 21230); auto s3 =3D curry3(&sum, 31000); assert(s3(200,30) =3D=3D 31230); return 0; } --=20 Witold Baryluk, aleph0 MAIL: baryluk smp.if.uj.edu.pl JID: movax jabber.autocom.pl
Nov 06 2007
Witold Baryluk wrote:Dnia Tue, 06 Nov 2007 20:17:58 -0700 Russell Lewis <webmaster villagersonline.com> napisał/a:Pedantic's note: currying an argument actually means creating a function that accepts a function of N arguments and returns a function that accepts 1 argument and returns a function that accepts N-1 arguments. But I don't really care if you want to call partial application currying. Just be aware that you're using the term in a way many would deem incorrect. Random link: http://srfi.schemers.org/srfi-26/mail-archive/msg00015.html --bbOr even: C delegate(B) curry(A, C, B...)(C delegate(A, B) dg_, A a_) { return delegate C(B b) { return dg_(a_, b); }; }Yes i was thinking about it, but didn't know if it should work. I just installed 2.007, and it is working! :) import std.stdio; C delegate(B) curry1(A, C, B...)(C delegate(A, B) dg_, A a_) { auto a = a_; auto dg = dg_; C add(B b) { return dg(a, b); } return &add; } C delegate(B) curry2(A, C, B...)(C delegate(A, B) dg, A a) { C add(B b) { return dg(a, b); } return &add; } C delegate(B) curry3(A, C, B...)(C delegate(A, B) dg, A a) { return delegate C(B b) { return dg(a, b); }; } int main(char[][] args) { int sum(int a, int b, int c) { return a+b+c; } auto s1 = curry1(&sum, 11000); assert(s1(200,30) == 11230); auto s2 = curry2(&sum, 21000); assert(s2(200,30) == 21230); auto s3 = curry3(&sum, 31000); assert(s3(200,30) == 31230); return 0; }
Nov 06 2007
Bill Baxter wrote:Witold Baryluk wrote:Pedant's note: a pedantic person is called a "pedant". --bbDnia Tue, 06 Nov 2007 20:17:58 -0700 Russell Lewis <webmaster villagersonline.com> napisał/a:Pedantic's note:Or even: C delegate(B) curry(A, C, B...)(C delegate(A, B) dg_, A a_) { return delegate C(B b) { return dg_(a_, b); }; }Yes i was thinking about it, but didn't know if it should work. I just installed 2.007, and it is working! :) import std.stdio; C delegate(B) curry1(A, C, B...)(C delegate(A, B) dg_, A a_) { auto a = a_; auto dg = dg_; C add(B b) { return dg(a, b); } return &add; } C delegate(B) curry2(A, C, B...)(C delegate(A, B) dg, A a) { C add(B b) { return dg(a, b); } return &add; } C delegate(B) curry3(A, C, B...)(C delegate(A, B) dg, A a) { return delegate C(B b) { return dg(a, b); }; } int main(char[][] args) { int sum(int a, int b, int c) { return a+b+c; } auto s1 = curry1(&sum, 11000); assert(s1(200,30) == 11230); auto s2 = curry2(&sum, 21000); assert(s2(200,30) == 21230); auto s3 = curry3(&sum, 31000); assert(s3(200,30) == 31230); return 0; }
Nov 06 2007
Ok, everyone knows that closures are cool (and those that don't know will soon see), so let me see what y'all think about this proposal... Now Perl has extremely compact closures because of the special $_ and _ variables that can be used to refer to the function arguments. Just the other day, I wrote this handy bit of code which converts relative directory entries into absolute pathnames: my $base = "/path/to/dir/"; my absoluteDirs = map { $_ = $base . $_ } readdir DIR; Whatever you think about Perl, you have to admit that this is some pretty sweet code. It's about as pure as Haskell (well, obviously I'm modifying $_, so it's not 'pure' in the literal sense), modulo Perl's syntax. Let's look at the old imperative way to do this: string base = "/path/to/dir/"; string[] absoluteDirs; foreach (file; readdir(DIR)) { absoluteDirs ~= base ~ file; } This really isn't too bad, and some folks might even prefer this style, but when the closure variables become lengthy expressions, this becomes more verbose. Note that we have to repeat absoluteDirs and file, but thank D that we don't have to spell out a bunch of extraneous types or loop mechanics. Now given some suitable definitions (which I demonstrate at the bottom), this is what we can do in D, with Walter's new closures: auto base = "/path/to/dir/"; auto absoluteDirs = map((string file) { return base ~ file; }, readdir(DIR)); We don't have to repeat absoluteDirs, but we still have to repeat file. Still, this is pretty darned close to the Perl form, so I think we're doing pretty well. However, I think it would be nice to get rid of the delegate type declaration altogether and git ourselves an implicit delegate argument, like so: auto base = "/path/to/dir/"; auto absoluteDirs = map({ return base ~ $0; }, readdir(DIR)); Notice that this is just about as close to the Perl/Haskell form as D is likely to ever get. And it's a thing of beauty. Of course, this is a small (but real) example, so it doesn't fully illustrate the utility of implicit lambda args, but don't you agree that this would be cool? Dave ----------8<----------Proof of Concept---------------8<--------------- module Test; import std.stdio; string[] readdir(string dir) { return ["foo", "barz", "bazzz"]; } T[] map(T)(T delegate(T) f, T[] list) { T[] result; foreach (e; list) result ~= f(e); return result; } void main() { auto base = "/path/to/dir/"; writeln( map((string file) { return base ~ file; }, readdir("")) ); }
Nov 06 2007
David B. Held wrote:However, I think it would be nice to get rid of the delegate type declaration altogether and git ourselves an implicit delegate argument, like so: auto base = "/path/to/dir/"; auto absoluteDirs = map({ return base ~ $0; }, readdir(DIR));I think you're asking for too much intelligence form the compiler here. Remember D is statically typed. Without giving the parameter type for that delegate, we can't verify that its body is well-typed or that the call to map() is well-typed. It is logically possible to infer the types in this case, but that goes way beyond the scope of what a D compiler should have to do - this isn't ML; we do type checking, but not full type inference. Thanks, Nathan Reed
Nov 06 2007
Nathan Reed wrote:David B. Held wrote:In order to analyze this properly, we need to look at my (working) definition of map(): T[] map(T)(T delegate(T) f, T[] list) { T[] result; foreach (e; list) result ~= f(e); return result; } Ok, what I propose is that typeof({ return base ~ $0; }) == T delegate(T) which is easy enough to infer, because that is the declared type of f, to which the delegate is bound! No brain surgery there. How do we know what T is? Well, that's easy enough to infer from list. And you're done. The mechanics of it isn't that hard. The question is whether the idea of implicit arguments makes people's stomachs turn or not. DaveHowever, I think it would be nice to get rid of the delegate type declaration altogether and git ourselves an implicit delegate argument, like so: auto base = "/path/to/dir/"; auto absoluteDirs = map({ return base ~ $0; }, readdir(DIR));I think you're asking for too much intelligence form the compiler here. Remember D is statically typed. Without giving the parameter type for that delegate, we can't verify that its body is well-typed or that the call to map() is well-typed. It is logically possible to infer the types in this case, but that goes way beyond the scope of what a D compiler should have to do - this isn't ML; we do type checking, but not full type inference.
Nov 06 2007
David B. Held wrote:In order to analyze this properly, we need to look at my (working) definition of map(): T[] map(T)(T delegate(T) f, T[] list) { T[] result; foreach (e; list) result ~= f(e); return result; } Ok, what I propose is that typeof({ return base ~ $0; }) == T delegate(T) which is easy enough to infer, because that is the declared type of f, to which the delegate is bound! No brain surgery there. How do we know what T is? Well, that's easy enough to infer from list. And you're done. The mechanics of it isn't that hard. The question is whether the idea of implicit arguments makes people's stomachs turn or not. DaveOf course, in this case it's "easy enough to infer". However, function bodies can be arbitrarily complicated, meaning you will need a full type inference engine for this to work in the general case. That adds so much complexity to the compiler that it's simply not worth it for the slight extra convenience. Besides, it's just fundamentally not in the philosophy of D to do things like this. As other people have pointed out, if you want to program like you would in Perl, you should use...Perl. Thanks, Nathan Reed
Nov 07 2007
Nathan Reed Wrote:David B. Held wrote:Not really. The compiler knows what the return-type is.In order to analyze this properly, we need to look at my (working) definition of map(): T[] map(T)(T delegate(T) f, T[] list) { T[] result; foreach (e; list) result ~= f(e); return result; } Ok, what I propose is that typeof({ return base ~ $0; }) == T delegate(T) which is easy enough to infer, because that is the declared type of f, to which the delegate is bound! No brain surgery there. How do we know what T is? Well, that's easy enough to infer from list. And you're done. The mechanics of it isn't that hard. The question is whether the idea of implicit arguments makes people's stomachs turn or not. DaveOf course, in this case it's "easy enough to infer". However, function bodies can be arbitrarily complicated, meaning you will need a full type inference engine for this to work in the general case.That adds so much complexity to the compiler that it's simply not worth it for the slight extra convenience. Besides, it's just fundamentally not in the philosophy of D to do things like this. As other people have pointed out, if you want to program like you would in Perl, you should use...Perl.On "http://www.digitalmars.com/d/" D is defined as "Its focus is on combining the power and high performance of C and C++ with the programmer productivity of modern languages like Ruby and Python." Well, somebody may say perl isn't modern. But in Ruby and Python this is also possible. So, why is this wrong? Only why this is not C-style?
Nov 07 2007
Simas wrote:Nathan Reed Wrote:In python the lambdas still requires arguments to be named, and use of the 'lambda' keyword. So it'd be something like: absDirs = map(lambda x: base + x, readdir(DIR)) I'd prefer that middle ground to perl's magic variables. Make it so the type can be inferred, but the user still has to give it a name. Maybe: auto base = "/path/to/dir/"; auto absoluteDirs = map((x){ return base ~ x; }, readdir(DIR)); But I find the the Python (or Perl) much easier to look at without all the (){}; business around the anonymous function. --bbDavid B. Held wrote:Not really. The compiler knows what the return-type is.In order to analyze this properly, we need to look at my (working) definition of map(): T[] map(T)(T delegate(T) f, T[] list) { T[] result; foreach (e; list) result ~= f(e); return result; } Ok, what I propose is that typeof({ return base ~ $0; }) == T delegate(T) which is easy enough to infer, because that is the declared type of f, to which the delegate is bound! No brain surgery there. How do we know what T is? Well, that's easy enough to infer from list. And you're done. The mechanics of it isn't that hard. The question is whether the idea of implicit arguments makes people's stomachs turn or not. DaveOf course, in this case it's "easy enough to infer". However, function bodies can be arbitrarily complicated, meaning you will need a full type inference engine for this to work in the general case.That adds so much complexity to the compiler that it's simply not worth it for the slight extra convenience. Besides, it's just fundamentally not in the philosophy of D to do things like this. As other people have pointed out, if you want to program like you would in Perl, you should use...Perl.On "http://www.digitalmars.com/d/" D is defined as "Its focus is on combining the power and high performance of C and C++ with the programmer productivity of modern languages like Ruby and Python." Well, somebody may say perl isn't modern. But in Ruby and Python this is also possible. So, why is this wrong? Only why this is not C-style?
Nov 07 2007
Bill Baxter wrote:[...] In python the lambdas still requires arguments to be named, and use of the 'lambda' keyword. So it'd be something like: absDirs = map(lambda x: base + x, readdir(DIR)) I'd prefer that middle ground to perl's magic variables. Make it so the type can be inferred, but the user still has to give it a name. Maybe: auto base = "/path/to/dir/"; auto absoluteDirs = map((x){ return base ~ x; }, readdir(DIR)); But I find the the Python (or Perl) much easier to look at without all the (){}; business around the anonymous function.I agree that anonymous lambda args are probably going a step too far. Andrei proposed the same as you (type-inferred named args), and I think it's a good compromise. Dave
Nov 07 2007
Simas wrote:Nathan Reed Wrote:Not in all cases. // What's the type of dg here? auto dg = { return $0; } void some_func ( int delegate (int) dg ); void some_func ( string delegate (string) dg ); // Which some_func is being called here? some_func({ return $0; }); You can't depend on the environment in which the delegate is defined giving you any information about what the delegate's type should be. It's only a happy accident that in the particular example with 'map' posted above, the second parameter to map() tells you what the type T is and therefore what the delegate's type should be. So, in the general case, you *will* need a full type inference engine to descend into the body of the delegate, examine the expressions in which parameters appear, and infer their types (e.g.: base ~ $0 where base is const(char)[] and ~ is an operation that works on two arrays of the same type, hence $0 is also const(char)[]). And in some cases even that's not enough - the identity delegate I used above, { return $0; }, cannot reasonably be assigned *any* delegate type, since its parameter and return could be any type at all. I'm sorry, this idea is just fundamentally not compatible with D's type system. True, the language *could be* modified in such a way as to make this work. But that is way too far of a departure from D's C/C++ roots. It is not a scripting language.Of course, in this case it's "easy enough to infer". However, function bodies can be arbitrarily complicated, meaning you will need a full type inference engine for this to work in the general case.Not really. The compiler knows what the return-type is.On "http://www.digitalmars.com/d/" D is defined as "Its focus is on combining the power and high performance of C and C++ with the programmer productivity of modern languages like Ruby and Python." Well, somebody may say perl isn't modern. But in Ruby and Python this is also possible. So, why is this wrong? Only why this is not C-style?Ruby, Python, and Perl are all dynamically typed languages, and they owe a good deal of their "programmer productivity" to that. D wants to be progammer-productive too, but D is committed to being *statically* type-safe as much as possible. So D will bring in only those features of modern dynamic languages that are compatible with strong, static typing. Thanks, Nathan Reed
Nov 07 2007
Nathan Reed wrote:Simas wrote:More sophisticated type inference is certainly possible with a strong static typing. When you trigger an ambiguous case like the ones you show above, the compiler should just tell you "cannot deduce type of ___ in expression ___". Maybe with a few possible valid deductions to point out the ambiguity. That's what the strong statically typed language ML does (and variants like OCaml). It just happens that type inference hasn't historically been a big part of C/C++. But D makes a number of moves in the direction of more ML-like type inference. It's just not all the way there. Deducing function return types would be a next logical step.Nathan Reed Wrote:Not in all cases. // What's the type of dg here? auto dg = { return $0; } void some_func ( int delegate (int) dg ); void some_func ( string delegate (string) dg ); // Which some_func is being called here? some_func({ return $0; }); You can't depend on the environment in which the delegate is defined giving you any information about what the delegate's type should be. It's only a happy accident that in the particular example with 'map' posted above, the second parameter to map() tells you what the type T is and therefore what the delegate's type should be. So, in the general case, you *will* need a full type inference engine to descend into the body of the delegate, examine the expressions in which parameters appear, and infer their types (e.g.: base ~ $0 where base is const(char)[] and ~ is an operation that works on two arrays of the same type, hence $0 is also const(char)[]). And in some cases even that's not enough - the identity delegate I used above, { return $0; }, cannot reasonably be assigned *any* delegate type, since its parameter and return could be any type at all. I'm sorry, this idea is just fundamentally not compatible with D's type system. True, the language *could be* modified in such a way as to make this work. But that is way too far of a departure from D's C/C++ roots. It is not a scripting language.Of course, in this case it's "easy enough to infer". However, function bodies can be arbitrarily complicated, meaning you will need a full type inference engine for this to work in the general case.Not really. The compiler knows what the return-type is.What I said above. Aggressive type inference is perfectly compatible with strong static typing. --bbOn "http://www.digitalmars.com/d/" D is defined as "Its focus is on combining the power and high performance of C and C++ with the programmer productivity of modern languages like Ruby and Python." Well, somebody may say perl isn't modern. But in Ruby and Python this is also possible. So, why is this wrong? Only why this is not C-style?Ruby, Python, and Perl are all dynamically typed languages, and they owe a good deal of their "programmer productivity" to that. D wants to be progammer-productive too, but D is committed to being *statically* type-safe as much as possible. So D will bring in only those features of modern dynamic languages that are compatible with strong, static typing.
Nov 07 2007
Bill Baxter wrote:More sophisticated type inference is certainly possible with a strong static typing. When you trigger an ambiguous case like the ones you show above, the compiler should just tell you "cannot deduce type of ___ in expression ___". Maybe with a few possible valid deductions to point out the ambiguity. That's what the strong statically typed language ML does (and variants like OCaml).I'm familiar with ML; in fact, I use it extensively. I never claimed this couldn't be done, I just think it's too big of a departure from D's C/C++ roots.It just happens that type inference hasn't historically been a big part of C/C++. But D makes a number of moves in the direction of more ML-like type inference. It's just not all the way there. Deducing function return types would be a next logical step.The only cases I'm aware of where D does any kind of type inference is in assigning `auto' variables (which is trivial, since the type-checker generates a type for the expression on the rhs anyway), and in template argument deduction / IFTI (which C++ has too). If there are other cases I'd love to hear about them. Anyway, deducing function *return* types would indeed be not much different from deducing types for `auto' variables, but deducing function *argument* types is another beast altogether - as I've tried to demonstrate above. Thanks, Nathan Reed
Nov 07 2007
Nathan Reed wrote:Bill Baxter wrote:Ok, you probably know better than me, then. You just seemed to be equating better type inference with scripting languages in your previous post.More sophisticated type inference is certainly possible with a strong static typing. When you trigger an ambiguous case like the ones you show above, the compiler should just tell you "cannot deduce type of ___ in expression ___". Maybe with a few possible valid deductions to point out the ambiguity. That's what the strong statically typed language ML does (and variants like OCaml).I'm familiar with ML; in fact, I use it extensively. I never claimed this couldn't be done, I just think it's too big of a departure from D's C/C++ roots.I was thinking of the type deduction in foreach which deduces the type from signatures of opApply functions. I suppose that's pretty similar to IFTI. But it doesn't really matter. My main point was that D could do more than it does now. That would still hold even if it did absolutely no inference of any kind.It just happens that type inference hasn't historically been a big part of C/C++. But D makes a number of moves in the direction of more ML-like type inference. It's just not all the way there. Deducing function return types would be a next logical step.The only cases I'm aware of where D does any kind of type inference is in assigning `auto' variables (which is trivial, since the type-checker generates a type for the expression on the rhs anyway), and in template argument deduction / IFTI (which C++ has too). If there are other cases I'd love to hear about them.Anyway, deducing function *return* types would indeed be not much different from deducing types for `auto' variables, but deducing function *argument* types is another beast altogether - as I've tried to demonstrate above.Sure I can believe it's different. But that doesn't mean it wouldn't be a good addition to the language. Just make the compiler generate errors on ambiguities. Also you gave the example: void some_func ( int delegate (int) dg ); void some_func ( string delegate (string) dg ); // Which some_func is being called here? some_func({ return $0; }); But you don't need $0 to create an ambiguity. void some_func ( long x); void some_func ( uint x); some_func(4); also gives you errors about ambiguity. So your example would too. Ambiguity isn't a killer as long as a) it's possible to recognize the ambiguity when it appears, and b) there's a significant number practically useful examples that aren't ambiguous. The killer is probably just how difficult it would be to implement without wrecking anything. --bb
Nov 07 2007
Nathan Reed wrote:Bill Baxter wrote:Guess what? Deducing argument types is no harder than choosing the correct return type for min(T, U) (which is why Andrei posed it as a challenge some time ago...it's a hard problem which nicely illustrates the difficulties of type inference in an example so small anyone can understand it). Saying that D can't do this level of type inference is equivalent to saying that you can't write a proper implementation of min() in D, which is awfully defeatist, IMO. I think we should *strive* to get a good implementation of min(), and upon doing so, would have sufficient type deduction machinery to implement these lambdas.More sophisticated type inference is certainly possible with a strong static typing. When you trigger an ambiguous case like the ones you show above, the compiler should just tell you "cannot deduce type of ___ in expression ___". Maybe with a few possible valid deductions to point out the ambiguity. That's what the strong statically typed language ML does (and variants like OCaml).I'm familiar with ML; in fact, I use it extensively. I never claimed this couldn't be done, I just think it's too big of a departure from D's C/C++ roots.[...] Anyway, deducing function *return* types would indeed be not much different from deducing types for `auto' variables, but deducing function *argument* types is another beast altogether - as I've tried to demonstrate above.It's a different problem, granted; but I don't see it as fundamentally harder. Ambiguity errors are a straw man. The hard stuff is when you have to do things like unification of types that people expect to work together, but have problematic subtleties (like int/uint). Dave
Nov 07 2007
On Nov 7, 2007 6:44 AM, David B. Held <dheld codelogicconsulting.com> wrote:my $base = "/path/to/dir/"; my absoluteDirs = map { $_ = $base . $_ } readdir DIR; Whatever you think about Perl, you have to admit that this is some pretty sweet code.Looks completely unreadable to me. It just doesn't make sense to my brain. D is C-like, not perl-like. Please let's keep it that way.Let's look at the old imperative way to do this: string base = "/path/to/dir/"; string[] absoluteDirs; foreach (file; readdir(DIR)) { absoluteDirs ~= base ~ file; }This I understand.Now given some suitable definitions (which I demonstrate at the bottom), this is what we can do in D, with Walter's new closures: auto base = "/path/to/dir/"; auto absoluteDirs = map((string file) { return base ~ file; }, readdir(DIR)); We don't have to repeat absoluteDirs, but we still have to repeat file.But of course. It's the name of a parameter. You ALWAYS have to repeat parameter names, unless the name is "this". (Or "outer" :) ). This is what code clear and readable. In a function parameter list, you give each paramter a name, and then in the function body, you refer to each paramter by name. Perfect!However, I think it would be nice to get rid of the delegate type declaration altogether and git ourselves an implicit delegate argumentGenerally speaking, I don't. Although that said, D already supports variadic functions, so one could declare a function as accepting a tuple, and then refer to the arguments as tuple[n]. I do not believe this would increase readability, however, in cases where functions are not actually variadic!auto base = "/path/to/dir/"; auto absoluteDirs = map({ return base ~ $0; }, readdir(DIR));Yuk! Dollar zero? Yuk! Yuk! Yuk! Please, no!
Nov 07 2007
Janice Caron wrote:On Nov 7, 2007 6:44 AM, David B. Held <dheld codelogicconsulting.com> wrote:What if I said D is BASIC-like? Is that a good thing? The main failure of Perl is that it went overboard with operators. In fact, Perl 6 has so many operators that they had to create a Periodic Table of the Operators for it: http://www.ozonehouse.com/mark/blog/code/PeriodicTable.pdf. The good thing about Perl is that it supports Functional style programming. What do you think about the STL? Good, bad, or ugly? Did you know that std::foreach(), std::transform(), std::accumulate(), etc. are all functional-style algorithms like map()?my $base = "/path/to/dir/"; my absoluteDirs = map { $_ = $base . $_ } readdir DIR; Whatever you think about Perl, you have to admit that this is some pretty sweet code.Looks completely unreadable to me. It just doesn't make sense to my brain. D is C-like, not perl-like. Please let's keep it that way.Of course. You can practically write this verbatim in BASIC and Pascal.Let's look at the old imperative way to do this: string base = "/path/to/dir/"; string[] absoluteDirs; foreach (file; readdir(DIR)) { absoluteDirs ~= base ~ file; }This I understand.^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Sorry, I couldn't resist. ;>Now given some suitable definitions (which I demonstrate at the bottom), this is what we can do in D, with Walter's new closures: auto base = "/path/to/dir/"; auto absoluteDirs = map((string file) { return base ~ file; }, readdir(DIR)); We don't have to repeat absoluteDirs, but we still have to repeat file.But of course. It's the name of a parameter. You ALWAYS have to repeat parameter names, unless the name is "this". (Or "outer" :) ). This is what code clear and readable. In a function parameter list, you giveeach paramter a name, and then in the function body, you refer to each paramter by name. Perfect!But what about closures? In the function body you're referring to variables that you *didn't* declare in the parameter list!Agreed, which is why I dislike Perl's _ variable (which is exactly Tuple[n] for Perl). But let's think about 'this'. You know, there was a time when 'this' was always spelled out: void push(Stack stack, void* item); void pop(Stack stack); void* top(Stack stack); size_t size(Stack stack); Like you said, you declare it in the parameter list, you use it in the function body, right? This must mean that C is superior to C++! Why do we tolerate that first variable disappearing from the function signatures like that? Why, that sounds suspiciously like an implicit parameter!However, I think it would be nice to get rid of the delegate type declaration altogether and git ourselves an implicit delegate argumentGenerally speaking, I don't. Although that said, D already supports variadic functions, so one could declare a function as accepting a tuple, and then refer to the arguments as tuple[n]. I do not believe this would increase readability, however, in cases where functions are not actually variadic!Well, there's many ways we could name implicit parameters. Most likely, it will require using numbers in some way (unless you think that letters are better); but if it's just the syntax that bothers you, I'm open to suggestions. Daveauto base = "/path/to/dir/"; auto absoluteDirs = map({ return base ~ $0; }, readdir(DIR));Yuk! Dollar zero? Yuk! Yuk! Yuk! Please, no!
Nov 07 2007
David B. Held Wrote: However, I think it would be nice to get rid of thedelegate type declaration altogether and git ourselves an implicit delegate argument, like so: auto base = "/path/to/dir/"; auto absoluteDirs = map({ return base ~ $0; }, readdir(DIR));At first blush, it sounded like a really good idea to me (I use Perl quite a bit for hacking out quick scripts, though never in a functional manner). The problem, I feel, is the intended audience and purpose of a language. Implicit function arguments, as well as being hard to implement, make code less readable and auditable. While Perl is great for smaller projects, having worked on a legacy system with ~1.5 million lines of Perl code, I can say it doesn't scale very well at all, and the lack of named arguments is a BIG part of that (since it takes longer to figure out what a subroutine does). Plus, in a big function/closure, you might not even be able to figure out how many arguments it needs without reading through the code. In other words, implicit function parameters (whether inside or outside a closure) make hacking out a quick piece of code easy, but if you're working with a team of different experience levels or on a huge 100-man project, I could see that causing trouble. * I say this without any background in functional anything (hopefully this spring I'll take a course in it and learn what all the fuss is about), but from what I know, pure-functional languages are rarely used in large systems.
Nov 07 2007
David B. Held Wrote:auto base = "/path/to/dir/"; auto absoluteDirs = map({ return base ~ $0; }, readdir(DIR)); Notice that this is just about as close to the Perl/Haskell form as D is likely to ever get. And it's a thing of beauty. Of course, this is a small (but real) example, so it doesn't fully illustrate the utility of implicit lambda args, but don't you agree that this would be cool?Thx for template. Really nice. Vote++
Nov 07 2007
Janice Caron Wrote:D is C-like, ...The D Language is : "Its focus is on combining the power and high performance of C and C++ with the programmer productivity of modern languages like Ruby and Python". This does't exclude a productiv language like perl.I think this is a helpful feature.However, I think it would be nice to get rid of the delegate type declaration altogether and git ourselves an implicit delegate argumentGenerally speaking, I don't. Although that said, D already supports variadic functions, so one could declare a function as accepting a tuple, and then refer to the arguments as tuple[n]. I do not believe this would increase readability, however, in cases where functions are not actually variadic!Once again. Really nice.auto base = "/path/to/dir/"; auto absoluteDirs = map({ return base ~ $0; }, readdir(DIR));Yuk! Dollar zero? Yuk! Yuk! Yuk! Please, no!
Nov 07 2007
the lazy keyword was introduced with a hint to the logging use case. "Save the performance of evaluating the string if logging is disabled." void log( lazy char[] msg ){ ... } ... char[] str = f(); log( "my message: "~str ); With full closures, is this an implicit "taking an address of a local function" that triggers a heap allocated stack frame? If yes, the stack frame is allocated without the need for. this would be counter productive in the logging use case. If no, everything is perfect i think. :)
Nov 11 2007