Symbol/String Equality and the Cincom® ObjectStudio® LegacyCompiler

InObjectStudio versions prior to 8.0, a Symbol and a String with the same contents are considered equal (#Fred = ‘ Fred’). In Cincom® VisualWorks®, they are not (#Fred ~= ‘Fred’). In ObjectStudio 8.x, running on the VisualWorks foundation, that simple difference is dealt with by our usual mechanism for handling semantic differences between ObjectStudio and VisualWorks: in code compiled by the ObjectStudioCompiler, #= is known as a method that can behave differently in the two systems, so it is translated to send #os_Equal: instead. The implementations of os_Equal: ensure that backwards compatibility with ObjectStudio 7.x is maintained. But the story is not quite as simple as that.

ObjectStudio now uses the core Collection hierarchy from VisualWorks. That means that while (#Fred = ‘Fred’) answers true when compiled by the ObjectStudio compiler, (Dictionary new at: #Fred put: #Wilma; at: ‘Fred’ put: ‘Wilma’) will create a Dictionary with two elements. That is because Dictionary methods are compiled by the VisualWorks compiler, so they use traditional VisualWorks behavior.

You developers know it: there are countless decisions, large and small, that go into designing software. We had a lot of decisions to make when updating ObjectStudio to run on the VisualWorks foundation. As I’ve written before, in creating a major new version of ObjectStudio, we had many opportunities to make a break with the past, but one of our most fundamental design imperatives was that code from previous versions of ObjectStudio should run unchanged in ObjectStudio 8.x. In almost all cases, we found a way to stay compatible with the past. In this case though, the cost of complete compatibility was too high. The only ways to keep perfect compatibility with ObjectStudio 7 in this area would have been to override a very large number of methods in the Collection hierarchy, or to implement a parallel hierarchy for ObjectStudio. Ignoring the effort involved, those are not workable solutions because they would make it difficult or impossible for ObjectStudio code to interoperate with VisualWorks functionality that uses collections. There were also numerous bugs in the ObjectStudio classic implementation, mostly due to classes with inconsistent implementations of >>=.

For instance,

('11/11/11' asDate = '11/11/11')

answered true, but

('11/11/11' = '11/11/11' asDate)

answered false.

A patchwork solution might be possible, where some but not all of backwards compatibility problems caused by this change were addressed, but no set of choices in that direction would satisfy everyone, so we chose instead to take the path that #os_Equal: was maintained for individual object comparison, but Collection usage was based on #=.

There are several paths customers upgrading from previous versions can take here. One would be to implement or override certain collection methods. For instance, if changing code so that it does not depend on

{ #Barney } includes: 'Barney'

answering true would be unacceptable, it would be possible to implement os_includes: in SequencableCollection so that it does.

A more interesting option for some cases would be one available, upon request to Cincom Smalltalk Support, called the ObjectStudio LegacyCompiler. It actually came from a customer, one of the earliest upgraders to ObjectStudio 8.0. It takes a hint from the ObjectStudioCompiler, in that it has a list of method selectors that it is interested in, and when it encounters those selectors in code, it translates them to something else. For instance, #at:put: could be redirected to #legacy_at:put: instead, and you could implement Dictionary>>legacy_at:put: to check if the key is a Symbol, and if so, force it to be a String instead. Dictionary>>legacy_at: could then be written in the same way to mimic the old behavior.

The intention of the ObjectStudio LegacyCompiler is not to make sure that no code that uses collections in a way that is no longer supported ever has to be updated. Rather, it is something support can provide to

A) Help upgraders get over the hump to get code working easily

B) Help upgraders find the right places to change so their code no longer depends on a certain behavior, like Symbol/String equality.

Often it is not difficult to migrate code to deal with this new behavior. The problem is that the right places to modify are not easy to find. It’s not practical to search for senders of #at:put: and review them to see which have to change. The LegacyCompiler has a facility to log which method sent #legacy_at: and needed special handling to work properly. Armed with this information, customers know where to go to make the right change. It can’t handle every problem, but we hope it can be a useful tool if you need it.