Mailing List Archive: 49091 messages
  • Home
  • Script library
  • AltME Archive
  • Mailing list
  • Articles Index
  • Site search
 

[REBOL] TECHNICAL ESSAY/CHALLENGE(s) - "Expression Based"?

From: joel::neely::fedex::com at: 2-Oct-2000 23:59

It's late; I'll try to be brief (for me ;-). DEFINITION: Language geeks use the term "referential transparency" to mean that things that have the same value are freely interchangeable. This property of a programming language is usually held to be A Good Thing, as it keeps down the number of exceptions one has to learn to be able to use the language. REBOL seems to break referential transparency in several places. I'm very interested in minimizing the effects of this. INTENT: What can we do with expressions? One important thing is that they let us describe a pattern of computation which can be re-used easily (either by substituting a new value in a copy of the expression, or by changing a variable's value and re-evaluating a copy of the same expression). Most programming languages use expressions in places where the designer expects us (the programmers) to want to change things; most programming languages avoid expressions in places where the designer expects us (the programmers) NOT to want to change things (or where the designer doesn't want us to do so). All else being equal, we'd expect an "Expression Based" language to be very generous about where we are allowed to use expressions. Sometimes REBOL breaks that expectation. EXAMPLE (FOR SAKE OF ILLUSTRATION ONLY): Suppose I want to take a bunch of (natural) numbers and separately total the even and odd members of the bunch. One way to do this is as follows...
>> bunchanums: [3 1 35 8 4 5 52 42 19 13 32 43 81 2 6 34 46]
== [3 1 35 8 4 5 52 42 19 13 32 43 81 2 6 34 46]
>> tally: make object! [
[ tot: 0 [ zero: does [tot: 0] [ up: func [/by n [number!]] [tot: tot + either by [n] [1]] [ down: func [/by n [number!]] [tot: tot - either by [n] [1]] [ now?: does [tot] [ ] We now have a nice reusable concept, which we can apply thus...
>> use [evens odds] [
[ evens: make tally [] [ odds: make tally [] [ foreach num bunchanums [ [ either even? num [evens/up/by num] [odds/up/by num] [ ] [ print ["evens:" evens/now? " odds:" odds/now?] [ ] evens: 226 odds: 200 There's something ugly inside that loop (don't be fooled by the brevity or triviality of this example!) We really want to invoke the same method and arguments to whichever tally object we select for each number in the bunch. In other words, what varies is the object, not the method or argument(s). Having to repeat method names and argument lists between the two alternatives is tedious, error-prone, and a potential maintenance landmine. (Imagine how this gets progressively worse with more than two alternatives!) We might try to work around that by...
>> use [evens odds] [
[ evens: make tally [] [ odds: make tally [] [ foreach num bunchanums [ [ use [whichone] [ [ whichone: either even? num [evens] [odds] [ whichone/up/by num [ ] [ ] [ print ["evens:" evens/now? " odds:" odds/now?] [ ] evens: 226 odds: 200 Well, this solves the duplication problem, but at the cost of another ugliness: we need a whole new name just to remember the object we've selected long enough to make a method call against it! The observant wizards among us will notice that I blew right past...
>> use [evens odds] [
[ evens: make tally [] [ odds: make tally [] [ foreach num bunchanums [ [ use [whichmeth] [ [ whichmeth: get in either even? num [evens] [odds] 'up [ whichmeth/by num [ ] [ ] [ print ["evens:" evens/now? " odds:" odds/now?] [ ] evens: 226 odds: 200 ...as it is even more obscure (but we'll revisit it later). What would be nice here would be to invoke a method call against an object which is the value of AN EXPRESSION... (*WARNING* *HYPOTHETICAL SYNTAX FOLLOWS*) use [evens odds] [ evens: make tally [] odds: make tally [] foreach num bunchanums [ (either even? num [evens] [odds])/up/by num ] print ["evens:" evens/now? " odds:" odds/now?] ] ...but, alas, that isn't REBOL anymore! How do we get rid of that pesky name? How about...
>> use [evens odds] [
[ evens: make tally [] [ odds: make tally [] [ foreach num bunchanums [ [ do reduce [ [ make path! reduce [ [ either even? num ['evens] ['odds] [ 'up 'by [ ] [ num [ ] [ ] [ print ["evens:" evens/now? " odds:" odds/now?] [ ] evens: 226 odds: 200 Yowch! We got rid of the name, but at what cost!?!?! In addition to being severely obscure, the loop body doesn't scale well AT ALL! (For example, suppose we were fetching the object out of a data structure -- e.g., via select or indexing into an array -- and didn't even have a name for it.) Thus we come to... CHALLENGE #1: Given an object (which is determined as the value of an expression, not by having "it's name" in hand), how can we call a predetermined method of that object, complete with argument(s) and refinement(s)? In other words, the object may vary, but the function and arguments will not. VARIATION ON THE THEME: Suppose we want to tally up the sum of the even numbers less the sum of the odd numbers in our previous bunch. Using our handy- dandy Acme tally object, we can write...
>> use [diffs] [
[ diffs: make tally [] [ foreach num bunchanums [ [ either even? num [diffs/up/by num] [diffs/down/by num] [ ] [ print ["net sum:" diffs/now?] [ ] net sum: 26 There's that duplication rearing its ugly head again! Now the only thing that changes -- that we need to compute -- is which method to call, and NOT which object in which to call it nor the argument(s). We can fall back to using a temporary name...
>> use [diffs] [
[ diffs: make tally [] [ foreach num bunchanums [ [ use [whichfunc] [ [ whichfunc: either even? num ['up] ['down] [ diffs/:whichfunc/by num [ ] [ ] [ print ["net sum:" diffs/now?] [ ] net sum: 26 ...or use the pseudo-function-reference trick from previously...
>> use [diffs] [
[ diffs: make tally [] [ foreach num bunchanums [ [ use [whichmeth] [ [ whichmeth: get in diffs either even? num ['up] ['down] [ whichmeth/by num [ ] [ ] [ print ["net sum:" diffs/now?] [ ] net sum: 26 ...but these still seem awkward and unnatural, IMHO. What we're really trying to express is simply... (*WARNING* *HYPOTHETICAL SYNTAX FOLLOWS*) use [diffs] [ diffs: make tally [] foreach num bunchanums [ diffs/(either even? num [up] [down])/by num ] print ["net sum:" diffs/now?] ] ...give or take a couple of ticks. ;-) Thus we close with... CHALLENGE #2: Given a specific object in hand, how can we invoke one of its methods (which one is determined by an evaluation) with a given list of arguement(s)/refinement(s)? In other words, the choice of function is varying, but the object and arguments are not. THANKS... ...in advance to all who give this little matter some thought and post their ideas! -jn-