This is mostly a note to myself, because these little things can cost you some hours, even though you clearly know about all the pieces of the puzzle and simply forget to use the right peek statement at the right time.
So what’s cost me hours today? I had weird results in my Seaside Apps where components shouldn’t be displayed and – even more important – not ask non-existant objects for data. But my whole problem wasn’t related to Seaside or our internal web framework at all. What we have implemented is a method for certain containers to decide whether they get rendered based on the value of a Block that’s evaluated at render time. Only if the Block returns true, the component would be rendered, and only then the underlying business object would be asked for its data.
Example: Only if a Customer has an Address object, the address FieldSet would render itself and ask the address instance for its street, zip code and so on. This is not only for optical reasons but also due to the fact that a non-existent address instance (aka nil) doesn’t understand #street, #zip or city.
So what I had was a Block that said
myComponent visible: [self customer address notNil] ...
(Note that visible: is not a method of Seaside, it’s in our own Framework, and also not that Seaside is not relevant here).
I always got errors when I rendered the component for customers without addresses, even though the Block was evaluated before rendering of the component would be started.
Interestingly, when I set a Breakpoint and inspected the result of
self customer address I got nil. And asking that nil object in the inspector whether it is notNil always gave the right answer. But obviously, when the code ran, it answered true, as if there were two kinds of nil.
So what was going on?
After some minutes of being a bit puzzled, I came to the conclusion this must be something related to GLORP’s Proxies, because the Customer objects were read from the database.
Some experimentation showed that replacing
~= nil worked like a charm.
Being a Smalltalker, the first idea was to see if Glorp’s Proxy did something wrong. Normally, sending a message to a GLORP Proxy works as expected: the proxy forwards a message to the result of
self getValue. So I tried setting a conditional Breakpoint into Proxy>>#doesNotUnderstand: , that only fires if the Message’s selector is #notNil. Unsurprisingly, the Breakpoint never fired. Even when I had a Breakpoint immediately before #notNil is sent to the Proxy, I could see that it the block was evaluated, but it seemed the #notNil message never reached the Proxy.
This was the moment when all of a sudden I remembered that #isNil and #notNil are optimized in VA Smalltalk, meaning these message are never really sent to the receiver. The VM checks for nil in a primitive rather than calling the methods #isNil and #notNil. Even though these two methods can be found in the Image, and the implementations are correct, they are just there for documentation purposes. You can put halts and breakpoints into them as many as you want, the methods never get sent.
This also explains the differences between the behavior when inspecting
self customer address and sending notNil to the resulting nil object and having the method just run. In the inspector case, the inspect would cause the proxy to execeute getValue and I’d be sending notNil to the resulting nil object, while in the method run case the receiver of #notNil would be the Proxy, that is not nil and will always answer false to #notNil (because it never gets the chance to forward the #notNil message to the real object).
So, still being a Smalltalker, you might think that situation is easy to solve, because you can alwas implement #isNil and #notNil in Proxy. But if you’ve read the last paragrapg carefully, you’ll understand that there’s no use in doing so. Because the new implementation would never be called.
So if you (me) are (am) working with GLORP and need to check for nil or not nil, there are mainly two workarounds (none of them elegant or nice, but they work):
- send #= nil or #~= nil
- send #getValue to an object before sending #isNil/#notNil
You may be bored by this, because you think it is old hat. And you’re right, it is. But I hope I won’t forget it again now that I’ve written it down 😉
[Update] As Alan mentions in a comment to this post, newer versions of GLORP do not create a Proxy if it’s obvious that the related object will be nil, but set the instance variable to nil instead. So with newer version of VA Smalltalk that will ship with a port of a newer Glorp version, using #notNil / #isNil will work as expected [/Update]