Saturday, November 27, 2010
Wednesday, November 24, 2010
A couple things have happened between version 1 and the current version (4) that's in the Public Repository, based on feedback I received in various channels. One thing I haven't changed yet, is the method names #sortUp and #sortDown. It was proposed by one person that maybe these would better be called #ascending and #descending, so that code snippets read like
somePoints sort: #x ascending, #y descending
I'm torn. I think it reads better in context, but not as well in isolation. Which do you prefer better?
The things that have changed are:
- The Tests have been separated out into a separate package
- It was pointed out that you can't always count on a per element accessor being able to reduce to a collatable value. For example, what if you want to sort the names of customers, but based on a given collation algorithm.
customers asSortedCollection: [:a :b | (UnicodeCollationAlgorithm collate: a with: b mode: #shifted) < 0]
How would that dovetail with this approach? So I added support for two argument blocks. You can send sortUp/sortDown to them too. Under the hood now, the SortFunction is two argument collation block, and all roads lead to creating one of them.
- Because of that, you no longer need your flavor of Smalltalk to be able to do theSymbol value: trick.
- Since it all boils down to collation, I did add a new binary method: <=>. This is called the Spaceship operator in some language environments. So... you're Smalltalk flavor has to be able to support 3 character binary selectors for this to port.
- Despite seeming like a lot of changes, IIRC, the code actually ended up being smaller than the original.
Tuesday, November 23, 2010
The idea is to return a "map" of the original series from original values to some derived value. For example, maybe we have a series of Strings, and I want to create a map from those Strings to their md5sum values.
I can code this as
Dictionary withAll: (setOfStrings collect: [:each | each -> (Security.MD5 new hash: 'Hello World')])
but I really would rather capture this as a common method. What would you call it?
Monday, November 22, 2010
In particular, to express gratitude to all the long hard work that the Argentinian natives such as Emilio, Gabi, Hernan, Jorge, Esteban, Andres, Leandro, Alfonsina and other unsung heros who I'm missing. Your efforts made not only a really cool conference, but also made it possible for someone like me--who would have otherwise been stressed about other things--to really enjoy the conference, both its enthusiasm and technical content.
My first Smalltalk connected conference was OOPSLA 1994, held in Portland. I've been to a number of OOPSLAs, ESUGs, Smalltalks, Camp Smalltalks, and Smalltalk Solutions since then. Over the years, I've heard of lots of cool things people do WITH Smalltalk. Lots of ideas about how to best use it. But never, have I been to a conference, where there was so much talk about what to do TO Smalltalk. From Gilad's talk which pushes us to think about where we ought to go, especially with regards to modularity, to four (not 1, or even 2 or 3, but 4!) talks about original and innovative VM work (in some ways there was even more actually), to other talks such as the xTreams talk which pushes the idea of a completely brand new approach to streams (much more than just a refactoring). Just all and all a really cool conference.
Friday, November 19, 2010
So it's tempting to want to add a series of APIs like those we added at Key Technology. This was a sortedBy: method, and was implemented something like
^self sorted: [:a :b | (aSingleArgBlock value: a) < (aSingleArgBlock value: b)]
The problems with this approach are a couple.
First you end up paralleling any of the existing 2 arg block sorting APIs. So if you have sorted: method, you add the sortedBy: method, as above. And then when you have a sort: method, you have to add a sortBy: method. And when you have a reverseSort: method, you add a reverseSortBy: method. Ad continuum.
Second, it doesn't capture the direction of the sort at all. It assumes you want to sort ascending. For descending, you'll have to implement even more methods.
Third, it doesn't capture chained sorting patterns at all. For example, what if I want to sort customers by first their last name, then their first name. I've seen a couple sites, where they add yet more multi argument sorting methods to the system to capture stacks of sorting criterion.
I sat down last night, and with tests, put together a TAG-SortFunctions package, published in the Cincom Public Repository. The basic idea was to use an object. Time and time and time again over the years, I rediscover the principle "there's an object waiting to be birthed here!" What this package does in principle is note that sorting by 2 arg blocks, doesn't have to be limited to 2 arg blocks. It's really about the value:value: interface. And we can introduce an object type that stands in for the simple block which captures the failings listed above.
Here's some examples.
Sort customers by last name in descending order.
customers asSortedCollection: [:each | each lastName] sortDown
Sort customers by last name in ascending order, and then by first name in ascending order.
customers sorted: [:each | each lastName] sortUp , [:each | each firstName] sortUp
customers sorted: [:each | each lastName] sortUp , [:each | each firstName]
(the second sortUp is taken care of automatically)
We don't have to stick to blocks either, we can use symbols. Here's an example that sorts a list of points by their x value in ascending order, but their y value in descending order.
points sort: #x sortUp, #y sortDown
Some things to note.
First, we don't have to add any new sorting APIs to the system. Just use the existing ones. Second, direction is easy to specify. The magic is all in the #sortUp/#sortDown methods. Third, we can express chains of sorting criterion by concatenating them with the , method.
If this is useful to others, I'll make it more usable, by pulling the tests out of the package so it doesn't need SUnitToo. And you can lobby Cincom to include it in VisualWorks. Or port it to your own flavor of Smalltalk. I did my best to write it in such a way that it would work in Squeak or Gemstone, or whatever.