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

[REBOL] Re: Fun with literal blocks!

From: joel:neely:fedex at: 2-Nov-2000 13:26

Hi, Rishi, Rishi Oswal wrote:
> ok. I've tried to ignore this but I have seen it referenced one > too many times. So...what is the "GC" bug? Does GC stand for > "Garbage Collector?" >
Yes. Sometimes the baby gets thrown out with the bath-water (to mix metaphors ... ;-) Here's a specific recipe for one way to invoke it: make-bump: func [n [number!]] [ do func [nn [number!]] [ func [m [number!]] [m + nn] ] n ]
>> onemore: make-bump 1 >> onemore 5 == 6 >> onemore 9 == 10 >> twomore: make-bump 2 >> twomore 5 == 7 >> twomore 9 == 11 >> onemore 5 == 6 >> recycle >> onemore 5
at which point REBOL goes to South Dakota, leaving no forwarding address... Now you may ask yourself, "Self, why would *anybody* write such an odd-looking bit of code to begin with?" [ STOP READING HERE IF YOU DON'T CARE ] Well, Self, there are several reasons, one of which is In an attempt to create a "closure", which can be used as a "specialized" function to minimize argument-passing. For example: Tennessee sales tax uses two rates, "state" and "local", with a cap on how much of a "single article" price is to be taxed at the local rate. The state portion applies no matter how high the single-article price. So, we can write sales-tax: func [ stt [decimal!] loc [decimal!] cap [money!] amt [money!] ][ (stt * amt) + (loc * min amt cap) ]
>> sales-tax 0.05 0.02 $100.00 $100.00 == $7.00 >> sales-tax 0.05 0.02 $100.00 $200.00 == $12.00
We can see that 7% (state plus local) was applied only to the first $100, and 5% (state only) was applied to any amount above that cap. Instead of sprinkling four-argument functions throughout our code, we would like to encapsulate the rate data into some special-purpose functions, which can be retrieved and used as appropriate. Our first attempt to do this might be... make-sales-tax: func [ srate [decimal!] lrate [decimal!] llim [money!] ][ func [amt [money!]] [ (srate * amt) + (lrate * min amt llim) ] ] Make-sales-tax just creates and returns a function that does needs only the amount; it uses the rate and limit information from make-sales-tax...(we might think!) Gotham-sales-tax: make-sales-tax 0.05 0.03 $100.00 The "function factory" gives us a function that now has those values plugged in, so the correct calculations are done...
>> Gotham-sales-tax $100.00 == $8.00 >> Gotham-sales-tax $200.00 == $13.00
That worked so well, that we'll try it again... Smallville-sales-tax: make-sales-tax 0.05 0.01 $100
>> Smallville-sales-tax $100.00 == $6.00 >> Smallville-sales-tax $200.00 == $11.00 >> Gotham-sales-tax $220.00 == $12.00
Whoops! How come the Gotham sales tax is less on $220 than on $200??? It's because the Srate, Lrate, and Llim inside any function from the function factory are actually in the context of Make-sales-tax. Changing one changes them all. One way around this (we might think...) is to ensure that a new context is established each time we call on the factory to give us a new sales tax function. Since function contexts are (apparently) constructed when the function is *defined* (not for each invocation), we can try defining and immediately invoking a function-defining function inside the function factory. (What a tongue-twister!) sales-tax-factory: func [ srate [decimal!] lrate [decimal!] llim [money!] ][ do func [stt [decimal!] loc [decimal!] cap [money!]] [ func [amt [money!]] [ (stt * amt) + (loc * min amt cap) ] ] srate lrate llim ]
>> Bigtown-sales-tax: sales-tax-factory 0.05 0.02 $100.00 >> Bigtown-sales-tax $100.00 == $7.00 >> Bigtown-sales-tax $200.00 == $12.00
So far, so good...
>> Smalltown-sales-tax: sales-tax-factory 0.05 0.01 $100.00 >> Smalltown-sales-tax $100.00 == $6.00 >> Smalltown-sales-tax $200.00 == $11.00 >> Bigtown-sales-tax $200.00 == $12.00
Hooray! It works -- until the next garbage collection, that is!
>> recycle >> Bigtown-sales-tax $150.00 Kaboom!
It appears that GC is concluding that the "hidden" function context is no longer in use and is wiping it out. That's only an inference on my part, but the above behavior is reliably reproducible. Closures are handy. We should be able to use them (and some other higher-order-function techniques) once GC stops being so greedy. -jn- -- ; Joel Neely [joel--neely--fedex--com] 901-263-4460 38017/HKA/9677 REBOL [] foreach [order string] sort/skip reduce [ true "!" false head reverse "rekcah" none "REBOL " prin "Just " "another " ] 2 [prin string] print ""