digitalmars.D - About objective programming
- Antti (17/17) May 15 2005 This should be possible in D:
- Unknown W. Brackets (37/60) May 15 2005 In the current version:
- JimH (5/42) May 15 2005 Are you sure that's true? Sorry, I'm new to D so maybe I'm missing somet...
- Unknown W. Brackets (2/61) May 15 2005
- Brian White (9/23) May 18 2005 I was thinking the same thing. Since you're passing a run-time
- Nod (11/28) May 15 2005 Ah, function overloading based on parameter *value*. I like it! Though t...
- David Medlock (4/47) May 15 2005 This is called 'pattern matching' in the functional programming world,
- Nod (17/64) May 15 2005 Heh, I actually had a feeling that it had something to do with functiona...
- xs0 (9/29) May 15 2005 What's wrong with
- David Medlock (25/61) May 16 2005 There are a few benefits which come with pattern matching.
- Nod (12/73) May 16 2005 True, true. On the same note, it would be possible to separate special c...
- MicroWizard (9/95) May 16 2005 These ideas make me very sad.
- David Medlock (8/20) May 16 2005 I agree with the clean statement, but I have no idea how any of my posts...
- Benji Smith (14/20) May 16 2005 I don't like it. It seems like another clever tricky way of doing
- Derek Parnell (9/32) May 16 2005 Or maybe ...
- David Medlock (15/51) May 16 2005 It doesnt replace conditionals, just particular types of them. (In
- Derek Parnell (19/38) May 16 2005 Oh, don't get me wrong. My 'counter example' was really just pointing ou...
- Nod (41/61) May 16 2005 I agree that in the small scale of these examples, no tangible benefit c...
- James Dunne (48/88) May 18 2005 Large switch blocks aren't horrible looking. They were invented to clea...
- Thomas Kuehne (11/16) May 18 2005 -----BEGIN PGP SIGNED MESSAGE-----
- Derek Parnell (32/44) May 18 2005 Though some ranges are not very intuitive ...
- Thomas Kuehne (18/55) May 18 2005 -----BEGIN PGP SIGNED MESSAGE-----
- Nod (31/125) May 18 2005 Indeed, I meant predominantly if-else statements. As you note yourself t...
- James Dunne (42/192) May 18 2005 Neither have I ever written a compiler, so we both could be conversing t...
- Nod (12/28) May 19 2005 They certainly are powerful enough. Goto is powerful enough. The rest of...
- Kevin Bealer (21/38) May 16 2005 I can see this being practical to implement, as long as the "foo(0)" ver...
This should be possible in D: inline int foo(0) { return 0; } int foo(int i) { return 10/i; } int main() { int i=0; int j; j=foo(i++); // if (i==0) j=0; else j=foo(i) j+=foo(i++); // if (i==0) j+=0; else j+=foo(i) return 0; }
May 15 2005
In the current version: template foo(int i) { int foo() { return 10 / i; } } template foo(int i: 0) { int foo() { return 0; } } In the next version: template foo(int i) { int foo() { static if (i == 0) return 0; else return 10 / i; } } Both are used in the following way: int main() { int i = 0; int j; j = foo!(i++); j += foo!(i++); return 0; } So, yes, it should be - and is - possible in D ;). -[Unknown]This should be possible in D: inline int foo(0) { return 0; } int foo(int i) { return 10/i; } int main() { int i=0; int j; j=foo(i++); // if (i==0) j=0; else j=foo(i) j+=foo(i++); // if (i==0) j+=0; else j+=foo(i) return 0; }
May 15 2005
In article <d67aie$2p6q$1 digitaldaemon.com>, Unknown W. Brackets says...In the current version: template foo(int i) { int foo() { return 10 / i; } } template foo(int i: 0) { int foo() { return 0; } } In the next version: template foo(int i) { int foo() { static if (i == 0) return 0; else return 10 / i; } } Both are used in the following way: int main() { int i = 0; int j; j = foo!(i++); j += foo!(i++); return 0; } So, yes, it should be - and is - possible in D ;). -[Unknown]Are you sure that's true? Sorry, I'm new to D so maybe I'm missing something. But in the case above, the value of i is not known until run time, whereas in Walter's example of static if, the parameter is a constant at compile time. Jim
May 15 2005
The compiler can do basic inlining. -[Unknown]In article <d67aie$2p6q$1 digitaldaemon.com>, Unknown W. Brackets says...In the current version: template foo(int i) { int foo() { return 10 / i; } } template foo(int i: 0) { int foo() { return 0; } } In the next version: template foo(int i) { int foo() { static if (i == 0) return 0; else return 10 / i; } } Both are used in the following way: int main() { int i = 0; int j; j = foo!(i++); j += foo!(i++); return 0; } So, yes, it should be - and is - possible in D ;). -[Unknown]Are you sure that's true? Sorry, I'm new to D so maybe I'm missing something. But in the case above, the value of i is not known until run time, whereas in Walter's example of static if, the parameter is a constant at compile time. Jim
May 15 2005
I was thinking the same thing. Since you're passing a run-time variable, the static-if should always evaluate false (actually an error would probably be better) for this case. Brian ( bcwhite precidia.com ) ------------------------------------------------------------------------------- Suffering is meaningful and can be endured... because it's worth the effort. What is more worthy of your pain than the evolution of your soul?template foo(int i) { int foo() { static if (i == 0) return 0; else return 10 / i; } } [...]Are you sure that's true? Sorry, I'm new to D so maybe I'm missing something. But in the case above, the value of i is not known until run time, whereas in Walter's example of static if, the parameter is a constant at compile time.
May 18 2005
In article <d677hq$2nh7$1 digitaldaemon.com>, Antti says...This should be possible in D: inline int foo(0) { return 0; } int foo(int i) { return 10/i; } int main() { int i=0; int j; j=foo(i++); // if (i==0) j=0; else j=foo(i) j+=foo(i++); // if (i==0) j+=0; else j+=foo(i) return 0; }Ah, function overloading based on parameter *value*. I like it! Though the example is tiny, the concept seems highly powerful. Would that also mean that I could do something like: void handleToken("//") { ... } void handleToken("/*") { ... } void handleToken(char[] t) { ... } and have a corresponding switch statement is generated on function invocation? Though I would assume this would add a quite bit of compiler complexity, it would close the void left by the lack of a preprocessor further, not to mention saving me a lot of typing :)
May 15 2005
Nod wrote:In article <d677hq$2nh7$1 digitaldaemon.com>, Antti says...This is called 'pattern matching' in the functional programming world, and its used pretty extensively. -DavidMThis should be possible in D: inline int foo(0) { return 0; } int foo(int i) { return 10/i; } int main() { int i=0; int j; j=foo(i++); // if (i==0) j=0; else j=foo(i) j+=foo(i++); // if (i==0) j+=0; else j+=foo(i) return 0; }Ah, function overloading based on parameter *value*. I like it! Though the example is tiny, the concept seems highly powerful. Would that also mean that I could do something like: void handleToken("//") { ... } void handleToken("/*") { ... } void handleToken(char[] t) { ... } and have a corresponding switch statement is generated on function invocation? Though I would assume this would add a quite bit of compiler complexity, it would close the void left by the lack of a preprocessor further, not to mention saving me a lot of typing :)
May 15 2005
In article <d681rq$6ll$1 digitaldaemon.com>, David Medlock says...Nod wrote:Heh, I actually had a feeling that it had something to do with functional programming. I've been thinking of checking haskell out one of these days. Well, functional or not, it looks tremendously useful. One could do away with *a lot* of explicit program flow control, and simply let the compiler "piece things together". Hmm, maybe it could also simplify some of the common cases in contract programming... Like, instead of having :void foo(int month) :in { : assert(month > 0 && month <= 12); :} :body { ... } one would simply have :void foo(int:0 < month <= 12) { ... } and both would react similarly on invalid values of course. Thoughts?In article <d677hq$2nh7$1 digitaldaemon.com>, Antti says...This is called 'pattern matching' in the functional programming world, and its used pretty extensively. -DavidMThis should be possible in D: inline int foo(0) { return 0; } int foo(int i) { return 10/i; } int main() { int i=0; int j; j=foo(i++); // if (i==0) j=0; else j=foo(i) j+=foo(i++); // if (i==0) j+=0; else j+=foo(i) return 0; }Ah, function overloading based on parameter *value*. I like it! Though the example is tiny, the concept seems highly powerful. Would that also mean that I could do something like: void handleToken("//") { ... } void handleToken("/*") { ... } void handleToken(char[] t) { ... } and have a corresponding switch statement is generated on function invocation? Though I would assume this would add a quite bit of compiler complexity, it would close the void left by the lack of a preprocessor further, not to mention saving me a lot of typing :)
May 15 2005
Antti wrote:This should be possible in D: inline int foo(0) { return 0; } int foo(int i) { return 10/i; } int main() { int i=0; int j; j=foo(i++); // if (i==0) j=0; else j=foo(i) j+=foo(i++); // if (i==0) j+=0; else j+=foo(i) return 0; }What's wrong with int foo(int a) { return a ? 10/a : 0; } I mean, I don't get the benefit, either performance-wise, or ease-of-use-wise.. xs0
May 15 2005
xs0 wrote:Antti wrote:There are a few benefits which come with pattern matching. 1. Readability. Your example is short but it also requires the person reading the code to understand ALL the possibilities to add/remove/change one. If each possibility has its own function, it becomes more modular. 2. Optimization. List comprehensions have some opportunities for caching results which arent possible with functions. list getOdd( list mylist ) { list result = new list(); foreach( int a; mylist ) if ( !( a & 1 ) ) result.add( a ); return result; } theres no way to know(for the compiler) ahead of time that this function is cacheable( ie that if the contents of the list do not change, the result of the function won't either ) Now try this: list a = mylist[ x -> ( x & 1 == 0 ) ]; Cleaner and no state dependent results. This need only be computed once per list. Haskell goes a step further and will not execute code until it needs the result! I am probably making myself as clear as mud... -DavidMThis should be possible in D: inline int foo(0) { return 0; } int foo(int i) { return 10/i; } int main() { int i=0; int j; j=foo(i++); // if (i==0) j=0; else j=foo(i) j+=foo(i++); // if (i==0) j+=0; else j+=foo(i) return 0; }What's wrong with int foo(int a) { return a ? 10/a : 0; } I mean, I don't get the benefit, either performance-wise, or ease-of-use-wise.. xs0
May 16 2005
In article <d6a46u$1mth$1 digitaldaemon.com>, David Medlock says...xs0 wrote:True, true. On the same note, it would be possible to separate special cases from the main algorithm, thus keeping the main algorithm "clean".Antti wrote:There are a few benefits which come with pattern matching. 1. Readability. Your example is short but it also requires the person reading the code to understand ALL the possibilities to add/remove/change one. If each possibility has its own function, it becomes more modular.This should be possible in D: inline int foo(0) { return 0; } int foo(int i) { return 10/i; } int main() { int i=0; int j; j=foo(i++); // if (i==0) j=0; else j=foo(i) j+=foo(i++); // if (i==0) j+=0; else j+=foo(i) return 0; }What's wrong with int foo(int a) { return a ? 10/a : 0; } I mean, I don't get the benefit, either performance-wise, or ease-of-use-wise.. xs02. Optimization. List comprehensions have some opportunities for caching results which arent possible with functions. list getOdd( list mylist ) { list result = new list(); foreach( int a; mylist ) if ( !( a & 1 ) ) result.add( a ); return result; } theres no way to know(for the compiler) ahead of time that this function is cacheable( ie that if the contents of the list do not change, the result of the function won't either ) Now try this: list a = mylist[ x -> ( x & 1 == 0 ) ]; Cleaner and no state dependent results. This need only be computed once per list.Hmm, I fail to see how list comprehension relates to pattern matching... Arent they quite different creatures? And other than improving readability and reducing typing, wouldnt pattern matching be equal, performance-wise, to a regular if-else/switch construct?Haskell goes a step further and will not execute code until it needs the result!Wee... Precomputation out the window. That doesnt sound good for interactive stuff.I am probably making myself as clear as mud... -DavidMIf everything was as clear as water, who would bother getting wet to see what is underneath? ;) -Nod-
May 16 2005
These ideas make me very sad. What I see: - confusing code making teamwork a religion instead of a method (Does anyone heard about Extreme Programming???), - non real optimization issues, which are important in script languages and not in a compiled language. I hope the community keeps the definition of D clean. Tamas Nagy In article <d6aq6d$2cl6$1 digitaldaemon.com>, Nod says...In article <d6a46u$1mth$1 digitaldaemon.com>, David Medlock says...xs0 wrote:True, true. On the same note, it would be possible to separate special cases from the main algorithm, thus keeping the main algorithm "clean".Antti wrote:There are a few benefits which come with pattern matching. 1. Readability. Your example is short but it also requires the person reading the code to understand ALL the possibilities to add/remove/change one. If each possibility has its own function, it becomes more modular.This should be possible in D: inline int foo(0) { return 0; } int foo(int i) { return 10/i; } int main() { int i=0; int j; j=foo(i++); // if (i==0) j=0; else j=foo(i) j+=foo(i++); // if (i==0) j+=0; else j+=foo(i) return 0; }What's wrong with int foo(int a) { return a ? 10/a : 0; } I mean, I don't get the benefit, either performance-wise, or ease-of-use-wise.. xs02. Optimization. List comprehensions have some opportunities for caching results which arent possible with functions. list getOdd( list mylist ) { list result = new list(); foreach( int a; mylist ) if ( !( a & 1 ) ) result.add( a ); return result; } theres no way to know(for the compiler) ahead of time that this function is cacheable( ie that if the contents of the list do not change, the result of the function won't either ) Now try this: list a = mylist[ x -> ( x & 1 == 0 ) ]; Cleaner and no state dependent results. This need only be computed once per list.Hmm, I fail to see how list comprehension relates to pattern matching... Arent they quite different creatures? And other than improving readability and reducing typing, wouldnt pattern matching be equal, performance-wise, to a regular if-else/switch construct?Haskell goes a step further and will not execute code until it needs the result!Wee... Precomputation out the window. That doesnt sound good for interactive stuff.I am probably making myself as clear as mud... -DavidMIf everything was as clear as water, who would bother getting wet to see what is underneath? ;) -Nod-
May 16 2005
MicroWizard wrote:These ideas make me very sad. What I see: - confusing code making teamwork a religion instead of a method (Does anyone heard about Extreme Programming???), - non real optimization issues, which are important in script languages and not in a compiled language. I hope the community keeps the definition of D clean. Tamas NagyI agree with the clean statement, but I have no idea how any of my posts have the slightest thing to do with teamwork, religion or Xtreme programming? int factorial(0) { return 1; } int factorial(int n) { return n * factorial(n-1); } Wow thats looks confusing... -DavidM
May 16 2005
David Medlock wrote:int factorial(0) { return 1; } int factorial(int n) { return n * factorial(n-1); } Wow thats looks confusing... -DavidMI don't like it. It seems like another clever tricky way of doing things, when the ordinary non-tricy way works just fine. What's wrong with: int factorial (int n) { if (n == 0) { return 0; } else { return n * factorial(n - 1); } } To me, using implicit function overloads as an alternate form of flow control is worse than using exceptions for flow control. Clarity, people!! --BenjiSmith
May 16 2005
On Mon, 16 May 2005 18:15:09 -0600, Benji Smith wrote:David Medlock wrote:Or maybe ... uint factorial (uint n) { return n == 0 ? 1 : n * factorial(n - 1); } -- Derek Melbourne, Australia 17/05/2005 10:32:16 AMint factorial(0) { return 1; } int factorial(int n) { return n * factorial(n-1); } Wow thats looks confusing... -DavidMI don't like it. It seems like another clever tricky way of doing things, when the ordinary non-tricy way works just fine. What's wrong with: int factorial (int n) { if (n == 0) { return 0; } else { return n * factorial(n - 1); } } To me, using implicit function overloads as an alternate form of flow control is worse than using exceptions for flow control. Clarity, people!!
May 16 2005
Derek Parnell wrote:On Mon, 16 May 2005 18:15:09 -0600, Benji Smith wrote:It doesnt replace conditionals, just particular types of them. (In languages like Prolog it is the only way to specify branches the code may take,however) With this small example it doesn't seem like much, but extend it to say, a parser with dozens of options for the same function which are likely to be added all the time. I personally hate multiply-nested if/then/else constructs. The usual way to help this type of complexity is to make the program data driven(regular expressions) or use a preprocessor(lex and yacc). I am not suggesting D adopt this however. It does not fit with what D tries to do, be a great systems language with as few surprises as possible for the developer. Cheers, -DavidMDavid Medlock wrote:Or maybe ... uint factorial (uint n) { return n == 0 ? 1 : n * factorial(n - 1); }int factorial(0) { return 1; } int factorial(int n) { return n * factorial(n-1); } Wow thats looks confusing... -DavidMI don't like it. It seems like another clever tricky way of doing things, when the ordinary non-tricy way works just fine. What's wrong with: int factorial (int n) { if (n == 0) { return 0; } else { return n * factorial(n - 1); } } To me, using implicit function overloads as an alternate form of flow control is worse than using exceptions for flow control. Clarity, people!!
May 16 2005
On Mon, 16 May 2005 20:46:01 -0400, David Medlock wrote:Derek Parnell wrote:[snip]On Mon, 16 May 2005 18:15:09 -0600, Benji Smith wrote:David Medlock wrote:int factorial(0) { return 1; } int factorial(int n) { return n * factorial(n-1); }Oh, don't get me wrong. My 'counter example' was really just pointing out a mistake in the previous example (return 0?) as well as providing yet another alternative. I think I like the pseudo-declarative style you have suggested. The one thing a little confusing about it is that it *looks* like a compile time evaluation but it is really a run time evaluation. That might surprise people at first. It is kind of like an elaborate switch statement... uint factorial (uint n) { switch (n) { case 0: return 1; default: return n * factorial(n - 1); } } -- Derek Melbourne, Australia 17/05/2005 11:01:17 AMOr maybe ... uint factorial (uint n) { return n == 0 ? 1 : n * factorial(n - 1); }It doesnt replace conditionals, just particular types of them. (In languages like Prolog it is the only way to specify branches the code may take,however)
May 16 2005
In article <d6bdbo$2sk0$1 digitaldaemon.com>, Benji Smith says...David Medlock wrote:I agree that in the small scale of these examples, no tangible benefit can be found, but think bigger! I believe the benefit can be found when you have a large number of flow paths to select from. In the real world you often see large-to-huge if-else/select-case statements which are *horrible* to read and maintain. Yes, one *should* break it down and refactor, but who has the time? If it works, dont touch it, right? And thus, it keeps on growing. Wouldnt it be better to let the compiler handle the kludge? Take for example a token despatcher in a parser. They usually have the same boilerplate buildup: a large switch statement, and in each of the cases sits a function which handles the token or token class. So what is really going on here? Data values controls program flow into functions. Well, thats exactly what is suggested with pattern matching, only implicitly! The loop gets potentially reduced to this: :foreach (Token t; tokens) : handleToken(t); .. and the compiler should generate the code necessary to select the correct handleToken overload based on the token. Now thats *increasing* clarity, not reducing it. Also, when using pattern matching, its the *function* which decides which cases it can handle, and not, as usually, the kludge. This is how it should be. If we were to, for example, split one of the token handler functions in two, we would have to change two places: both the kludge and the function(s). Not so if using pattern matching. Since the flow control is implicit, one simply have to update the function signatures to handle each of their own cases. Clean! As another example, a bit more real-world, take the pure Win32 Windows Message Loop (TM). I am sure most of you are familiar with it. The WML can be done in two ways, a bad one and a good one respectively: either handling things in a huge kludge as in the above example (I have seen too many of these) or, the better way is to use windowsx.h macros. These macros do nothing more than, calls a specific function for each message type. Exactly that could be accomplished by pattern matching based on function signatures, implicitly, and you would never have to touch the main loop. Of course its not a panacea, but my point is, when used judiciously it could potentially make maintenance and refactoring easier. Other than that it would just be another tool in the chest. btw: I call the idiom "pattern matching" because thats what DavidM said it was called in functional programming, but maybe something like "value-based overloading" would be more appropriate in D context? Phew! That went on too long. -Nod-int factorial(0) { return 1; } int factorial(int n) { return n * factorial(n-1); } Wow thats looks confusing... -DavidMI don't like it. It seems like another clever tricky way of doing things, when the ordinary non-tricy way works just fine. What's wrong with: int factorial (int n) { if (n == 0) { return 0; } else { return n * factorial(n - 1); } } To me, using implicit function overloads as an alternate form of flow control is worse than using exceptions for flow control. Clarity, people!! --BenjiSmith
May 16 2005
I'm a bit skeptical... In article <d6bk3r$4t8$1 digitaldaemon.com>, Nod says...I agree that in the small scale of these examples, no tangible benefit can be found, but think bigger! I believe the benefit can be found when you have a large number of flow paths to select from. In the real world you often see large-to-huge if-else/select-case statements which are *horrible* to read and maintain. Yes, one *should* break it down and refactor, but who has the time? If it works, dont touch it, right? And thus, it keeps on growing. Wouldnt it be better to let the compiler handle the kludge?Large switch blocks aren't horrible looking. They were invented to clear up large blocks of repeated if-else definitions. Now *those* are barely readable. The only reason some repeated if-else blocks are used is because there is a lack of range-handling by case statements. Admittedly, switch statements can be made a bit more readable by providing each case block with its own curly-braced scope. #switch (mydata) {Take for example a token despatcher in a parser. They usually have the same boilerplate buildup: a large switch statement, and in each of the cases sits a function which handles the token or token class. So what is really going on here? Data values controls program flow into functions. Well, thats exactly what is suggested with pattern matching, only implicitly! The loop gets potentially reduced to this: :foreach (Token t; tokens) : handleToken(t); .. and the compiler should generate the code necessary to select the correct handleToken overload based on the token.With magical handwaving? No, with an effectively large compiler-generated switch statement! =P We've just shoved the work on down to the compiler - and I think our current compiler has enough on its plate to deal with =P. However, if the compiler were able to inline all of these value-based overloaded functions into the large generated switch statement, then I can see the gain in efficiency. That probably won't happen though, and would be a lot of work to implement in the compiler.Now thats *increasing* clarity, not reducing it.I can see how, in the limited number of examples proposed, that this would increase visual clarity - somewhat. It depends how you're trained to look at it. My eyes are trained to see switch statements calling functions based on data, and that's instantly recognizable to me - not having worked with any functional-based languages. I think you'll find this a pattern in C++ and C programmers moving over to D.Also, when using pattern matching, its the *function* which decides which cases it can handle, and not, as usually, the kludge. This is how it should be. If we were to, for example, split one of the token handler functions in two, we would have to change two places: both the kludge and the function(s). Not so if using pattern matching. Since the flow control is implicit, one simply have to update the function signatures to handle each of their own cases. Clean!Seems like a nightmare to try to maintain all the possible cases if the number of overloads gets up there. Tell me how this would work for a straight bytecode interpreter and how it could improve readability and efficiency.As another example, a bit more real-world, take the pure Win32 Windows Message Loop (TM). I am sure most of you are familiar with it. The WML can be done in two ways, a bad one and a good one respectively: either handling things in a huge kludge as in the above example (I have seen too many of these) or, the better way is to use windowsx.h macros. These macros do nothing more than, calls a specific function for each message type. Exactly that could be accomplished by pattern matching based on function signatures, implicitly, and you would never have to touch the main loop.Why not make a table of function pointers to handle the messages? You then get O(1) lookup on which function to call, instead of O(n) (?) as in a switch statement. Then again, you lose the *possible* performance benefits of the compiler inlining your functions.Of course its not a panacea, but my point is, when used judiciously it could potentially make maintenance and refactoring easier. Other than that it would just be another tool in the chest.Having this support for n number of parameters increases the complexity exponentially, not to mention the amount of typing and high potential to introduce bugs when copy/pasting definitions.btw: I call the idiom "pattern matching" because thats what DavidM said it was called in functional programming, but maybe something like "value-based overloading" would be more appropriate in D context?Despite my objections above, I'd say that "value-based overloading" sounds better since we're not matching patterns. Then again, I truly hate terminology =P, especially when a term has no latin-based root in its name or any relevance to what it describes.Phew! That went on too long. -Nod-And I made it longer! =) What I'd really like to see is better switch statement case handling. The new ISOC99 standard for C now allows ranges in case statements; why not adapt D's switch statement for this? *checks the docs* We already have comma-separated cases, why not ranges in the form case a .. b (like Pascal did)? I think this would improve significantly on readability of data-driven program flow. Regards, James Dunne
May 18 2005
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 James Dunne schrieb am Wed, 18 May 2005 21:55:47 +0000 (UTC): <snip> <snip> <snip>What I'd really like to see is better switch statement case handling. The new ISOC99 standard for C now allows ranges in case statements; why not adapt D's switch statement for this? *checks the docs* We already have comma-separated cases, why not ranges in the form case a .. b (like Pascal did)? I think this would improve significantly on readability of data-driven program flow.Ranges would certanly allow cleaner looking code. Thomas -----BEGIN PGP SIGNATURE----- iD8DBQFCi9463w+/yD4P9tIRAsgxAKDCUVzGPtA0cO3HcPYhffmRJ3v3lgCeKI/0 jifpXgpONUGlVU6w6ApJ4t0= =NsTb -----END PGP SIGNATURE-----
May 18 2005
On Thu, 19 May 2005 02:30:51 +0200, Thomas Kuehne wrote:-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 James Dunne schrieb am Wed, 18 May 2005 21:55:47 +0000 (UTC): <snip> <snip> <snip>Though some ranges are not very intuitive ... char[] x; . . . switch (x) { case "abend" ... "define": . . . } would that be the same as ... if (x >= "abend" && x <= "define") { . . . } And would overlapping ranges be allowed? int x; . . . switch (x) { case 0 ... 5: . . . // dropthru case 3 ... 9: . . . break; default: . . . } -- Derek Melbourne, Australia 19/05/2005 9:18:30 AMWhat I'd really like to see is better switch statement case handling. The new ISOC99 standard for C now allows ranges in case statements; why not adapt D's switch statement for this? *checks the docs* We already have comma-separated cases, why not ranges in the form case a .. b (like Pascal did)? I think this would improve significantly on readability of data-driven program flow.Ranges would certanly allow cleaner looking code.
May 18 2005
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Derek Parnell schrieb am Thu, 19 May 2005 09:22:59 +1000:On Thu, 19 May 2005 02:30:51 +0200, Thomas Kuehne wrote:Where is the problem? (see std.string.cmp)James Dunne schrieb am Wed, 18 May 2005 21:55:47 +0000 (UTC): <snip> <snip> <snip>Though some ranges are not very intuitive ... char[] x; . . . switch (x) { case "abend" ... "define": . . . } would that be the same as ... if (x >= "abend" && x <= "define") { . . . }What I'd really like to see is better switch statement case handling. The new ISOC99 standard for C now allows ranges in case statements; why not adapt D's switch statement for this? *checks the docs* We already have comma-separated cases, why not ranges in the form case a .. b (like Pascal did)? I think this would improve significantly on readability of data-driven program flow.Ranges would certanly allow cleaner looking code.And would overlapping ranges be allowed? int x; . . . switch (x) { case 0 ... 5: . . . // dropthru case 3 ... 9: . . . break; default: . . . }That should be illegal, just like int x; switch(x){ case 2: .... case 2: ... } Thomas -----BEGIN PGP SIGNATURE----- iD8DBQFCi+zQ3w+/yD4P9tIRAiROAJ9odjosyZOUEhIccRTBBTbJm/F9RACgn1It 4fq8VvJ1cnLxFUQXsKzS8Jg= =Arzw -----END PGP SIGNATURE-----
May 18 2005
In article <d6gdl3$caf$1 digitaldaemon.com>, James Dunne says...I'm a bit skeptical...Skepticism is healthy.In article <d6bk3r$4t8$1 digitaldaemon.com>, Nod says...Indeed, I meant predominantly if-else statements. As you note yourself though, switch statements are not without flaws, and cannot handle all cases (pun not intended). And when switch statements are not enough, they often get if-else statements "bolted on" within, making the switch statement hard to read. With value-based overloading that is not possible, since the path selection logic is compressed to within the function parameters of the overloads.I agree that in the small scale of these examples, no tangible benefit can be found, but think bigger! I believe the benefit can be found when you have a large number of flow paths to select from. In the real world you often see large-to-huge if-else/select-case statements which are *horrible* to read and maintain. Yes, one *should* break it down and refactor, but who has the time? If it works, dont touch it, right? And thus, it keeps on growing. Wouldnt it be better to let the compiler handle the kludge?Large switch blocks aren't horrible looking. They were invented to clear up large blocks of repeated if-else definitions. Now *those* are barely readable. The only reason some repeated if-else blocks are used is because there is a lack of range-handling by case statements. Admittedly, switch statements can be made a bit more readable by providing each case block with its own curly-braced scope.#switch (mydata) {Yup, range handling in switch statements would be neat. Value-based overloading can also benefit from this, check out my draft proposal: http://www.digitalmars.com/drn-bin/wwwnews?digitalmars.D/23796Heh, no not with magical hand-waving :) I agree it will increase compiler complexity somewhat, but how much is really only for Walter to answer. If you think about it, the compiler must already be doing some of this for exception handling; e.g. multiple catch statements. NB: I have never written a compiler in my life, so I could be talking out of my ass.Take for example a token despatcher in a parser. They usually have the same boilerplate buildup: a large switch statement, and in each of the cases sits a function which handles the token or token class. So what is really going on here? Data values controls program flow into functions. Well, thats exactly what is suggested with pattern matching, only implicitly! The loop gets potentially reduced to this: :foreach (Token t; tokens) : handleToken(t); .. and the compiler should generate the code necessary to select the correct handleToken overload based on the token.With magical handwaving? No, with an effectively large compiler-generated switch statement! =P We've just shoved the work on down to the compiler - and I think our current compiler has enough on its plate to deal with =P.However, if the compiler were able to inline all of these value-based overloaded functions into the large generated switch statement, then I can see the gain in efficiency. That probably won't happen though, and would be a lot of work to implement in the compiler.This is not meant to *replace* if-else/switch-case statements. It should only be used where it actually *does* make the code cleaner, such as the examples presented. Also You are wrongly assuming I have a functional programming background. I'm all imperative, baby. :) Besides, this thing doesn't look *that* much like functional programming does it? I just think it fits well into D.Now thats *increasing* clarity, not reducing it.I can see how, in the limited number of examples proposed, that this would increase visual clarity - somewhat. It depends how you're trained to look at it. My eyes are trained to see switch statements calling functions based on data, and that's instantly recognizable to me - not having worked with any functional-based languages. I think you'll find this a pattern in C++ and C programmers moving over to D.If you keep the overloads physically together, which you should, it won't be worse than the corresponding i-e/s-c.Also, when using pattern matching, its the *function* which decides which cases it can handle, and not, as usually, the kludge. This is how it should be. If we were to, for example, split one of the token handler functions in two, we would have to change two places: both the kludge and the function(s). Not so if using pattern matching. Since the flow control is implicit, one simply have to update the function signatures to handle each of their own cases. Clean!Seems like a nightmare to try to maintain all the possible cases if the number of overloads gets up there. Tell me how this would work for a straight bytecode interpreter and how it could improve readability and efficiency.Hmm well, yes, but that's a bit more clutter. Speed isn't always paramount.As another example, a bit more real-world, take the pure Win32 Windows Message Loop (TM). I am sure most of you are familiar with it. The WML can be done in two ways, a bad one and a good one respectively: either handling things in a huge kludge as in the above example (I have seen too many of these) or, the better way is to use windowsx.h macros. These macros do nothing more than, calls a specific function for each message type. Exactly that could be accomplished by pattern matching based on function signatures, implicitly, and you would never have to touch the main loop.Why not make a table of function pointers to handle the messages? You then get O(1) lookup on which function to call, instead of O(n) (?) as in a switch statement. Then again, you lose the *possible* performance benefits of the compiler inlining your functions.Now that's just wrong. Complexity will not increase more than the corresponding i-e/s-c, and that complexity increases somewhere between O(N) and O(N + ln N) depending on the skill of the programmer :)Of course its not a panacea, but my point is, when used judiciously it could potentially make maintenance and refactoring easier. Other than that it would just be another tool in the chest.Having this support for n number of parameters increases the complexity exponentially, not to mention the amount of typing and high potential to introduce bugs when copy/pasting definitions.Agreed. I have switched.btw: I call the idiom "pattern matching" because thats what DavidM said it was called in functional programming, but maybe something like "value-based overloading" would be more appropriate in D context?Despite my objections above, I'd say that "value-based overloading" sounds better since we're not matching patterns. Then again, I truly hate terminology =P, especially when a term has no latin-based root in its name or any relevance to what it describes.I second that.Phew! That went on too long. -Nod-And I made it longer! =) What I'd really like to see is better switch statement case handling. The new ISOC99 standard for C now allows ranges in case statements; why not adapt D's switch statement for this? *checks the docs* We already have comma-separated cases, why not ranges in the form case a .. b (like Pascal did)? I think this would improve significantly on readability of data-driven program flow.Regards, James Dunne-Nod-
May 18 2005
In article <d6gjl5$hac$1 digitaldaemon.com>, Nod says...In article <d6gdl3$caf$1 digitaldaemon.com>, James Dunne says...And how! -- if anyone says "I'm a consumer whore" I'll be shocked...I'm a bit skeptical...Skepticism is healthy.Neither have I ever written a compiler, so we both could be conversing through our asses. =DIn article <d6bk3r$4t8$1 digitaldaemon.com>, Nod says...Indeed, I meant predominantly if-else statements. As you note yourself though, switch statements are not without flaws, and cannot handle all cases (pun not intended). And when switch statements are not enough, they often get if-else statements "bolted on" within, making the switch statement hard to read. With value-based overloading that is not possible, since the path selection logic is compressed to within the function parameters of the overloads.I agree that in the small scale of these examples, no tangible benefit can be found, but think bigger! I believe the benefit can be found when you have a large number of flow paths to select from. In the real world you often see large-to-huge if-else/select-case statements which are *horrible* to read and maintain. Yes, one *should* break it down and refactor, but who has the time? If it works, dont touch it, right? And thus, it keeps on growing. Wouldnt it be better to let the compiler handle the kludge?Large switch blocks aren't horrible looking. They were invented to clear up large blocks of repeated if-else definitions. Now *those* are barely readable. The only reason some repeated if-else blocks are used is because there is a lack of range-handling by case statements. Admittedly, switch statements can be made a bit more readable by providing each case block with its own curly-braced scope.#switch (mydata) {Yup, range handling in switch statements would be neat. Value-based overloading can also benefit from this, check out my draft proposal: http://www.digitalmars.com/drn-bin/wwwnews?digitalmars.D/23796Heh, no not with magical hand-waving :) I agree it will increase compiler complexity somewhat, but how much is really only for Walter to answer. If you think about it, the compiler must already be doing some of this for exception handling; e.g. multiple catch statements. NB: I have never written a compiler in my life, so I could be talking out of my ass.Take for example a token despatcher in a parser. They usually have the same boilerplate buildup: a large switch statement, and in each of the cases sits a function which handles the token or token class. So what is really going on here? Data values controls program flow into functions. Well, thats exactly what is suggested with pattern matching, only implicitly! The loop gets potentially reduced to this: :foreach (Token t; tokens) : handleToken(t); .. and the compiler should generate the code necessary to select the correct handleToken overload based on the token.With magical handwaving? No, with an effectively large compiler-generated switch statement! =P We've just shoved the work on down to the compiler - and I think our current compiler has enough on its plate to deal with =P.most other C-paradigm based languages is powerful enough as it is. I'd really need more concrete examples of the usefulness of this technique of value-based overloading for me to buy into it. As of right now, I can't think of any.However, if the compiler were able to inline all of these value-based overloaded functions into the large generated switch statement, then I can see the gain in efficiency. That probably won't happen though, and would be a lot of work to implement in the compiler.This is not meant to *replace* if-else/switch-case statements. It should only be used where it actually *does* make the code cleaner, such as the examples presented. Also You are wrongly assuming I have a functional programming background. I'm all imperative, baby. :) Besides, this thing doesn't look *that* much like functional programming does it? I just think it fits well into D.Now thats *increasing* clarity, not reducing it.I can see how, in the limited number of examples proposed, that this would increase visual clarity - somewhat. It depends how you're trained to look at it. My eyes are trained to see switch statements calling functions based on data, and that's instantly recognizable to me - not having worked with any functional-based languages. I think you'll find this a pattern in C++ and C programmers moving over to D.Ah! There's the guy I was lookin for: "if you keep the overloads physically together" :) Thanks. Admittedly, I've seen some terrible code written by terrible coders in my life, and assuming they would keep their code readable is a pretty rough assumption, albeit common sense to us "clean coders". =D I think that this value-based overloading would be cleaner looking if it were implemented syntactically like the contract system. An in {}, out {}, and body {}... simply extending this to something like: when (i in 0 .. 2) {}, when (j == 3), when (else) {}... or something less horrible looking. But you get the idea I hope.If you keep the overloads physically together, which you should, it won't be worse than the corresponding i-e/s-c.Also, when using pattern matching, its the *function* which decides which cases it can handle, and not, as usually, the kludge. This is how it should be. If we were to, for example, split one of the token handler functions in two, we would have to change two places: both the kludge and the function(s). Not so if using pattern matching. Since the flow control is implicit, one simply have to update the function signatures to handle each of their own cases. Clean!Seems like a nightmare to try to maintain all the possible cases if the number of overloads gets up there. Tell me how this would work for a straight bytecode interpreter and how it could improve readability and efficiency.I agree - not always; but it's a good idea to not totally ignore it :). Did someone say Java? *gun cock noise*Hmm well, yes, but that's a bit more clutter. Speed isn't always paramount.As another example, a bit more real-world, take the pure Win32 Windows Message Loop (TM). I am sure most of you are familiar with it. The WML can be done in two ways, a bad one and a good one respectively: either handling things in a huge kludge as in the above example (I have seen too many of these) or, the better way is to use windowsx.h macros. These macros do nothing more than, calls a specific function for each message type. Exactly that could be accomplished by pattern matching based on function signatures, implicitly, and you would never have to touch the main loop.Why not make a table of function pointers to handle the messages? You then get O(1) lookup on which function to call, instead of O(n) (?) as in a switch statement. Then again, you lose the *possible* performance benefits of the compiler inlining your functions.Ah damn. I've been found out. Admittedly that's where I was talking mostly out my ass. Sorry =P. Still, user-written O(1) with function pointer lookups looks much more pleasing than compiler-generated O(N) or O(N + log N) to me. Alas, to each his own.Now that's just wrong. Complexity will not increase more than the corresponding i-e/s-c, and that complexity increases somewhere between O(N) and O(N + ln N) depending on the skill of the programmer :)Of course its not a panacea, but my point is, when used judiciously it could potentially make maintenance and refactoring easier. Other than that it would just be another tool in the chest.Having this support for n number of parameters increases the complexity exponentially, not to mention the amount of typing and high potential to introduce bugs when copy/pasting definitions.Glad to know the relays have fired.Agreed. I have switched.btw: I call the idiom "pattern matching" because thats what DavidM said it was called in functional programming, but maybe something like "value-based overloading" would be more appropriate in D context?Despite my objections above, I'd say that "value-based overloading" sounds better since we're not matching patterns. Then again, I truly hate terminology =P, especially when a term has no latin-based root in its name or any relevance to what it describes.In response to multiple posts about the case statement range-handling, it should follow the C standard specifications regarding single-valued case statements: "[no two of the case constant expressions in the same switch statement shall have the same value]". I think that pretty clearly sums up overlapping ranges. If it doesn't, then they shouldn't be allowed. Also, I think ranges should only be allowed on numeric types, since overlapping ranges of strings cannot easily be detected, and honestly - doesn't make much sense to do so. switch (mystring) { case "ape" .. "zoo": break; case "boy" .. "cat": break; } Do those overlap? If we follow the strcmp guides I think they do, but you try determining if the ranges collide in a simple and fast algorithm that the compiler can use. Regards, James DunneI second that.Phew! That went on too long. -Nod-And I made it longer! =) What I'd really like to see is better switch statement case handling. The new ISOC99 standard for C now allows ranges in case statements; why not adapt D's switch statement for this? *checks the docs* We already have comma-separated cases, why not ranges in the form case a .. b (like Pascal did)? I think this would improve significantly on readability of data-driven program flow.Regards, James Dunne-Nod-
May 18 2005
In article <d6h6o9$11f9$1 digitaldaemon.com>, James Dunne says...They certainly are powerful enough. Goto is powerful enough. The rest of the control structures only make code more readable and manageable. As does this technique. Check out the latest part of the draft for some more examples: http://www.digitalmars.com/drn-bin/wwwnews?digitalmars.D/23966<snippety-snap-all-over>most other C-paradigm based languages is powerful enough as it is. I'd really need more concrete examples of the usefulness of this technique of value-based overloading for me to buy into it. As of right now, I can't think of any.Ah! There's the guy I was lookin for: "if you keep the overloads physically together" :) Thanks. Admittedly, I've seen some terrible code written by terrible coders in my life, and assuming they would keep their code readable is a pretty rough assumption, albeit common sense to us "clean coders". =DAgreed: bad coders suck. Nothing will help that fact.I think that this value-based overloading would be cleaner looking if it were implemented syntactically like the contract system. An in {}, out {}, and body {}... simply extending this to something like: when (i in 0 .. 2) {}, when (j == 3), when (else) {}... or something less horrible looking. But you get the idea I hope.That is certainly a possibility, but I think that keeping it with the parameters will also serve as a damper against overuse. With an extra block there's no limit to the amount of ranges you can throw in there.I agree - not always; but it's a good idea to not totally ignore it :). Did someone say Java? *gun cock noise*Nice conversing with ya :) -Nod-
May 19 2005
In article <d677hq$2nh7$1 digitaldaemon.com>, Antti says...This should be possible in D: inline int foo(0) { return 0; } int foo(int i) { return 10/i; } int main() { int i=0; int j; j=foo(i++); // if (i==0) j=0; else j=foo(i) j+=foo(i++); // if (i==0) j+=0; else j+=foo(i) return 0; }I can see this being practical to implement, as long as the "foo(0)" version was only called for literal 0 or compile-time-known zero values. In other words, you could optimize for some cases, but the "int i" version would need to be able to handle zero as well; thus the burden is not on the compiler, even not supporting the constant ones would be legal. This would allow compile-time optimization for certain more-common inputs (every function's got a few of them.) : boyer_moore_search(char[] data, char[] search_for) : { : // Heavy duty version that is faster for longer search strings. : } : : boyer_moore_search(char[] data, "\n") : { : // special optimized version for the known 'getline' like : // scenario which is 90% of the calls in this program. : } If you pass a variable for /search_for/, it could always call the first one, even if it is equal to "\n". Kevin
May 16 2005