This post originated from an RSS feed registered with Agile Buzz
by James Robertson.
Original Post: The Devil's Set in the Details
Feed Title: Travis Griggs - Blog
Feed URL: http://www.cincomsmalltalk.com/rssBlog/travis-rss.xml
Feed Description: This TAG Line is Extra
Apparently. But boy, it was close. Switching sets to use something other than nil for the "empty slot" is an interesting proposition. If this were say, the GNUstep objective C libraries, we'd rewrite the library, and recompile it. But Smalltalk is a live system. Generally, rewriting the live Smalltalk system is OK, until you start changing the fundamental machinery that it itself uses to get things done. So... this represents a fun challenge.
Overview
Here's the basic strategy we'll used to try to lobotomize the Set implementation.
Create a new object for the EmptySlot
Rewrite Set and subclasses so that slots can be nil OR our new EmptySlot
Cause new empty slot stores to use EmptySlot instead of nil
Convert all existing nil slots into EmptySlot
Remove the or nil clauses from Set and subclasses
1. Create EmptySlot
This is an easy step, we just add a new Shared Variable to Set:
Then we make sure we initialize it, because otherwise it'll be nil, and that will kinda defeat the whole purpose, huh? (aside, wouldn't it be nice if shares just were initialized always, like say, lazily).
2. Rewrite Set and subclasses
At first blush this looked scary. There are many == nil sites in Set and its many subclasses. But then we remembered "Have Rewrite Tool, Will Travel". It turns out that the nil comparisons appear to come in just a few flavors. Most are of the form someStatement == nil. So we select Set and all of its subclasses in Hierarchy mode, and select all packages. Go to the rewrite tool and run the following rewrite operations
Basically, we have two things here, new Sets need to be populated with EmptySlots immediately. And any place we're storing nil should be replaced with an EmptySlot store. The first is accomplisehd by reimplementing Set's setTally method. setTally is to Set what intialize is to most other objects, I've still never figured out why the couldn't just use initialize.
setTally
1 to: self basicSize do: [:n | self basictAt: n put: EmptySlot].
tally := 0
The other part is handled with another application of the rewrite tool.
Search for:
Repace With:
`@recv basicAt: `@key put: nil
`@recv basicAt: `@key put: EmptySlot
New sets should just have EmptySlots in them instead of nil's now.
4. Replace existing nil slots with EmptySlots
Here we use the following workspace snippet:
Set allGeneralInstancesDo:
[:each |
1 to: each basicSize
do: [:n | (each basicAt: n) ifNil: [each basicAt: n put: Set.EmptySlot]]]
Somewhere in that step... out story comes to a tragic end. We were so close! You get an SubscriptOutOfBounds error. At least the system didn't just instantly dissapear. In fact you can still browse around and all kinds of things. But... you can't compile anymore. Somewhere in HintedCodeStream, something is amuck that prevents it from properly emitting literals. Yeck. I'd love to know what little detail went wrong here. I'm throwing the gauntlent down here. Can you figure out how to flip the system to use EmptySlots?