Postings tagged with Smalltalk/Squeak
Error handling (or god forbid, exception handling) is one of the weak parts in Squeak. A great deal of course comes from its dynamically typed nature. However, the greater impact probably comes from the utter lack of guard clauses in most core librares I've seen so far: Objects will happily accept any kind of parameter and continue to wrack havoc on them until the debugger, at some later point (IF we are lucky), stops the travesty.
Let's move on to BitBlits, where the lack of parameter checking and the absence of proper initialization quickly crashes your VM. Why use BitBlits at all? Because Morphics are crap. More precisely, they feature:
- poor performance (I guess all the redrawing with overlapping Morphic instances doesn't come for free),
- a hideously complex class hierarchy
- undocumented models (say, the model for PluggableListMorph)
- a hard-to-access event loop (read: no ready-made support for signals & slots),
- a complex API (only for the base class Morph itself "Morph methodDictionary size" returns 1165 methods, yikes).
Naturally, while using BitBlits or Forms most work is actually done by Squeak primitives, that is, external C code that is called by some Squeak wrappers. At this point proper argument checking becomes paramount, or else:
Division by zero (3.10, win32/linux)
| blit form | form := Form new. "form setExtent: 0@0 depth: 32." blit := (BitBlt destForm: form sourceForm: nil halftoneForm: nil combinationRule: Form blend destOrigin: 0@0 sourceOrigin: 0@0 extent: 0@0 clipRect: (0@0 extent: 0@0)). blit copyBits.
Note the out-commented line, which would prevent the VM crash. Appearantly the form's depth isn't initialized. The funny part starts when you replace "destForm: form" with "destForm: Object new", which triggers the proper error handling and prevents the VM crash as well.
The magic bitmap (3.10, linux-only)
picture := Form fromBMPFileNamed: 'bigbitmap.bmp'.
The comment at the top of this method reads "deprecated". Sadly, bitmaps are much faster than any compressed image format, even after the initial reading (oops?). More importantly though is that we have different behaviour (crash/no crash) depending on the platform where this is executed. Wait a moment - a VM? Platform-dependant? Actually, this is not the only example, many Form methods will not work with the Linux VM. But instead of telling you, they'll happily crash the VM, too.
Oh well, at least Squeak is good when it comes to runtime optimizations, like any other VM. And loop unrolling is so basic it is hardly worth to test it. Or is it?
Let's try a simple loop, with the inner loop manually unrolled (add:to: adds arg2 to arg1 and stores the result in a member):
MessageTally spyOn: [ (1 to: 10000) do: [:unused | self add: 1 to: 1. self add: 2 to: 2. self add: 3 to: 3. ]. ].
This executes in 4ms on my machine.
OK, now outer loop & inner loop:
MessageTally spyOn: [ (1 to: 10000) do: [:unused | (1 to: 3) do: [:idx | self add: idx to: idx. ]. ]. ].
WTF?! 17ms? Well, one quick optimization is to store the range (interval) of the inner loop like so: innerLoop := 1 to: 3.
The code then becomes:
| innerLoop | MessageTally spyOn: [ innerLoop := 1 to: 3. (1 to: 10000) do: [:unused | (innerLoop) do: [:idx | self add: idx to: idx. ]. ]. ].
+1 for everything (but variables) is an object, -1 for code obfuscation, +1 for getting execution time down to 12ms. But still. WTF?!? Does the code really spend 8 precious milliseconds in the loop variable handling (with other words, 67% of the execution time)?
(My personal) conclusion: Squeak fails at getting basic stuff right. The things it gets right (reflection/introspection, fast development round-trips) are fantastic! But it suffers from a big NIH syndrome so that instead of wrapping useful and established libraries you are left alone with an incomprehensible "self-documenting" class library. Also, wrapping C libraries using the foreign function interface seemingly requires recompilation of the VM ... platform independence, anyone?
I'd welcome comments correcting my perception problems with Squeak!
For some time now I've been busy with Squeak. It's nice to get a chance to work with Smalltalk which is one of the most consistent languages I've ever seen. This probably doesn't mean much - I haven't seen that many languages yet. Of course consistency comes with a price but I can appreciate the idea. And yes, I think "Everything is an object" and Garbage Collection are great concepts. The fomer allows for amazing introspection features out-of-the-box. And everyone that is still complaining about the latter should take a deep breath and read this paper about the G1 GC that will be "the long-term replacement for HotSpot's low-latency Concurrent Mark-Sweep GC". Memory management can't get much faster than that.
But Squeak isn't Java. Nor is it using the JVM as other projects do. This is partially because Squeak is dynamically typed and the JVM does not work for such environments (yet). What it means though is that you get a VM which vastly lags behind the current development. Also, dynamic typing is really not what I want, for a couple of reasons. Sure, I want local auto variables that take whatever type is necessary given their first assignment (and keep that type), but that's type inference! On the other hand, I don't what method parameters that can be everything. How are you supposed to write robust code unless you start to check every parameter for its type during run-time? And even if you do (ignoring the performance penalty), wouldn't that be a very strong hint that dynamic typing doesn't work well for method parameters?
In Squeak for example you have this "self changed: aParameter" method for every Object, which is called to indicate that your object has changed. What it does is to inform a list of observers (dependents in Smalltalk MVC speak), supplying aParameter to them. Now, everybody thinks differently about what aParameter should be. A symbol (think string literal)? A reference to self? Perhaps a code block (for the lack of callbacks)? There is simply no right or wrong here, the interface doesn't tell you! And so it happens that you find the changed-method (ab-)used in every possible way, destroying quite a bit of the MVC pattern's flexibility since you have to guess which kind of changed information you might get from any given model.
The way I see it, Squeak is "just" a class library around a "reflexive" Smalltalk80 implementation. And what is - right after the API design itself - the most important part of a class library? I am sure Squeakers won't be able to come up with the correct answer themselves so I help a bit: API documentation and example code.
With Squeak, you often only have "example" code (since "The Code is the Documentation."), or rather, class library code with the quality of example code, if you catch my drift. Seriously, forget it, I won't keep reading library code that lacks comments or any kind of explainations. Am I supposed to guess from the awesome code itself what parameter types are allowed? Or what to expect as the result? Even better, error handling? Well, the answer to the last one is easy. There is no error handling. At some point during the code execution the debugger will tell me that we tripped over the Squeak equivalent of a nullpointer exception. And this is library code!
OK, enough of this rambling - for now! I even left out the obvious things like missing SCM integration (no, Monticello is a joke) or this image-based development crazyness (guess why everyone keeps a clean image as backup?).
My next post will cover some gruesome examples (also code) which are likely to make you squeak.