tag:blogger.com,1999:blog-75153068759060428282024-03-12T21:10:50.367-07:00ObjologyTravis Griggshttp://www.blogger.com/profile/01599271142862167244noreply@blogger.comBlogger39125tag:blogger.com,1999:blog-7515306875906042828.post-68897032942867686432016-09-21T11:55:00.001-07:002016-09-21T11:55:57.944-07:00Two Years Later...So nearly two years later, I've stuck with it. I am not a Swift expert, but I no longer use Objective-C. I have written a small app in Swift. And rewrote my much larger app in 100% Swift. Plus also written a few small test/demo apps.<div>
<br /></div>
<div>
Here's a recap on my original observations:</div>
<div>
<br /></div>
<div>
<span style="font-size: medium;"><b>Out of Date Information</b> - This remains an issue. But seems to burn me less lately. I've learned to always look in the newly integrated doc viewer first. Some, but not all, StackOverflow posts get updated to match Swift3. The mailing list is always current. I make sure if I ask a question on SO, that I make it clear I'm doing Swift3.</span></div>
<div>
<span style="font-size: medium;"><br /></span></div>
<span style="font-size: medium;"><span style="font-size: medium;"><b>Replacing one Legacy with Another</b> - Apple's aggressive integration makes this a lot less than I expected. The fact that most APIs follow the Swift naming conventions instead of being obviously converted from keyword style naming makes it seem less legacy already. The aggressive attitude of replacing oft used types with bridged variants also makes it seem legacy (e.g. Date replacing NSDate). My apps need no @objc qualifiers, so the need to have legacy rubbed in my face is obviously disappearing quickly.</span></span><br />
<span style="font-size: medium;"><span style="font-size: medium;"><br /></span></span>
<span style="font-size: medium;"><span style="font-size: medium;"><span style="font-size: medium;"><b>Head Turning Paradigm</b> - The human brain can adapt to some things pretty quickly. This appears to have been one. In fact, I actually prefer the "type post cedes" rather than "type precedes" pattern. I still do quite a bit of C, so it's not just me moving on. I have to do both approaches regularly, and I actually like this. I've even begun to use the optional typing in Python which follows the same notation.</span></span></span><br />
<span style="font-size: medium;"><span style="font-size: medium;"><span style="font-size: medium;"><br /></span></span></span>
<span style="font-size: medium;"><span style="font-size: medium;"><span style="font-size: medium;"><span style="font-size: medium;"><b>:, :, and :</b> - This has turned out to not bother me much. I don't really notice it in fact. The dictionary thing never shows up much. The tools quickly correct me if I get a dictionary/array mismatched. The other two uses just seem natural now. It still can seem a lot of typing (especially now that default first arguments have been deprecated), but the auto completer keeps me from noticing how much I spell things out.</span></span></span></span><br />
<span style="font-size: medium;"><span style="font-size: medium;"><span style="font-size: medium;"><span style="font-size: medium;"><br /></span></span></span></span>
<span style="font-size: medium;"><span style="font-size: medium;"><span style="font-size: medium;"><span style="font-size: medium;"><span style="font-size: medium;"><b>Wrap, Unwrap, Ugh</b> - Doesn't bother me at all anymore. Especially once I figured out how to extend Optional with ifNil and ifNotNil methods. Sometimes I get a bit tired of nested if let... statements.</span></span></span></span></span><br />
<span style="font-size: medium;"><span style="font-size: medium;"><span style="font-size: medium;"><span style="font-size: medium;"><span style="font-size: medium;"><br /></span></span></span></span></span>
<span style="font-size: medium;"><span style="font-size: medium;"><span style="font-size: medium;"><span style="font-size: medium;"><span style="font-size: medium;"><span style="font-size: medium;"><b>Refactoring</b> - Still miss this a lot. But lets face it, I don't have much refactoring support in my C or Python stuff.</span></span></span></span></span></span><br />
<span style="font-size: medium;"><span style="font-size: medium;"><span style="font-size: medium;"><span style="font-size: medium;"><span style="font-size: medium;"><span style="font-size: medium;"><br /></span></span></span></span></span></span>
At two years in, I like Swift. As an older more polyglotted developer, I don't love it like I did other systems in my younger more idealistic years, but it works for me. I often think "this is like a C++ that I can handle." I still detest C++. If I have to choose between Java and Swift, I'd take Swift in a heartbeat. In fact, I even have a slight preference for Swift over Python.<br />
<span style="font-size: medium;"><span style="font-size: medium;"><span style="font-size: medium;"><span style="font-size: medium;"><span style="font-size: medium;"><br /></span></span></span></span></span>
<span style="font-size: medium;"><span style="font-size: medium;"><span style="font-size: medium;"><span style="font-size: medium;"><span style="font-size: medium;"><br /></span></span></span></span></span>
<span style="font-size: medium;"><span style="font-size: medium;"><span style="font-size: medium;"><span style="font-size: medium;"><br /></span></span></span></span>
<span style="font-size: medium;"><span style="font-size: medium;"><span style="font-size: medium;"><span style="font-size: medium;"><br /></span></span></span></span>
<span style="font-size: medium;"><span style="font-size: medium;"><br /></span></span>
<span style="font-size: medium;"><span style="font-size: medium;"><br /></span></span>Travis Griggshttp://www.blogger.com/profile/01599271142862167244noreply@blogger.com1tag:blogger.com,1999:blog-7515306875906042828.post-58288533862723380822014-10-06T15:50:00.002-07:002014-10-06T15:51:38.468-07:00Initial Thoughts about SwiftBack in June, Apple announced the new language platform, Swift. I took a brief look at it and then put it aside for the time being. I figured the early adopters could vet things out without me, run around in circles of hysteria for a bit.<br />
<br />
Fast forward a few months, iOS 8 and Xcode 6 are both official now, no longer beta. I have a prototype app written in Objective-C, that it's time to kind of do version 2 of. Is now a good time to try and grab hold of the train and jump on? I don't know yet. I'm trying to keep an open mind. Here's a couple of thoughts so far...<br />
<br />
<b><span style="font-size: large;">
Out of Date Information</span></b><br />
<div>
One of the most difficult bits, is that a lot of the information out there on the web is already slightly (or very) obsolete. Much of the initial traffic generated after the announcement was quick from-the-cuff responses, often just there to generate clicks. Looking for really useful and helpful content, one still must filter through a lot of noise. Hopefully, with time, the ratio will tip in favor of the more useful.</div>
<div>
<br /></div>
<div>
I found this to be very true with the first batch of tutorials that were quick to hit the streets. Most are just so flat out trite, they don't really teach you anything. An app that is a little more than "Hello World" doesn't really teach you much. Furthermore, many of them have issues in their purported source code that prevent you from finishing if you can't figure out what's changed (a common example I saw was issues with the optionals mechanism).</div>
<div>
<br /></div>
<div>
In the end, I found a Tetris game tutorial that I was actually able to complete (<a href="https://www.bloc.io/swiftris-build-your-first-ios-game-with-swift" target="_blank">Swiftris</a>). As a tutorial it's OK, it has a fun flippant style, and it actually goes through a bit of stuff. And it distinguished itself by not being overly trivial and actually producing a working program. Most of the following thoughts were generated from this tutorial, as well as reading quite a bit of the <a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-XID_454" target="_blank">Apple Swift Docs</a>.</div>
<br />
<b><span style="font-size: large;">
Replacing one Legacy with Another</span></b><br />
Back when Craig Federighi announced Swift, one of the catch phrases was "Objective C without the C". Computer language historians will describe Objective-C as a somewhat unholy union of two very different languages: Smalltalk and C. While somewhat effective, it's a weird experience. Being accomplished at both Smalltalk and C, I can personally attest to Objective-C's weirdness. I often giggle when coding Objective-C. In an ideal world, you'd be thrilled at being able to leverage the best of both worlds. But it's usually the case that each is holding the other back in amusing or annoying ways.<br />
<br />
So supposedly Apple ditched the C. It's a typed Object Oriented language with a syntax that us more C like than the Smalltalk keyword style. In the end, I'd say they ditched both, and it's just its own language.<br />
<br />
But like Objective-C which always had to play fiddle to its C heritage, Swift plays fiddle too. You may see posts pitching things as better or innovative, but what I perceive often is "since we're still using the Cocoa runtime, we had to come up with something." So while C is gone. The Cocoa libraries and runtime are not. And ultimately, Swift has to bend to fit that model. Just like with Objective-C where I would giggle at how C would force something silly into the marriage, I find myself asking "why did they do that??" and usually the answer is "ah, because the Cocoa runtime forced their hand there."<br />
<br />
The Law of Conservation of Ugly wins again.<br />
<br />
<b><span style="font-size: large;">
Head Turning Paradigm</span></b><br />
One of the things that messed with me at first, is that Swift function signatures are backwards. In C, a function signature/definition might look something like:<br />
<br />
<code> float doSomething(int arg1, double arg2)</code><br />
<br />
Or in a more abstract sense<br />
<br />
<code> returnType functionName(typeQualifier1 argName1, typeQualifier2 argName2)</code><br />
<br />
This is a pretty common pattern in many languages. But in Swift, there's a game of musical chairs that is played so that we end up with things in different order. The equivalent Swift variant is<br />
<br />
<span style="font-family: monospace;"> <span style="color: #666666;">func</span> doSomething(arg1: Int, arg2: double) <span style="color: #666666;">-></span> float</span><br />
<br />
And put abstractly<br />
<br />
<span style="font-family: monospace;"> <span style="color: #666666;">func</span> functionName(argName1:typeQualifier, argName2:typeQualifier2) <span style="color: #666666;">-></span> returnType</span><br />
<br />
I don't know how I feel about it. It's different, so it kind of feels fresh and new. OTOH, my brain has spent a lot of years learning how to scan the opposite order, where qualifiers precede what they annotate, rather than post cede them.<br />
<br />
<b><span style="font-size: large;">
:, :, and :</span></b><br />
One of the things that makes the C part of Objective-C annoying, is that C with its many years of evolution can often feel complex to parse. You have to look at the context to figure out what a given character does. However, the : character doesn't play to much of a role in C. While cleaning up use of other infix characters, Swift decided to celebrate the : character.<br />
<br />
So far, I've counted at least three different uses of this character that I have to press the shift key for (Swift has relegated the easier to type semicolon to near nothingness, using it as a<br />
multiple; statements; on; the; same; line; separator just as in Python).<br />
<br />
The first, as shown above, is that it is used in function signatures to "attach" the type of an argument to the back side of it.<br />
<br />
The second, is that it can be used when calling a function (or method). It looks subtly, similar to a keyword style invocation.<br />
<br />
<code>Point(x: 4, y: 2)</code><br />
<br />
Yes folks, Swift lets you type not only a comma separated argument list (C style) or a colon delineated keyword list interspersed with arguments (Smalltalk style), but you get to (must) do both! Type out the function name, the parens, the commas, the keywords, and the colons. It's like a politically correct function signature. It's so all inclusive.<br />
<br />
What I find particularly disingenuous about the readability of this though, is that it undos what I just got used to. I had decided that : was how I attached annotating information to a keyword (e.g. the type), but here the annotating or qualifying element precedes it.<br />
<br />
This is not a show stopper. But what it means is that your brain can't use a simple pattern match to put the pieces together.n You can't see a code and instantly know if you're looking at a function definition or call. Instead you have to parse the surrounding context to figure out what you're seeing.<br />
<br />
The third use, is to indicate that a list (array) is not a list, it's a dictionary. Dictionaries and Lists both start and stop with the [ ] characters in Swift, and the elements are separated by commas. But to figure out whether it's a literal Dictionary or List, you'll have to peer inside of it, scanning it's contents to see if you can find a :. If you do find one, then you have a Dictionary. Then please scan back to the beginning to see if it all started with a [ or a (, so you can disambiguate whether it was a function call, or a dictionary.<br />
<br />
Keep on open mind, I keep muttering to myself about this one. Maybe some zen unifying principle will befall me eventually, and I'll see the wisdom in the ambiguities.<br />
<br />
<b><span style="font-size: large;">
Wrap, Unwrap, Ugh</span></b><br />
Objective-C uses the nil message eating pattern. You can send messages to the nil object, and things don't blow up. It just silently does nothing. It's not really Objective-C per se, it's the implementation of the Objective-C runtime engine. And since that doesn't go away with Swift, they had to allow for that kind of thing with Swift. The solution is to support optional types.<br />
<br />
<code>var TheAnswer:Int? = 42</code><br />
<br />
That says that TheAnswer can be an Int or it can be a nil. Anytime I want to access it though, I have to know that I declared it with a ? and use a ! to get the value out. But if that bothers me too much, I can live a little dangerously and declare it as<br />
<br />
<code>var TheAnswer:Int! = 42</code><br />
<br />
This says that it must be an Int, but it recognizes that until I get it initialized it might not yet be, so I'll have to be careful.<br />
<br />
This need to paper over the Objective-C/Cocoa patterns of nil, is one of those cases where I see the legacy as compromising the new. Maybe I'll be wrong and become a big fan of the optionals system. So far though, the compiler is constantly nagging me to add !'s or ?'s here and there. Sometimes, I don't understand entirely why. So I'm not sure it's a productivity winner for me at all yet. We'll see how the lay app developer of iOS apps deals with it.<br />
<br />
<b><span style="font-size: large;">Refactoring</span></b><br />
Finally for now, with all this type goodness, and the improved completion and playground, I was surprised to find that if you highlight a chunk of Swift code and choose the Xcode Refactor menu option, you'll be rewarded with this wonderful message:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjiAcrYOjsfmzKTvwZjEgMPMkYmUFhG7cPmMjJz5iq3q5M0Q3asb_X2RlnLDBdUtQef7DhjhvLgWC4E67Iihw1Y3kb7536gxXuXZjYN5xSOC5gsuAwcziZPBZ4pCZIexMQ_XWf4-xGjjiY/s1600/Screen+Shot+2014-10-06+at+3.45.52+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjiAcrYOjsfmzKTvwZjEgMPMkYmUFhG7cPmMjJz5iq3q5M0Q3asb_X2RlnLDBdUtQef7DhjhvLgWC4E67Iihw1Y3kb7536gxXuXZjYN5xSOC5gsuAwcziZPBZ4pCZIexMQ_XWf4-xGjjiY/s1600/Screen+Shot+2014-10-06+at+3.45.52+PM.png" height="99" width="320" /></a></div>
<br />
<br />
Hopefully, we'll see that go away as Apple continues to mainstream its new darling language.Travis Griggshttp://www.blogger.com/profile/01599271142862167244noreply@blogger.com2tag:blogger.com,1999:blog-7515306875906042828.post-19667682537914097982014-09-29T11:53:00.001-07:002014-09-29T11:53:57.076-07:00Dipping Toe in WaterTwo plus years ago, I decided to take a hiatus from blogging. In addition to a cessation of blogging, I also delisted from Facebook and Goggle+. Dropped off of a bunch of mailing lists. Sort of walked away from an online persona I had spent a bit of time curating. At one point, I had always thought, maybe I'll write a "year later" post to try and put some of my life/career changes that were going on in perspective.<br />
<br />
A year came, and I thought about it. But thoughts didn't gel enough, so I put a retrospective aside for the time being, and went back to work. I was happily coding in C with 8K of RAM by that point. Smattered with Python. And then another year went by, and I was amusedly coding in iOS by then.<br />
<br />
This morning, I was working my way through a tutorial on Swift (Apple's new language), I thought I'd have a go at it again. It probably won't be (much) about Smalltalk; I have done very little of that in the last 2 years. But I do miss the cathartic process of journaling my passage through programming.<br />
<br />
We'll see how it goes.Travis Griggshttp://www.blogger.com/profile/01599271142862167244noreply@blogger.com1tag:blogger.com,1999:blog-7515306875906042828.post-34635308982162075552012-06-01T11:16:00.002-07:002012-06-01T11:16:56.254-07:00Some farewell thoughts/code on widget layout/placementA couple of releases ago, an object called Panel was introduced by me to VisualWorks. New UIs were put together with it, including among others: BundleOrderTools, PrereqTool, and new Change Tools, as well as the new Skinny widgets.<div>
<br /></div>
<div>
Panel was a hard swing away from the traditional VisualWorks layout facilities. It took the position that layout was entirely a container responsibility (whereas the traditional framework puts a lot of emphasis on encapsulating the layout parameters of a widget with it on a one-to-one basis). The advantage of doing it this way, was that you could build layout algorithms that took into account the interplay between the different children better. It was a hard swing, because rather than a rich set of as-yet-understood-ideas, I made egregious use of blocks to pull it off.</div>
<div>
<br /></div>
<div>
A couple of months back (maybe 6 even), I sat down and began playing with some ideas that were a little more "half way." Having done VisualWorks for many years, having explored ideas with Panel, I was interested in addressing the following ideas:</div>
<div>
<br /></div>
<div>
<ul>
<li>Neither Panel nor CompositePart have a good way of differentiating between what their ideally composed size would be from what their layout ended up being. In other words, the preferredExtent of a Composite is just whatever it ended up laying things out as.</li>
<li>While various widgets in the system can answer preferredHeight/preferredBounds/preferredWidth/preferredExtent, they don't deal with the need to some times set or tune these values on a per instance basis</li>
<li>95% of layouts are follow one or two axes (e.g. i want a "row" of buttons)</li>
</ul>
<div>
The unfinished product of this is published as WidgetRowsColumnsAndWeaves, replicated into the OR.</div>
</div>
<div>
<br /></div>
<div>
It has two primary types of classes in it. One set is the ViewStripe and ViewWeave classes. ViewStripe is a one axis layout container. It can be configured as a row or a column. And ViewWeave is a two axis container, what some might think of as a LayoutGridBag or whatever Java calls those things.</div>
<div>
<br /></div>
<div>
The other half of the classes, are the EdgePlacement classes. These are the worker objects that a ViewStripe or ViewWeave might use to place it's child widgets. They make heavy use of the RectangleEdge classes that were integrated in VisualWorks 7.9.</div>
<div>
<br /></div>
<div>
One of the things I realized when working on this, was that when I think programmatically about widget placement, I *don't* think in rectangles. A widget's frame may be a rectangle, but I don't compose them that way. I think in axes. I might a though process that goes something like "I need a row of widgets. Vertically, I want them all to to hang from the top, inset down by 2 pixels. And horizontally, I want them evenly distributed, so that they're all the same size, with a gap of 4 between them, and edge insets of 5." See how the reasoning is about the axes separately?</div>
<div>
<br /></div>
<div>
So when you configure the layout of a VisualStripe or a ViewWeave, you see messages like</div>
<div>
<br /></div>
<div>
row leftRight stretchAll</div>
<div>
row topBottom alignTop</div>
<div>
<br /></div>
<div>
The leftRight or topBottom message will return either a SingleCellPlacememt or a MultiCellPlacement depending on how it's configured.</div>
<div>
<br /></div>
<div>
There are a variety of tests and examples in the code that are worth perusing. Here's a piece of the ViewWeave exampleTicTacToe method:</div>
<div>
<br /></div>
<div>
<div>
<span class="Apple-tab-span" style="white-space: pre;"> </span>me leftRight stretchAllCells.</div>
<div>
<span class="Apple-tab-span" style="white-space: pre;"> </span>me leftRight perCell alignCenter.</div>
<div>
<span class="Apple-tab-span" style="white-space: pre;"> </span>me topBottom stretchAllCells.</div>
<div>
<span class="Apple-tab-span" style="white-space: pre;"> </span>me topBottom perCell alignCenter.</div>
</div>
<div>
<br /></div>
<div>
That single "two-scrollbar-long" method is able to define a complete TicTacToe game. There's also an exampleCalendar which actually does quite a bit more with the features in there.</div>
<div>
<br /></div>
<div>
I used properties a bit to pull some of this off. You can attach a #cellWidth or #cellHeight property to a widget that can be used to tune it's cell size along an axis. You can do things like set #cellBackground and #cellBorder as well.</div>
<div>
<br /></div>
<div>
Over all, I was somewhat pleased with this. I regret that I won't be staying around to see it to completion. Maybe someone will pick it up and run with it.</div>Travis Griggshttp://www.blogger.com/profile/01599271142862167244noreply@blogger.com0tag:blogger.com,1999:blog-7515306875906042828.post-11867887119397206432012-05-29T23:11:00.002-07:002012-05-29T23:11:47.282-07:00QueryTwoAs promised, here's one of those "tying up loose ends" things.<br />
<br />
A couple months ago, I <a href="http://objology.blogspot.com/2011/10/sounding-out-view-tree.html">posted a prototype</a> of some code inspired by JQuery like behavior for VisualWorks view trees. It got a variety of feedback. A lot positive, some skeptical, some mixed.<br />
<br />
Recently, I've been working on modifying the newer VisualWorks comparison tool that presents changes in a disclosure/rollup navigation style. I've been working to put filters in it, so you can filter out different kinds of changes (e.g. hide all the category changes, or show just the additions).<br />
<br />
I found myself wanting that JQuery like behavior to have lightweight communication between view tree objects again. I did not want to fabricate a model with dependencies just to facilitate some cross talk. So I took a "go around #2" at the idea. This time, no funky syntax. More based on real world needs. Definitely lighter weight.<br />
<br />
It's been published as <b>QueryTwo</b> to the Open Repository. What will come of it? I don't know at this point. Maybe it'll get integrated, maybe not, that's up to others to decide now.<br />
<br />
Here is an example of me using it in real life (instead of hypothetical examples).<br />
<br />
<pre><code><br />
<br />
propogateChanges<br />
<br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>(self query)<br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>top;<br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>type: AbstractComparisonRollupView;<br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>do: [:view | view hideTypes: hiddenTypes].<br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>self updateCellFills<br />
<br />
</code></pre><br />
<br />
It reminds me a lot of writing Glorp queries. Similar patterns, you create one, send messages to configure it, and then enumerate it. Or kind of like Seaside html writer pattern too. Make one, configure it, execute a block for it.<br />
<br />
What follows is a portion of the class comment that describes usage:<br />
<br />
<br />
<span class="Apple-style-span" style="color: #7f6000;">The most common case is to ask a VisualPart or Window to create one for you using</span><br />
<span class="Apple-style-span" style="color: #7f6000;"><br /></span><br />
<span class="Apple-style-span" style="color: #7f6000;"><i>self query</i></span><br />
<span class="Apple-style-span" style="color: #7f6000;"><br /></span><br />
<span class="Apple-style-span" style="color: #7f6000;">This will return a query that has the receiver as the root of its query path. One can send <i>top</i> to the query to shift the root of the query to the top most element of the view tree (e.g. the window at the top).</span><br />
<span class="Apple-style-span" style="color: #7f6000;"><br /></span><br />
<span class="Apple-style-span" style="color: #7f6000;">You can also ask an ApplicationModel to create one</span><br />
<span class="Apple-style-span" style="color: #7f6000;"><br /></span><br />
<span class="Apple-style-span" style="color: #7f6000;"><i>myApplicationModel viewQuery</i></span><br />
<span class="Apple-style-span" style="color: #7f6000;"><br /></span><br />
<span class="Apple-style-span" style="color: #7f6000;">The pattern is that after creating a query, one sends configurations messages to it, and then invokes various collection methods (e.g. <i>do:</i>). The enumeration methods should be sent <b>after</b> configuration methods. There are couple of different methods that govern which objects in the view tree are traversed, they come in pairs:</span><br />
<span class="Apple-style-span" style="color: #7f6000;"><br /></span><br />
<span class="Apple-style-span" style="color: #7f6000;"><u>Traversal Configuration Messages</u></span><br />
<span class="Apple-style-span" style="color: #7f6000;"><br /></span><br />
<span class="Apple-style-span" style="color: #7f6000;"><i>up</i> - causes the query to proceed upwards through the parent path from the root object</span><br />
<span class="Apple-style-span" style="color: #7f6000;"><i>down</i> - causes the query to proceed downwards through the children of the root object (this is the defaut if neither <i>up</i> nor <i>down</i> is sent to configure the query)</span><br />
<span class="Apple-style-span" style="color: #7f6000;"><br /></span><br />
<span class="Apple-style-span" style="color: #7f6000;"><i>withRoot</i> - causes the query to include the root in its traversal</span><br />
<span class="Apple-style-span" style="color: #7f6000;"><i>withoutRoot</i> - causes only parents or children (as configured by <i>up</i>/<i>down</i>) to be traversed (this is the default if neither <i>withRoot</i> or <i>withoutRoot</i> is sent to configure the query)</span><br />
<span class="Apple-style-span" style="color: #7f6000;"><br /></span><br />
<span class="Apple-style-span" style="color: #7f6000;"><i>one</i> - causes the query to cease traversal after the first match is found and enumerated</span><br />
<span class="Apple-style-span" style="color: #7f6000;"><i>many</i> - causes the query to traverse all elements, matching as many as encountered that match (this is the default if neither <i>one</i> or <i>many</i> or sent to configure the query)</span><br />
<span class="Apple-style-span" style="color: #7f6000;"><br /></span><br />
<span class="Apple-style-span" style="color: #7f6000;"><u>Queries</u></span><br />
<span class="Apple-style-span" style="color: #7f6000;"><br /></span><br />
<span class="Apple-style-span" style="color: #7f6000;">Adding queries controls which elements of the traversal are "matched" and thus show up in things like <i>do:</i> enumerations. By default the query will match everything. Methods found in the <b>queries</b> method category provide utility methods for setting up some common queries. Ultimately, they all pass through the <i>addQuery:</i> method. The argument to the this method is a block, which is <i>cull:</i>ed for every element in the traversal, and for those that answer true to this block, they will be enumerated. Repeated query configuration messages, will AND the queries together. The method <i>reset</i> will return the query to its default [true] state.</span><br />
<span class="Apple-style-span" style="color: #7f6000;"><br /></span><br />
<span class="Apple-style-span" style="color: #7f6000;"><i>Examples</i></span><br />
<span class="Apple-style-span" style="color: #7f6000;"><br /></span><br />
<span class="Apple-style-span" style="color: #7f6000;">(self query id: #foobar) any</span><br />
<span class="Apple-style-span" style="color: #7f6000;">"will return the first element with id of #foobar"</span><br />
<span class="Apple-style-span" style="color: #7f6000;"><br /></span><br />
<span class="Apple-style-span" style="color: #7f6000;">(self query top property: #frame satisfies: [:rect | rect area isZero]) not invalidate</span><br />
<span class="Apple-style-span" style="color: #7f6000;">"invalidate all widgets in my window that have real area to them"</span><br />
<span class="Apple-style-span" style="color: #7f6000;"><br /></span><br />
<span class="Apple-style-span" style="color: #7f6000;">(self query up withRoot) do: [:each | each flash]</span><br />
<span class="Apple-style-span" style="color: #7f6000;">"flash me and all my parents"</span><br />
<span class="Apple-style-span" style="color: #7f6000;"><br /></span><br />
<span class="Apple-style-span" style="color: #7f6000;">(self query top hasProperty: #UpdateGroup) do: #update</span><br />
<span class="Apple-style-span" style="color: #7f6000;">"send #update to all elements in my window that are marked with the #UpdateGroup property"</span><br />Travis Griggshttp://www.blogger.com/profile/01599271142862167244noreply@blogger.com0tag:blogger.com,1999:blog-7515306875906042828.post-78406983456582111622012-05-29T17:18:00.000-07:002012-05-29T17:18:25.197-07:00Stepping Out of the Balloon<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIxr1qkz6JCVA8rkwqRjhI_lStx2v-0fgwbeNBHLIeu5PeUKgqKaihoJgijins7M2wCc9ez6SBZ1V8Gsxv7P97mTpIr97kV2NFHxa4EcG1ojFyUp-JSNxjR6LXnmy9gh4qHtnSHmlip2Te/s400/norway-flag.gif" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="135" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIxr1qkz6JCVA8rkwqRjhI_lStx2v-0fgwbeNBHLIeu5PeUKgqKaihoJgijins7M2wCc9ez6SBZ1V8Gsxv7P97mTpIr97kV2NFHxa4EcG1ojFyUp-JSNxjR6LXnmy9gh4qHtnSHmlip2Te/s200/norway-flag.gif" width="200" /></a></div>
Many many years ago, I returned from an LDS Mission in the lovely country of Norway. It was Christmas of 1991. I took a job at what was then Siemens Nuclear Power. In the months that followed, I was introduced to this novel computer programming language called Smalltalk. I took to it, and I like to think it took to me. For the last twenty years, I've done quite a few things with it. From writing nuclear fuel design and assembly software (which are still running today) to making sure that the french fries, green beans, and much of the rest of the world's food is a little cleaner and better. From numeric modeling to implementing frivolous things like roman numeral message selectors and goto. And quite a bit of toolsmithing. To say it's "served me well" is an understatement.<br />
<br />
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5pCg4FJ_mqN_BdaAm_yxJ8dnXDLt8LBT8IrS6sDkA8MY0Knu9RnplUgFysGi3vQ96JcL7i2yrZX3ACofWUvI_uWFJ__dnHiMiq-ED5iDLLcRksfSCHTSCh_5xWZQxmIedlFax444ahHvI/s400/post-12606-0-37482900-1304270901_thumb.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5pCg4FJ_mqN_BdaAm_yxJ8dnXDLt8LBT8IrS6sDkA8MY0Knu9RnplUgFysGi3vQ96JcL7i2yrZX3ACofWUvI_uWFJ__dnHiMiq-ED5iDLLcRksfSCHTSCh_5xWZQxmIedlFax444ahHvI/s200/post-12606-0-37482900-1304270901_thumb.jpg" width="148" /></a></div>
<div>
The wonderful world of Smalltalk technology and philosophy, wouldn't have been as enriching for me, if not for the wonderful community of people I have rubbed shoulders with over the years. I remember my first post via bitdearn to comp.lang.smalltalk back in 1994. Meeting people at conferences such as OOPSLA, ESUG, STIC, and others. I have made a ton of friends and come to admire the work and enthusiasm of so many people.</div>
<div>
<br /></div>
<div>
Working at <a href="http://www.cincomsmalltalk.com/main/">Cincom</a>, the "original" commercial Smalltalk vendor, has always been a sort of pinnacle in my Smalltalk pilgrimage. A chance to be at a hub of where Smalltalk was happening at.</div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
But all journeys must come to an end. And the time for this journey, for me, for now, has come to an end. On June 4th, I will begin work at <a href="http://www.nelsonirrigation.com/">Nelson Irrigation</a>, doing embedded automation work, sprinkled (that's a pun) with a variety of end user application work. I am super excited. It's a neat project, a neat company, and an indescribably neat culture.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnTNDLv6xKxbX7ym1VqeWM1ysvWotYVLGrkGE_DfitQ-DrAUyi_21pWeeLKUdlgXa-O8yXWxX0uc63dv1uw4GKKfcbpiGbLEiZmDAQcLIJRsyyMJ2W95HDijJFDeFKogoC5fIIyjWC2wGn/s400/lg_Hot-Air-Balloon-Skydive.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="265" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnTNDLv6xKxbX7ym1VqeWM1ysvWotYVLGrkGE_DfitQ-DrAUyi_21pWeeLKUdlgXa-O8yXWxX0uc63dv1uw4GKKfcbpiGbLEiZmDAQcLIJRsyyMJ2W95HDijJFDeFKogoC5fIIyjWC2wGn/s400/lg_Hot-Air-Balloon-Skydive.jpg" width="400" /></a></div>
<div>
<br /></div>
<div>
But it means I'll be dropping out of that central involvement in the Smalltalk community. I may still do some Smalltalking for sure, and the ethos that is Smalltalk will permeate all the work I do, but it's unlikely I'll show up at a Smalltalk conference in the near future or be active in the mailing lists as a heavy contributor.<br />
<br />
And so it's in some ways, a probable good bye for me. And that makes me sad. And yet happy, because it's better to feel sad about what I'm losing with the community, than thrilled to be shot of it all.<br />
<br />
I also want to point out something my departure from Cincom does NOT mean. There have been some others prominent names that have left Cincom recently, and one might assume there was a sinking ship meme going on. Such is simply not the case with me. The timing of this opportunity to learn and be involved in some new and different things, was out of my hands. When it surfaced, unfortunate timing aside, I felt I could not pass the opportunity up. So please don't read any sort of ill boded fate for Cincom or VisualWorks into my departure. I have faith in the people that remain, and in the people that will replace me, I'm sure they'll take the balloon farther and better heights than I was capable of. Any age or oddities aside, it remains some of the best tech that is out there.<br />
<br />
As for this blog, I'm not sure what will happen. The purpose of this blog was always meant to be about Smalltalk, and in particular the live "biological" nature of the Smalltalk program philosophy. There are one or two things of the normal ilk that I'd like to write about based on some work I've been doing of late, and then, it'll likely take a hiatus, possibly permanent.<br />
<br />
If our paths don't cross in the future, in the immortal (and skewed) words of Spock, may you "Learn Long and Prosper." And remember, "Dead men never wish they'd spent more time at the office."<br />
<br />
<br /></div>Travis Griggshttp://www.blogger.com/profile/01599271142862167244noreply@blogger.com8tag:blogger.com,1999:blog-7515306875906042828.post-89944555227784716522012-05-15T00:34:00.001-07:002012-05-15T09:39:02.740-07:00Caching and The Way of the ObjectLately, we've had an internal debate about how to make some places where we do sorting go faster. Of course, there's always the caveat: Make it Work, Make it Right, and if you need to, Make it Fast. Learning when you need to care about that last step, is always a sort of art based on experience. Often the answer is simply "all other things considered, it's fast enough, I've got bigger problems to solve elsewhere."
<br />
<br />
Let's take an example though. Take a collection of Class objects (we'll leave the MetaClasses out), and sort them by their response to <i>toolListDisplayString</i>:
<br />
<pre><code>
| classes |
classes := Object withAllSubclasses reject: #isMeta.
classes sorted: #toolListDisplayString ascending
</code></pre>
<br />
In our example, we're using VisualWorks' ability to substitute simple Symbols as replacements for BlockClosures that send a single unary message to their arguments. It is equivalent in behavior to the more traditional:
<br />
<pre><code>
| classes |
classes := Object withAllSubclasses reject: [:each | each isMeta].
classes sorted: [:each | each toolListDisplayString] ascending
</code></pre>
<br />
Sending <i>ascending</i> to a BlockClosure before using it was first developed back in <a href="http://objology.blogspot.com/2010/11/tag-sortfunctions.html">this post</a> and <a href="http://objology.blogspot.com/2010/11/tag-sortfunctions-redux.html">this following post</a>. And that was then integrated into VisualWorks 7.8 (or was it 7.8.1?).
<br />
<br />
The problem with our example, is that the method <i>toolListDisplayString</i> is not cheap. It's more than just concatenating strings for class and namespace names together. It looks at how much context needs to be added to the class name by itself to make it unique. Or put another way, since there are multiple classes in the system with the name Text, it determines it must add some info about the containing namespace, while the class PostgreSQLEXDIBLOBManipulationOutsideTransaction probably only has one instance and doesn't need any namespace info to contextualize it.
<br />
<br />
The core default sort algorithm in VisualWorks is hybridization of <a href="http://en.wikipedia.org/wiki/Quicksort">quicksort</a> and a <a href="http://en.wikipedia.org/wiki/Insertion_sort">insertion sort</a>. The implications for this, is that this somewhat expensive <i>toolListDisplayString</i> method may be called repeatedly for some objects. That means redundant CPU cycles.
<br />
<br />
A common solution to this kind of problem is <a href="http://en.wikipedia.org/wiki/Memoization">memoization</a>. Memoization basically is a fancy word which means "cache the results of your computation function, so you only evaluate the function once for each unique input and just look up the cached result for subsequent calls."
<br />
<br />
How to go about doing memoization around sorting sites can be accomplished a number of different ways.<br />
<b><br /></b><br />
<b>In Place</b><br />
<br />
The first and simplest way is to simply do it right at the sort site. We could rewrite our example to read:
<br />
<pre><code>
| classes |
memory := IdentityDictionary new.
classes := Core.Object withAllSubclasses reject: [:each | each isMeta].
classes sorted: [:each | memory at: each ifAbsentPut: [each toolListDisplayString]] ascending
</code></pre>
<br />
This is the simplest thing that could possibly work. That's its single advantage. The disadvantages is that adds a bit code bloat for every place we decide this is worth doing. It intermingles with what was otherwise pretty simple and easy to read. To flip back and forth between memoized and non-memoized is a pain. And it gets done again and again and again at each call site, so there's no real reuse involved. The risk of implementing it wrong is retaken at each implementation.
<br />
<br />
The desire to be able to easily flip back and forth between memoizing and not, shouldn't be underrated. Memoization is not free. It costs cycles. It is usually trial and error under conditions that the programmer knows to be common for his code, that determine if the overhead of memoizing is less than the cost of the original redundant function.
<br />
<br />
This technique is best for those that like to write more code. If you like to brag about how much code you've written, how many lines, classes, or methods, this might be for you. It's simple, and you can demonstrate your superior typing speeds.<br />
<b><br /></b><br />
<b>More Sort Methods</b><br />
<br />
Another approach is to add a new sort method. VisualWorks already has <i>sort</i>, <i>sort:</i>, <i>sorted</i>, <i>sorted:</i>, <i>sortWith:</i>, and probably some I've missed. Application developers tend to add one or two of their own. A common one in the past has been <i>sortBy:</i> which supports using a single arg block. So you figure out how many of these APIs you want to replicate as memoized alternatives and implement them, for example: <i>memoizedSortBy:</i>, etc. This is if you're a good citizen. If you're not so kind, you use something that looks like just another general purpose sorting API (e.g. <i>sorting:</i> <i>aOneArgBlock</i>).
<br />
<br />
Implementing <i>memoizedSortBy:</i> gives you the advantage of optimizing things a little differently. You can choose to build a parallel vector of objects <i>collect:</i>ing for the function, retaining index information, sort those, and then basically apply those indices to the original input set. Or you can just go with the Dictionary and <i>at:ifAbsent:</i> approach.
<br />
<br />
Now the only change we need to make to our call site is to change it to:
<br />
<pre><code>
| classes |
memory := IdentityDictionary new.
classes := Core.Object withAllSubclasses reject: [:each | each isMeta].
classes memoizedSortBy: [:each | each toolListDisplayString]
</code></pre>
<br />
You'll note that we don't have <i>ascending</i> anymore in there. The SortFunctions stuff is basically incompatible with this approach. Since this API wants to work with single arg blocks, which it's memoizing the results for, it has hard coded the sort direction inside of it.
<br />
<br />
I consider this the C Programmer's (or procedural) Approach. If at first you don't find a function, try, try, another one. That it is in this simplistic form incompatible with the SortFunctions thing, is personally aggrieving to me (we lose the elegance of setting the direction, as well as chaining functions, or deriving our own rocketship sorts). Another disappointment is that it's one more API I have to figure out which one I should use. I see a family of sort methods, and I've got to figure out (or recall) what the different nuances of each are (this one takes one arg, this one takes none, this one takes two, each has different trade offs, etc).
<br />
<br />
Finally, it limits the technique of memoization to sorting. What if I want to use memoization for <i>collect</i>:ing over a collection that I know has redundant elements. In that case, I have to go back to the In Place approach.<br />
<b><br /></b><br />
<b>The Way of the Object</b><br />
<br />
I'd rather take a page from the SortFunction technique. BlockClosures (or more generally, objects which respond to the message <i>value:</i> and fill the roles of functions) are real Objects too. And I'm declaring that they too have a right to be included in the General Love Fest of Polymorphism. The idea here, is that we add a new <i>memoizing</i> method to BlockClosure (and Symbol too so they can continue to stand double as simple BlockClosures). Sending <i>memoizing</i> to a BlockClosure returns a MemoizedFunction object which can do <i>value:</i> just like a BlockClosure. But it keeps a memory of evaluations and uses those when found. My first cut implementation is published as TAG-MemoizedFunctions in the Open Repository.
<br />
<br />
Now our example just turns in to:
<br />
<pre><code>
| classes |
classes := Object withAllSubclasses reject: #isMeta.
classes sorted: #toolListDisplayString memoizing ascending
</code></pre>
<br />
For this simplistic example, slapping memoizing in there is a 10x speed boost.
<br />
<br />
What do I like about this approach? First of all, it was fun. This kind of thing, to me, is where the zen of Object Oriented dispatch is at (I don't pretend to be brilliant about this at all, Wilf LaLonde probably wrote an article demonstrating this 20 years ago). I like that it is terse. I like that it localizes the decision about whether to memoize around the function itself rather than the API using it. This is the fastest/easiest way to toggle memoization on and off to profile the differences. I like that I can use it with collect:, or detect:, or allSatisfy:, or any method that makes use of first class function objects. And I like that it only took 10 methods and one class to do. Because Less is More.
<br />
<br />
Happy Memoizing!
<br />
<br />
(Why does Apple insist on constantly changing "memoizing" to read "memorizing"? Grumble...)Travis Griggshttp://www.blogger.com/profile/01599271142862167244noreply@blogger.com3tag:blogger.com,1999:blog-7515306875906042828.post-60180824731009724132012-04-30T10:22:00.001-07:002012-04-30T10:29:48.237-07:00Smalltalk meets Cubism<br />
<div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px;">
Everytime I meet up with <a href="http://bergel.eu/">Alexandre Bergel</a> at a Smalltalk conference, we talk about <a href="http://www.moosetechnology.org/tools/mondrian">Mondrian</a>. And I always ask him a question: "Why is it that every time I see Mondrian, it's always about Rectangles?" In some ways, it's appropriate that Mondrian is always about rectangles. <a href="http://www.google.com/search?tbm=isch&hl=en&source=hp&biw=1120&bih=1042&q=Mondrian&gbv=2&oq=Mondrian&aq=f&aqi=g10&aql=&gs_nf=1&gs_l=img.3..0l10.3698.5358.0.6062.8.8.0.1.1.0.95.534.7.7.0.M90mAtFYiow" rel="nofollow">Google Mondrian</a>, and the <a href="http://www.google.com/search?tbm=isch&hl=en&source=hp&biw=1120&bih=1042&q=Mondrian&gbv=2&oq=Mondrian&aq=f&aqi=g10&aql=&gs_nf=1&gs_l=img.3..0l10.3698.5358.0.6062.8.8.0.1.1.0.95.534.7.7.0.M90mAtFYiow" rel="nofollow" target="_blank">first images</a> you'll see are all about rectangles. Nearly all of the artwork associated with <a href="http://en.wikipedia.org/wiki/Piet_Mondrian">Piet Mondrian</a> is a love affair with rectangles.</div>
<div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 14.0px;">
<br /></div>
<div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px;">
Anyway, I thought it was time to put up or shut up. I want a new question to bug Alexandre about when we cross paths in the future. So I decided to play a little. I wasn't interested in rewriting all that Mondrian is. I just wanted to experiment a little with other polyshapes to express multiple simultaneous attributes of subjects I was trying to visualize. Mostly, I was interested in playing with MeshGradients using <a href="http://www.cairographics.org/">Cairo</a>, because I was curious if I could find an interesting problem I could use mesh gradients with.</div>
<div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 14.0px;">
<br /></div>
<div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px;">
If you <a href="http://en.wikipedia.org/wiki/Cubism">google Cubism</a>, you'll see the artwork that I was inspired by. According to Wikipedia</div>
<div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 14.0px;">
<br /></div>
<blockquote class="tr_bq">
In cubist artworks, objects are broken up, analyzed, and re-assembled in an abstracted form—instead of depicting objects from one viewpoint, the artist depicts the subject from a multitude of viewpoints to represent the subject in a greater context.</blockquote>
<div style="font: 13.0px Helvetica; margin: 0.0px 0.0px 6.0px 0.0px; min-height: 16.0px;">
<br /></div>
<div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 6.0px 0.0px;">
<span style="font: normal normal normal 13px/normal Helvetica;">That sounded exactly like what I was trying to do. </span>I published my couple-day-prototype in the Open Repository in a package called TAG-Cubist. I realized in playing with this how it's not just about rectangles (of course), but how important layout of the per-subject-graphics is. I didn't do anything, other than present then in a tiled format. I'll leave that kind of thing to others. Here are screencaptures of the four example methods I put on the Portfolio class (a Portfolio is a collection of similar drawings for a group of different subjects).</div>
<div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 6.0px 0.0px;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<p>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgx-lQlfUoSaEdypjnDlstJ1oypOaSI_YO6DrEpDwmSAoWnq8IOt4SmSPY_YOUdOHdit7l7AAB2i0b6OOPqHukWAxLw943D1Z3cUh5qwF8x3vYIHh57PGAkJVvzTS1J-tPIg4bp_iWKDOM/s1600/PortfolioMethods.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="704" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgx-lQlfUoSaEdypjnDlstJ1oypOaSI_YO6DrEpDwmSAoWnq8IOt4SmSPY_YOUdOHdit7l7AAB2i0b6OOPqHukWAxLw943D1Z3cUh5qwF8x3vYIHh57PGAkJVvzTS1J-tPIg4bp_iWKDOM/s1600/PortfolioMethods.png" width="876" /></a></div>
<div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 6.0px 0.0px;">
Some of the methods found in the Portfolio object, showing clockwise, from noon high position: LoC, inst var ref count, selector size, argument count, and bytecodes.</div>
<div class="separator" style="clear: both; text-align: center;">
<p>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsokmdZoyEL5RB-B3J8bsKiDMtPwVLhEMo7kLrjQjgXWRNSQqsbTGL1le8Zga47pRNLm5KyDtTUNeHXHr0my3USmUhR4XFNnjj9PsomDcEq_AitX0SMb4LpAI1gtN15PX4wn6tdWPqWUI/s1600/Packages.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="840" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsokmdZoyEL5RB-B3J8bsKiDMtPwVLhEMo7kLrjQjgXWRNSQqsbTGL1le8Zga47pRNLm5KyDtTUNeHXHr0my3USmUhR4XFNnjj9PsomDcEq_AitX0SMb4LpAI1gtN15PX4wn6tdWPqWUI/s1600/Packages.png" width="878" /></a></div>
<div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 6.0px 0.0px;">
Some of the top-level packages found in my image, showing clockwise, from noon high position: prerequisites, defined classes, extended classes.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-MQTzeoo_gCq48xIB3sL8kXoLgbcj4_PUzUhUot3WUCcK_kMSqVtF1Y6I-JV0BErNt0DbdEVGd-Y9HYNraYjs5k-dBYaAeGx0HG5-9ViQqnhoFJht9rOez8FXXOatQFflK2v1PtlOjJk/s1600/ArithmeticValueClassTree.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="522" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-MQTzeoo_gCq48xIB3sL8kXoLgbcj4_PUzUhUot3WUCcK_kMSqVtF1Y6I-JV0BErNt0DbdEVGd-Y9HYNraYjs5k-dBYaAeGx0HG5-9ViQqnhoFJht9rOez8FXXOatQFflK2v1PtlOjJk/s1600/ArithmeticValueClassTree.png" width="862" /></a></div>
<div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 6.0px 0.0px;">
Classes found in the ArithmeticValue class hierarchy, showing clockwise, from noon high position: methods, inst vars, refs to globals, global refs to the class (attributes suggested by Bob Hartwig, thanks!).</div>
<div class="separator" style="clear: both; text-align: center;">
<p>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgl8Jy9fiWiHsod0GsMC2Oa03gNmo08NRVshYb7ZFjGRdRNtdOYrVSPMDzhizlIGgihB0TfnyL0FCG02Fbt3tInhTqEp2MYsGELr7h783BeSbeczZWumfeE7JzahIPOeSg1VU0wdzVgQM8/s1600/TopLevelBundles.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="534" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgl8Jy9fiWiHsod0GsMC2Oa03gNmo08NRVshYb7ZFjGRdRNtdOYrVSPMDzhizlIGgihB0TfnyL0FCG02Fbt3tInhTqEp2MYsGELr7h783BeSbeczZWumfeE7JzahIPOeSg1VU0wdzVgQM8/s1600/TopLevelBundles.png" width="1010" /></a></div>
<div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 6.0px 0.0px;">
Top-level bundles in my image, showing clockwise, from noon high position: child packages, child bundles, prerequisites, comment size, defined classes, methods of defined classes, extended classes, extension methods.</div>
<div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 6.0px 0.0px;">
<br /></div>
<div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 6.0px 0.0px;">
If I was to go on playing, I'd basically start to reinvent Mondrian. Which is not something I really wanted to do. I might play with the way the shape is generated some more, make it more of a star graph (whereas it's a sort of spider plot right now). And I'd definitely figure out how to do a legend plot.</div>Travis Griggshttp://www.blogger.com/profile/01599271142862167244noreply@blogger.com0tag:blogger.com,1999:blog-7515306875906042828.post-49110054981911003792012-04-11T23:08:00.004-07:002012-04-11T23:35:05.867-07:00Cairo 1.12.0A couple weeks ago, the good folks that give us <a href="http://www.cairographics.org/">Cairo</a> announced that <a href="http://www.cairographics.org/news/cairo-1.12.0/">version 1.12.0 is ready for consumption</a>.<div><br /><div>In the last couple weeks, at sundry moments, I've been updating the VisualWorks binding to it, and playing with it some. There's some exciting news and some not-yet-good news. First, the good.</div><div style="text-align: center;"><br /></div><div>Doing a build for Linux was quite simple. And that allowed me to update/play right away. You can build it yourself, or ask me, I'm curious how portable the binaries I built are for consumption.</div><div><br /></div><div>On of the exciting things new in 1.12.0 is at long last, Mesh Gradients. Mesh gradients are the "you can do anything with it" gradient. They can be used to simulate any other kind of gradient. Radial and Linear gradients can be done with them (Cairo gives us first class versions of these already). You can also do the elusive conical gradient.</div><div><div style="text-align: center;"><br /></div><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 400px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCqpBo9UqmF-F4k11UT74OIuLymCe0C8qaarxm0uR8zG484XCbveFrmbRNauBjQkwLwdb1VOTYSichp6l6aIfzXFkuUj08CRsRCvEqL-hcn1uaosNk4d8eqFYmw7tpFICccxGRVp_kvzY/s400/colorCone.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5730394880330633122" /></div><div><br /></div><div>Here'a round one (they don't have to be round) which varies evenly on hue. This can be fun to build pie charts with. Or you can build a conical gradient useful for doing highlighting masks after clipping to a shape</div><div><br /></div><img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipzc2t5cTK4IciKj-Mav2lpnRKggaRpJ6fcROPNkxwJNS0up6mop-SHz8rf5ZmxnFs0NXLb7fcl2b5PG5fRhen_3lW90jS4rwvTrmdfgnPRz2Aamc0Qr8R-QTvij7Pj76htLQlJm-nK0g/s400/shadeCone.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5730396809175181234" style="display: block; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; text-align: center; cursor: pointer; width: 400px; height: 400px; " /><div><br /></div><div>You can do more complex things with them, such as display map data, but I didn't find any simple ones to read and map to mesh (if anyone knows of some, let me know). Other than cones I haven't done to much with the meshes yet. But I'm excited to figure out what kinds of data visualization one could do with them for an application that might be written with VisualWorks. I have an idea of something, but... that'll have to wait for a later post if it works out.</div><div><br /></div><div>The not-yet-good news is that so far, I only have this working on Linux. The build method I've used in the past, doesn't seem to work with this new version (it could just as well be that I'm now running Lion and newer versions of Xcode). I did manage to grab a binary from someone who had built just prior to release. While that worked for many things, it crashed when using the MeshGradient stuff. And the same goes for Windows so far. I hope to do some looking and have more/better news on these fronts in the weeks to come.</div><div><br /></div></div>Travis Griggshttp://www.blogger.com/profile/01599271142862167244noreply@blogger.com0tag:blogger.com,1999:blog-7515306875906042828.post-40302796283807339322012-04-11T17:20:00.006-07:002012-04-11T17:56:56.865-07:00A Post STIC Talk Thought<div style="text-align: center;"><br /></div>At STIC I gave a talk about Skins. Skins are a way of separating display behavior from widgets, and trying to rectify some of what we see as "no longer sound" design decisions. At one point, I had put this graphic up on the screen<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjv56J8eeCSbgKX1p3tY-Tv4g11_jac4hEdukB6dFDkN0X4xoclBR2gxMx0BQCA2xKku0vGWfpDEUInoSx1xL6PG4YqeeI2q25eqvq83bBk9YKiHZ1-dTtRcb6u5jXgYJZxbwxAfr6cmb4/s1600/Screen+Shot+2012-04-11+at+5.47.05+PM.png" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 209px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjv56J8eeCSbgKX1p3tY-Tv4g11_jac4hEdukB6dFDkN0X4xoclBR2gxMx0BQCA2xKku0vGWfpDEUInoSx1xL6PG4YqeeI2q25eqvq83bBk9YKiHZ1-dTtRcb6u5jXgYJZxbwxAfr6cmb4/s400/Screen+Shot+2012-04-11+at+5.47.05+PM.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5730309724335319074" /></a>followed by this one<div><br /></div><div><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 276px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEga6LzA0z9JHK8CdJChsOYoKaMcj0OPwrBbK_sKj10WpI7METsQk8uiUtUsoUkB3a0eW0QzfzPQHCjKvqJfvCkLhfQxI5JVwjO-aciIKPsenCdcFe-P-S5UYRDvO55QOGPcJkP8zI_vU4M/s400/Screen+Shot+2012-04-11+at+5.50.38+PM.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5730310499333928626" /></div><div><br /></div><div>It describes the central, fatter role, we're trying to give widgets we develop with the Skin mindset in mind. The one thing this kind of Widget does not do, is actual drawing.<div><br /></div><div>I was asked later on "so, if we wanted to do some new widgets in with this newer GUI approach, how would we do this? Would we make a new skin?</div><div><br /></div><div>The answer is no. Or at least, most likely not. The whole point of separating the drawing behavior into stateless policy objects, is to deal with having a single widget object, that can be rendered in different look and feels. But for most custom widget development, you don't care about different looks. You're not trying to ape Windows or OSX or something else, the look of your widget tends to be uniform. And so you don't need the whole separation/decoupling that a skin approach provides. You just have your Widget do it's own #displayOn: method.<br /><div><br /></div><div><br /></div></div></div>Travis Griggshttp://www.blogger.com/profile/01599271142862167244noreply@blogger.com0tag:blogger.com,1999:blog-7515306875906042828.post-9609639228233924142012-03-12T11:19:00.004-07:002012-03-12T11:26:26.228-07:00Apple's MVC SongWhile prepping for my <a href="">talk at STIC about Skins</a>, I stumbled across this homemade song and video from the folks at Apple about Model-View-Controller. The first minute or so is skippable, but the lyrics are cute once the song starts. Look for the Smalltalk hat tip at 4.14.<br /><br /><iframe width="504" height="378" src="http://www.youtube.com/embed/YYvOGPMLVDo" frameborder="0" allowfullscreen></iframe><br /><br />What's (again) interesting to me is how fungible the controller aspect is. Every MVC framework you meet treats Controller a little differently. Everyone agrees that core state and ui presentation should be separated. Makes good design sense for a variety of reasons. The devils in how you bridge or connect the two. And everyone then calls this Controller. It would have been better to have renamed the pattern Model-View-ConnectorTravis Griggshttp://www.blogger.com/profile/01599271142862167244noreply@blogger.com0tag:blogger.com,1999:blog-7515306875906042828.post-24810555123609640762012-01-17T10:06:00.000-08:002012-01-17T10:07:14.180-08:00TAG-MemoryUsagePrompted by a discussion in the VWNC mailing list, I decided to write a little tool to help me see where and how much memory is being used by what objects.<br /><br /><object id="scPlayer" width="1002" height="744" type="application/x-shockwave-flash" data="http://content.screencast.com/users/TravisGriggs/folders/Jing/media/215e3330-a993-4a4f-8bc3-bc7ad66c90e0/jingswfplayer.swf" > <param name="movie" value="http://content.screencast.com/users/TravisGriggs/folders/Jing/media/215e3330-a993-4a4f-8bc3-bc7ad66c90e0/jingswfplayer.swf" /> <param name="quality" value="high" /> <param name="bgcolor" value="#FFFFFF" /> <param name="flashVars" value="thumb=http://content.screencast.com/users/TravisGriggs/folders/Jing/media/215e3330-a993-4a4f-8bc3-bc7ad66c90e0/FirstFrame.jpg&containerwidth=1002&containerheight=744&content=http://content.screencast.com/users/TravisGriggs/folders/Jing/media/215e3330-a993-4a4f-8bc3-bc7ad66c90e0/00000228.swf&blurover=false" /> <param name="allowFullScreen" value="true" /> <param name="scale" value="showall" /> <param name="allowScriptAccess" value="always" /> <param name="base" value="http://content.screencast.com/users/TravisGriggs/folders/Jing/media/215e3330-a993-4a4f-8bc3-bc7ad66c90e0/" /> Unable to display content. Adobe Flash is required. </object>Travis Griggshttp://www.blogger.com/profile/01599271142862167244noreply@blogger.com0tag:blogger.com,1999:blog-7515306875906042828.post-71763342032815926222012-01-13T16:12:00.000-08:002012-01-14T01:11:26.666-08:00Some Edgy ReificationEver had that experience where you're working on something, and it just feels like something's missing? You muddle on for a while. You begin to suspect there's an object whispering in the digital ether to come into being. When it works out (it doesn't always), I've found it to be one of the more rewarding experiences along the Zen Path of All Objects All The Time.<br /><br />Working with GUIs and widgets and layout, an object I get to know real well is Rectangle. It's a pretty classic object that you find in many a class library, Smalltalk or otherwise. When working with widget trees and layout, rectangles make a lot of sense, because they store (directly or indirectly) the data you need to approximate most widget's layouts (e.g. top, left, bottom, and right). But when it comes to actually working with layout, my experience has been that often just Rectangles are sub optimal.<br /><br />Generally, the types of behavior Rectangle supports are methods that work with all or most of the 4 implied values of a rectangle. But much of layout involves dealing with just one side of a rectangle. When we're left aligning some rectangles, we care about their left first, and then possibly all of their rights. So we often see code that looks like<br /><br /><pre><code>newBox := bounds left + someDelta @ bounds top extent: bounds extent.</code></pre><br />We didn't want to have to care about tops or extents.<br /><br />We could do<br /><pre><code>aBox left: aBox left + someDelta</code></pre><br />but I see less of this pattern actually.<br /><br />A more involved example (no code) involves determining the bottomRight corner of a rectangle for a widget that may or may not have scrollbars around it. First we check if we need to move the right in for a scrollbar, and do so by the thickness if necessary. Then we use that width to determine if we need to show a horizontal scrollbar now. And now if we adjusted for that, we have to check one more time to see if we didn't need a vertical scrollbar before but do now. We care about first the right side, then the bottom side, then the right side.<br /><br />Another pattern I've seen is that there are many types of widgets and layouts that can be either vertical or horizontal. A row layout container and column layout container are very similar, but one works along the x axis and the other the y axis. Consider a chunk of code that given a list of rectangles, stretch their widths from the leftmost to the rightmost, proportionately by their current widths.<br /><br /><pre><code>| sumWidth scalar lastRight |<br />sumWidth := aBoxList inject: 0 into: [:sum :each | sum + each width].<br />scalar := sumWidth / (aBoxList last right - aBoxList first left).<br />lastRight := aBoxList first left.aBoxList do: <br /> [:each |<br /> each left: lastRight.<br /> each right: (lastRight := each width * scalar + each left)]</code></pre><br /><br />If I want to reuse that same algorithm to work in a vertical direction, I get to copy/paste it and modify as such<br /><br /><pre><code>| sumHeight scalar lastRight |<br />sumHeight := aBoxList inject: 0 into: [:sum :each | sum + each height].<br />scalar := sumHeight / (aBoxList last bottom - aBoxList first top).<br />lastBottom := aBoxList first top.aBoxList do: <br /> [:each |<br /> each top: lastBottom.<br /> each bottom: (lasBottom := each height * scalar + each top)]</code></pre><br /><br />It gets even funner when you're working with points and have to transpose all of the constant x values for constant y values.<br /><br /><a href="http://www.thefreedictionary.com/">The Free Dictionary</a> defines <a href="http://www.thefreedictionary.com/reification"><i>reification</i></a> as<br /><blockquote>To regard or treat (an abstraction) as if it had concrete or material existence.</blockquote><br />What I found working with these types of problems was that there was an object for a Rectangle's edge that really wanted to come into being. RectangleEdge is an abstract class defining the API, and polymorphic subclasses for TopEdge, LeftEdge, RightEdge, and BottomEdge take care of the particulars. Having an object which reifies the edge of a rectangle allows us to talk about rectangle manipulation at a higher level for many problems.<br /><br />What follows is an overview of the classes' use and APIs. Followed by a revisit of the above examples, but using edges.<br /><br /><h4>Creation</h4><br />A particular edge can created by sending <i>leftEdge</i>, <i>rightEdge</i>, <i>bottomEdge</i>, or <i>topEdge</i> to an instance a Rectangle.<br /><br /><pre><code>aRectangle bottomEdge</code></pre><br />Or by sending the message <i>of:</i> to a given EdgeClass<br /><pre><code>BottomEdge of: aRectangle</code></pre><br /><br />A selection of some of the handier high level messages that allow us to talk about rectangle edges more directly<br /><br /><i>Accessing</i><br /><b>complementDistance</b><br />"How far apart are I and my complement (opposite) edge?"<br /><b>position</b><br />"The placement value for this edge along either the x or y axis as appropriate."<br /><b>position:</b> aNumber<br />"Modify my target rectangle such that the edge I reify is now at aNumber."<br /><br /><i>Adjusting</i><br /><b>-=</b> aDelta<br />"Modify my position so that it is my current position minus aDelta."<br /><b>+=</b> aDelta<br />"Modify my position so that it is the sum of aDelta and my current position."<br /><b>lineUpWith:</b> anEdge<br />"Change my position so that I line up with anEdge."<br /><br /><i>Other Edges</i><br /><b>adjacentEdges</b><br />"Return the edges that are normal or perpendicular to me."<br /><b>complementEdge</b><br />"Answer the edge of my box that is opposite of me."<br /><b>sameEdgeOf:</b> aRectangle<br />"Return the equivalent edge of aRectangle to me."<br /><br /><i>Testing</i><br /><b>attractsEdge:</b> anEdge<br />"Answer whether I'm the kind of edge that would want to be considered in concert with anEdge for alignment purposes."<br /><b>linesUpWith:</b> anEdge<br />"Do I associate with anEdge in alignment considerations?"<br /><br />There's more than these, but these seemed some of the more obvious. I actually came up with these classes when I first came to work at Cincom and was working on the failed next generation UIPainter with <a href="http://blog.3plus4.org/">Vassili Bykov</a> called "Splash". Having reified edges was a big help in doing snap to alignment and other interactive layout operations. And I've been using it with the work on skins (see the description of doing scrollbar layout above). And have been using it with layout prototypes on beyond the current Panel idea found in VisualWorks. It's likely it will finally be integrated in a build near you, before our next release.<br /><br />Circling back around to the original examples, I now write code like<br /><br /><pre><code>aBox rightEdge -= self scrollbarThickness</code></pre><br /><br />I think this makes the code more expressive of what I'm doing, instead of being lost in how I'm trying to manipulate a rectangle to get the same result. The layout algorithm is even more interesting. Parameterizing the code shown with which edge classes to use, one can make it work for either horizontal or vertical manipulation, by just changing one variable:<br /><br /><pre><code>| sum scalar lastPosition edgeClass |<br />edgeClass := LeftEdge.sum := aBoxList inject: 0<br /> into: [:sum :each | sum + (edgeClass of: each) complementDistance].<br />scalar := sum /<br /> ((edgeClass of: aBoxList last) complementEdge position - (edgeClass of: aBoxList first) position).<br />last := (edgeClass of: aBoxList first) position.aBoxList do: <br /> [:each |<br /> | lowEdge |<br /> lowEdge := (edgeClass of: each) position: last.<br /> lowEdge complementEdge<br /> position: (last := lowEdge complementDistance * scalar + lowEdge position)]</code></pre><br /><br />This reified Edge object has enabled me to build layout algorithms that can be configured to work in either the x or y directions without having to have lots of code duplication.Travis Griggshttp://www.blogger.com/profile/01599271142862167244noreply@blogger.com0tag:blogger.com,1999:blog-7515306875906042828.post-78348074547975535622011-12-08T11:37:00.000-08:002011-12-08T11:40:14.716-08:00Call for STIC Talk InputI think I'm going to submit a talk proposal to <a href="http://www.stic.st/conferences/stic12/">STIC for the March conference</a>. There are lots of UI/Tools related things I could try and talk about, however there's only 45 minutes given for a talk slot. So I'm putting out a call for talk input. If you are coming to STIC this year, and have to listen to me for 45 minutes, what would I talk about to make the best use of your time?<br /><br />Some possible thoughts that have crossed my mind:<br /><br /><ul><br /><li>Here's how Skins work, the issues involved, and where we want to go with them</li><br /><li>Where 15 years of the VW UI framework has gotten us, how we got here, what we don't like, what we do like, and what we want to fix, and how</li><br /><li>10000 ft view of how VisualWorks UI framework fits together, do's and don'ts</li><br /><li>Rolling your own widgets. The old way. And the new way.</li><br /><li>Stand at the front of the room and let people throw bread rolls at me</li><br /></ul><br /><br />or something completely different?<br /><br />Private or public responses are fine, and thanks for them.Travis Griggshttp://www.blogger.com/profile/01599271142862167244noreply@blogger.com0tag:blogger.com,1999:blog-7515306875906042828.post-33816988221295612912011-10-06T23:11:00.000-07:002011-10-06T23:35:51.263-07:00Sounding Out the View TreeA couple of days ago, I asked myself the following question:<br /><br /><div style="text-align: center;">What would something like jQuery look like in Smalltalk?</div><br />What follows is a sort of experience report and overview of what I came up with. It took me about 3 iterations to get to what's described below. I'm unsure at this point whether it's really pretty good, completely wrong, or sorta there but sorta not.<br /><br /><h3>Motivation</h3><br />My motivation for putting this together was some disturbing patterns I see all to often in VisualWorks (and for many years). The first is the reliance on the builder variable. When the VW UIBuilder assembles an interface from specs, it hangs on to the widgets that had an ID associated with them. You can then use the componentAt: message to retrieve that in application code. Here's an example from CompiledCodeInspector.<br /><pre><code>changeSelectedField<br /> "..."<br /> self builder == nil ifTrue: [^self].<br /> w := self builder componentAt: #text.<br /> w == nil ifTrue: [^self].<br /> "..."</code></pre><br />There are a couple of different problems I have with this. First of all, there's the need to first check if we have a builder. Much of this type of code is writen with a "if the builders there, do this, otherwise who cares" approach. Then we turn around and do a similar check on the result to make sure it's not nil. Later on in this method, not shown, we have to send things like <i>widget</i> and <i>widget controller</i> to it to further manipulate it. #controllerAt:, #wrapperAt:, and #widgetAt: all exist to try and help smooth this out.<br /><br />We really shouldn't be having code needed to retain artifacts of the assembling process to get at widgets. That information ought to be encapsulated with the widgets themselves. But instead, we have to worry about whether we have enough state yet in two different places to determine whether we can act or not. And what we get back is... well it's a Wrapper. Usually. Of some sort. But usually we want the view object. And so we have to become intimately familiar with what was constructed and how it is related to be able to get at it.<br /><br />We also may have to worry about where it's actually stored at. In the case of subcanvasing, where a different builder is used to assemble subsets of UI, we end up with cases like found in the ClassCreationDialog, where we have to add an extra instance variable to keep track of those subbuilders, just so we can use the componentAt: methods. The widgets are there in the view tree, but alas, the only way to get at them is from the builder.<br /><br />To see examples of the second pattern that bothers me, do this.<br /><ol><br /><li>Open a System Browser</li><br /><li>Select all Packages using Ctrl-a</li><br /><li>Select the <i>Rewrite</i> tool (in the mid page tab control)</li><br /><li>Enter <i>``@r container container</i> in the <i>Search for:</i> box</li><br /><li>Click the <i>Search...</i> button and wait for the result</li><br /></ol><br /><br />One of the more egregious examples is found in SpinButtonController<br /><pre><code>pressAction<br /> | compositeView inputBoxController |<br /> "..."<br /> compositeView := view container container.<br /> inputBoxController := compositeView controller.<br /> (view spin: inputBoxController view model value) ifFalse: [^self].<br /> compositeView container container takeKeyboardFocus</code></pre><br />Two times in the same method, the code walks up the view tree. Not just with one <i>container</i> send, but with two. There's explicit assumptions being made here about the structure of the view tree. Needing to have one widget reach out and communicate with another is not terrible. But exploiting the particular structure of the view tree violates encapsulation. This kind of a code is a huge headache for us as we consider how to move beyond and reduce the amount of Wrappers in our view trees. Getting rid of WidgetStateWrappers is actually probably achievable, but every site like this will need to be fixed. The only information we should realistically be exploiting is that "somewhere above me is an object that fulfills a role I know about and I'd like to talk to it."<br /><br /><h3>Three Interesting Aspect of jQuery</h3><br />First of all, I am not a jQuery expert. I've played a little with it. Read about it. Asked questions about it. In those forays, there were three basic properties that intrigued me about it:<br /><ol><br /><li>Ability to specify a criteria to match against widgets in a DOM.</li><br /><li>The idea that it is always a collectionish thing that is returned, whether it has 0, 1, or many matches.</li><br /><li>Terseness, which allows the programmer to see more code that has to do with manipulating the matches, rather than lots of code finding matches.</li><br /></ol><br /><br /><h3>Building a Query Model in Smalltalk</h3><br />The first thing I set out to do was build a Query model for view trees. The <a href="http://www.w3schools.com/jquery/jquery_ref_selectors.asp">jQuery selectors</a> have at least 4 kinds of general queries.<br /><br /><ol><br /><li>Type (e.g. $("div") )</li><br /><li>Id (e.g. $("#cancelButton") )</li><br /><li>Class (e.g. $(".demoGroup") )</li><br /><li>Attribute (e.g. $("[checked ='false']") )</li><br /></ol><br /><br />I started with a basic Query object. A query can be asked if it <i>matches: aViewTreeElement</i>. Different subclasses capture different kinds of matches.<br /><br /><h4>Type Queries</h4><br />These are probably the easiest to map over. DOM's have things like div and table elements. VisualWorks view trees also have different types of elements, embodied as different Class behaviors. The only real difference, is that you have inheritance in the Smalltalk world. So you might want to match all subtypes of Wrapper. This is why I called it a TypeQuery instead of a ClassQuery. You can make one like<br /><br /><pre><code>Query type: Wrapper</code></pre><br /><br /><h4>Id Queries</h4><br />The skin work we've been doing in the next version of VisualWorks adds an <i>id/id:</i> API to VisualPart. It turns out that if we insert a leading line in the following UILookPolicy method<br /><pre><code>widgetWrapperWrapping: aWrapper decorator: aDecorator component: aComponent state: aWidgetState spec: aSpec colors: colors isOpaque: opaqueFlag inBuilder: aBuilder<br /> | sw dt |<br /> aComponent id: aSpec name.<br /> "...."</code></pre><br />Once that happens, any name of a widget given in the UIPainter is applied as the id of the actual view object (not one wrapper or another). You can make an IdQuery with<br /><br /><pre><code>Query id: #okButton</code></pre><br /><br /><h4>Class Queries (or Tag Queries)</h4><br />In css, which jQuery leverages, you can attach one or more arbitrary "class" attributes to an element. It's like being able to keyword tag subsets of widgets. One might, for example, have a set of widgets that all close a dialog: "Overwrite All", "Overwrite Older", "Overwrite None", and "Cancel". One could set the class of these buttons to be ".dialogAction".<br /><br />We currently have nothing like this in the VisualWorks view tree. But it might be nice to have. Because the word "Class" already has a very canonized and entrenched meaning in Smalltalk, I chose to use a different name for these. I called them tags. Three methods are added to VisualPart to support this:<br /><br /><ul><br /><li><i>tag: anObject</i> "adds anObject as a tag to the receiver"</li><br /><li><i>hasTag: anObject</i> "returns whether the receiver has anObject attached to it as a tag"</li><br /><li><i>untag: anObject</i> "removes anObject as a tag from the receiver, if it is there"</li><br /></ul><br /><br />I think my thought for these, like Id's, is that they would usually be Symbol objects, but there's actually nothing that says they couldn't be more complex objects. A TagQuery would be created with an expression like<br /><br /><pre><code>Query tag: #dialogActions</code></pre><br /><br /><h4>Attribute Queries (or Block Queries)</h4><br />I did not create something like Attribute Queries. The VisualWorks view tree elements don't have rigorously defined attributes. But they do respond to messages of course. So I just generalized this so that you could use a BlockClosure (actually any object that responds to cull:) to select elements. For example<br /><br /><pre><code>Query block: [:element | element isEnabled]</code></pre><br /><br />But one could do weirder things with it<br /><br /><pre><code>"all widgets in the lower half of a window"<br />Query block: [:each | (each localPointToGlobal: Point zero) y >= aWindow bounds center y]</code></pre><br /><br />would be used to select view tree elements that currently respond true to isEnabled. The astute reader will note that the previous three query's could all be emulated with a BlockQuery.<br /><br /><h4>Logical Combinators</h4><br />For full expression's sake, I also added a NotQuery, and AndQuery and an OrQuery.<br /><pre><code>(Query type: Button) | (Query id: #okField) "Either inherits from Button or has id of #okButton"<br />(Query type: Wrapper) & (Query block: [:each | each component isNil]) "All wrappers with no component set yet"<br />(Query tag: #header) not "Everything that is not tagged #header"</code></pre><br /><br /><h3>View Trees as Collections</h3><br /><br />a jQuery results in a collection of 0 or more elements that you can operate on. We already have a Collection API in Smalltalk. I wasn't interested in rolling a new or specific one of those. I'd rather leverage what I can do already with existing Collections. So the other half of this is the ability to create a collection facade to a view tree, rooted in one or more existing tree elements.<br /><br />ViewTreeSet is a subclass of Collection, it implements the necessary messages and inherits all of the other services that Collection already provides. jQuery (I believe) always searches the whole DOM tree, top down. Where our focus is at the specific widget level, I did not want to restrict it to that. Two specific classes currently exist for ViewTreeSet. One goes down the view tree, traversing the child elements of the roots, and the other goes up, traversing the parents up to the topmost element. Putting these two pieces together (the Query and the Collection), these also have a slot for a query. We can ask ApplicationModels, VisualParts, and Windows for either their <i>childViewTree</i> or their <i>parentViewTree</i>. Now we can do things like compute just how many Wrappers are in a current UI?<br /><br /><pre><code>(self childViewTree query: (Query type: Wrapper)) size</code></pre><br /><br />Or we could enable a bunch of widgets<br /><br /><pre><code>(self childViewTree query: (Query tag: #dialogAnswer))<br /> do: [:each | each enable]</code></pre><br /><br />Or programmatically drive the OK button<br /><br /><pre><code>(self childViewTree query: (Query id: #okButton)) do: [:each | each simulateMousePress]</code></pre><br /><br />Or tell a view's appropriate parent to takeKeyboardFocus without worrying how many layers away it is<br /><br /><pre><code>(self parentViewTree query: (Query type: WidgetWrapper))<br /> do: [:each | each takeKeyboardFocus]</code></pre><br /><br /><br />Since a ViewTreeSet is actually derived from a collection of objects, we can ask them for a <i>childViewTree</i> or <i>parentViewTree</i> as well, and then we can stack queries<br /><br /><pre><code>((self parentViewTree query: (Query type: BorderDecorator)) childViewTree<br /> query: (Query type: Scrollbar)) do: [:each | each invalidate]</code></pre><br /><br />In that example, we're first querying upwards to the nearest BorderDecorator, and then querying downwards to find all Scrollbars and forcing them to invalidate (redraw).<br /><br />I found myself struggling to come up with interesting and fun examples, so I apologize if these seem too esoteric. There's a sort of Chicken-and-Egg problem here. Without the utility, you don't think about what to do with it. And then you have it, and then you begin looking around to see if it's just a solution in search of a problem, if there's real value here. I think at this point, I'm not entirely sure yet.<br /><br /><h3>Trying to Sweeten Things Up</h3><br /><br />jQuery uses CSS for its selectors, which uses lots of infix/punctation characters to mean individual things and keep things pretty tight and terse. We could do something like that in Smalltalk too. But I was interested in seeing how much mileage I could get out of sticking to Smalltalk legal syntax.<br /><br />This is a tricky proposition I found. Basically, I anticipate that if the code I had to input to use these were terser, I'd be more likely to use them. But without having used them much, I'm left trying to guess and anticipate exactly what the syntactic sugar I might use would look like.<br /><br />I basically used 3 tricks to try and get things short and sweet (or bitter and sour maybe).<br /><br /><h4>Binary Selectors</h4><br /><br />I chose to reduce the common <i>childViewTree query:</i> part of my expressions to the single ? character. And to use ?? for the parent counterpart. The examples from above now become<br /><br /><pre><code>(self ? (Query type: Wrapper)) size<br /><br />self ? (Query tag: #dialogAnswer) do: [:each | each enable]<br /><br />self ? (Query id: #okButton) do: [:each | each simulateMousePress]<br /><br />self ?? (Query type: WidgetWrapper) do: [:each | each takeKeyboardFocus]<br /><br />self ?? (Query type: BorderDecorator) ? (Query type: Scrollbar)<br /> do: [:each | each invalidate]</code></pre><br /><br />I've toyed with implementing ?! and ??! as well which automatically send <i>not</i> to the argument. Note that the do: Blocks could be shortened by taking advantage of the fact that unary selectors can be <i>cull:</i>'ed in place of those blocks, but we're concentrating on the query part here, not what we do with the results.<br /><br /><h4>Overloading Literals</h4><br /><br />The second technique to shorten and simplify these, was to make assumptions about certain literal Smalltalk expressions. By having an <i>asUIQuery</i> method sent to arguments of these messages, we can coerce certain common Smalltalk objects into their Query counter part objects. I've added it to Symbol, Class, and BlockClosure. This allows us to shorten 4 of the above examples to<br /><br /><pre><code>(self ? Wrapper) size<br /><br />self ? #okButton do: [:each | each simulateMousePress]<br /><br />self ?? WidgetWrapper do: [:each | each takeKeyboardFocus]<br /><br />self ?? BorderDecorator ? Scrollbar do: [:each | each invalidate]</code></pre><br /><br />We can do blocks too<br /><br /><pre><code>self ? TextEditorView ? [:each | each displayContents isEmpty]</code></pre><br /><br />The problem I run into with this part is that I haven't devised a way to be able to use Symbols directly as standins for both Id's and Tags. Since we can commute spec names to id's easily, using Symbols directly as Id matches makes the most sense right now. I've toyed with ideas like letting the first character of the Symbol mean something. And generalizing it to CharacterArray. But I could go farther and make String arguments a whole embedded syntax similar to the CSS style that jQuery uses.<br /><br />Another area where this only goes so far, is that we can't put together complex queries this way. I'm not sure how often those would exist anyway. One could borrow more from the whole ? theme and add more binary selector sugar: ?& to and the argument with the receiver. But we wouldn't be able to do ?|; Smalltalk doesn't allow | as a binary selector, except for by itself.<br /><br />We could abuse things like Collections too, so that one could easily assemble or lists, something like<br /><br /><pre><code>self ? #(#okButton #cancelButton) do: [:each | each flash]</code></pre><br /><br />From my little playing around, I like the basic techniques here, but I'm not sure how far it scales. Or how far it needs to scale.<br /><br /><h4>Adding Common Actions</h4><br /><br />Assuming that 90% of the time, we might use something like this to do some common widget behavior, I've added some of those directly to the ViewTreeSet. So then some of the examples from above can be simplified as<br /><br /><pre><code>(self ? (Query tag: #dialogAnswer)) enable<br /><br />(self ?? BorderDecorator ? Scrollbar) invalidate</code></pre><br /><br /><h3>Beyond Cheesy Examples</h3><br /><br />I did sit down and try to use this on some actual real existing code in the system. The ClassCreationDialog was a good case. It tries to be relatively interactive, and was written by a programmer whose skills I esteem highly: Vassili Bykov. To accomplish what he wanted with VisualWorks standard approaches (plus some technique he added), he had to not only work thru a builder, but retain a reference to intermediate sub builders, so that he could hunt down and update widgets. And a number of helper methods had to be written. I was able to get rid of an instance variable and a family of helper methods, replaced with one longer method, but more expressive (IMO). Here's a screenshot of some of the differences made:<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSO5tyWEZZ4b1LsF4ejdZgz9nM31RkMZ6cBl5GtaiSiItjSbVhESNsLnf-9xqHrnnLpVpeaNtn80divhc39nmoY2i0JPYwCgZr3Q-SWM0o__gAC57cskctTj6yBUPowFL3NcZMlz3vcEo/s1600/ClassCreationDialogOne.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 1192px; height: 380px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSO5tyWEZZ4b1LsF4ejdZgz9nM31RkMZ6cBl5GtaiSiItjSbVhESNsLnf-9xqHrnnLpVpeaNtn80divhc39nmoY2i0JPYwCgZr3Q-SWM0o__gAC57cskctTj6yBUPowFL3NcZMlz3vcEo/s1600/ClassCreationDialogOne.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5660633078877060514" /></a><br /><br />Followed by a screenshot that shows a sample of the kind of change that is made to the longer <i>validateEverything</i> method<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwLbHzQFRrBhdxmAPW7vn_vnBAg2DbhWW7ajd9OJayLmXLwDianc5pZnovPYHYpYeI-BrGyAAfPxU4zrnbKQW6FkCpPmaY3jOkBoDnFXoUZDuKZBiMdwHYTQjElvUD2MNeumL19MaHXp4/s1600/ClassCreationDialogTwo.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 786px; height: 159px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwLbHzQFRrBhdxmAPW7vn_vnBAg2DbhWW7ajd9OJayLmXLwDianc5pZnovPYHYpYeI-BrGyAAfPxU4zrnbKQW6FkCpPmaY3jOkBoDnFXoUZDuKZBiMdwHYTQjElvUD2MNeumL19MaHXp4/s1600/ClassCreationDialogTwo.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5660633085062117586" /></a><br /><br />The code on the right is longer, but it is no longer so long and involved that it needs to use a helper method to do what it does.<br /><br /><a href="http://mlucassmith.tumblr.com/">Michael Lucas-Smith</a> suggested I take a look at some of the methods he put together for xPressly, which was used to give his Smalltalk Solutions presentations on xTreams. He had some methods that were forced to do lots of container container container types of things. So I modified it to use this query stuff. It improves the container container nonsense, but it brings into that much more relief how silly it is that you have to go running around in other object to essentially manipulate the one you intended. Fixing <b>that</b> is a problem for another day unfortunately.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSeG3HSq3NfxZEAZdAxA-U2yCgQJLPxPYxMoUXeB4BdhItgh9raLV2s9i4-gmIiXu2CM7lpnpT-UwS7iaiBbOP-QllHla5yTEtj6EIHSX6P693iowHNU1BMppPHHFnhiZRWkg6u8JK0J4/s1600/xPresslyOne.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 930px; height: 507px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSeG3HSq3NfxZEAZdAxA-U2yCgQJLPxPYxMoUXeB4BdhItgh9raLV2s9i4-gmIiXu2CM7lpnpT-UwS7iaiBbOP-QllHla5yTEtj6EIHSX6P693iowHNU1BMppPHHFnhiZRWkg6u8JK0J4/s1600/xPresslyOne.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5660633087442149442" /></a><br /><br /><h3>Current Conclusions</h3><br /><br />I don't have any yet. I need more data. There are parts of this I feel right about, other parts I'm not so sure. I'd love to get some feedback. Does this just all sound like a bad idea? Or maybe good idea in spirit, but misguided in implementation? Generally warm to it, but issues with certain details?<br /><br />If you have some code in the Open Repository that you'd like to see how this would change, I'd love to take a crack at modifying it as a way of taking this stuff for a spin, if your stuff isn't too involved.<br /><br />Also, I'm still looking for a good name for this stuff. I've currently called it WidgetPath, which I think is a dumb name.Travis Griggshttp://www.blogger.com/profile/01599271142862167244noreply@blogger.com9tag:blogger.com,1999:blog-7515306875906042828.post-81636127632663384312011-09-23T16:14:00.000-07:002011-09-25T21:47:54.562-07:00One of the Best Bits of Programming Advice I ever GotYears ago (early 1992), I attached myself to this crazy skunkworks project that was using this weird language called Smalltalk. "Object Oriented" was in its infancy as a "hot" item. High paid consultants. Lots of people laying claim to what this new object religion was all about. This was 5 years before <a href="http://en.wikipedia.org/wiki/Alan_Kay">Alan Kay</a> would make the statement "I invented the term 'Object Oriented Programming' and this {<span style="font-style:italic;">Java and C++</span>} is not what I had in mind."<br /><br />Shortly after hooking up with this whacky group with the whacky language, still confused about what the difference was between an instance variable, a class variable, and a class instance variable, I found myself in a training course taught by Russ Pencin, of ParcPlace. Russ would say something that I didn't really appreciate at the time. Despite not understanding the point behind this sage advice, I endeavored to follow it. It would take years of experience and exposure to appreciate it's value. The advice?<br /><br /><div style="text-align: center;"><i><b>Don't make objects that end with 'er'.</b></i></div><br />That's it. The OOP paradigm sprang to life amidst of a culture of what we called "procedural programming." Now days we don't talk so much about the comparison between the two paradigms. Probably in part because Object Oriented languages are now a dime a dozen. The OOP religion, in a multitude of flavors won out. Sadly, I often find myself echoing words I heard <a href="http://en.wikipedia.org/wiki/Adele_Goldberg_(computer_scientist)">Adele Goldberg</a> say around 2000: "Now days we have lots of Object Oriented Programming, but not so many Object Oriented Programmers". If there was one piece of advice I would pass on to the hordes of would be Object Oriented Programmers, it would be the sage advice offered by Russ: "Don't make objects that end with 'er'."<br /><br />What's in a name anyway? Why is this worth getting excited about? What I've discovered over the years, is that the jst of OOP is that we bind behavior to data. As long as you haven't joined in the Functional Monks in their Monasteries of Statelessness, programs are made of behavior and data. In classic structured/programming, we concentrate on behavior (verbs), and then figure out what data (nouns) we need to make it all work. In other words, we bind data to behavior. But in OOP, we make the locus of programs be the nouns, the data, and then we figure out what kind of behavior we can bind to them, and hope that the problems we hope to solve gel out of the emergent behaviors.<br /><br />I recently posited to a colleague that in nearly every "er" object case, there was a better name for it. And that giving it a better name would tend to make the design more encapsulated, less spaghetti code, in short more object oriented. It's not a hard and fast rule, but there are a lot of cases where it can improve things.<br /><br />Take some sort of "Loader" for example. The focus here is on the unit of work it does. It'll have lots of instance variables, lots of arguments, and pass lots of data around probably. Now instead replace that with a LoadRecord and a LoadStream. I'm reasonably confident you'll end up with something that is more akin to what the original Founding Fathers of OOP had in mind. We want to create objects that describe what they are, and then bind behavior to them, rather than focus on what they do, and then figure out what data they'll need to do that.<br /><br />Some er's that I've learned to avoid over the years:<br /><br /><div><ul><li>Managers - Every time I see one of these, I cringe. People will usually tell me what it does, long before they can tell me what it is. Is it a registry? Fine call it a registry. Is it a history or a log? Call it that. Is it a factory? Call it that.</li><li>Controllers - Only good controller object I've made in the last 20 years was an interface to a BallastVoltageController that represented a real world object. The fact that every single MVC implementation in the world has had a different role for Controller ought to tell us something about how well that idea fit.</li><li>Organizer (and many like them) - Focus is on what it does. This is a great example of how easy it is to turn many of these 'ers' into nouns. Call it an Organization. Now we're focusing on what it is.</li><li>Analyzer/Renderer/etc - Definitely examples of "worker" objects. What if they had been Analysis/Rendering/etc.</li><li>Builder/Loader/Reader/Writer/etc - Remove the focus from the objects being manipulated, and tend assume to much responsibility themselves.</li></ul><div>There's lots of exceptions to such a rule of course.</div><div><ul><li>There are lots of noun words that end in 'er'. Register. Border. Character. Number. If it's really a noun, fine.</li><li>There are many 'er' words that despite their focus on what they do, have become so commonplace, that we're best to just stick with them, at least in part. Parser. Compiler. Browser.</li><li>When you are trying to model a domain object that ends in 'er'. I'm fine with a Manager subclass of Personel, which is there to refine a type of personal that has management behavior to it.</li></ul><div>Your mileage may vary, I'm sure there are those that disagree with this. Until you apply the mindset for a while though, you'll never really know. Give it a whirl on one of your projects/designs and see what happens.</div></div></div>Travis Griggshttp://www.blogger.com/profile/01599271142862167244noreply@blogger.com59tag:blogger.com,1999:blog-7515306875906042828.post-40023564885449865452011-09-21T17:47:00.000-07:002011-09-21T18:12:52.218-07:00Don't Try This At Home: Stealing from the stackI think there are days, when I want to do things I know I shouldn't as a programmer. Do others experience this. Some have said that Smalltalk is like a gun, that "with great power comes great responsibility." Some times, some of the tricks tempt me, and if I know no one's looking (read: I'm not going to be putting this in any production code), I find myself looking around for opportunities to flex a little bit of language super power muscle. Just for the grins. Just because I can.<br /><br />Messing with <span style="font-weight:bold;">thisContext</span> and the stack is one of those things. When I was implementing the _1:_2:_3:_4:_5: message I was talking about the <a href="http://objology.blogspot.com/2011/09/syntactic-tartness-for-macro-expansion.html">other day</a>.<br /><br />The proper and boring way to implement it was like this:<br /><pre><code>_1: one _2: two _3: three _4: four _5: five<br /><br /> | arguments |<br /> arguments := Array new: 5.<br /> arguments at: 1 put: one.<br /> arguments at: 2 put: two.<br /> arguments at: 3 put: three.<br /> arguments at: 4 put: four.<br /> arguments at: 5 put: five.<br /> ^(StringParameterSubstitution default)<br /> originalString: self;<br /> args: arguments;<br /> expandedText</code></pre><br /><br />But, I didn't want to do that. That was too much typing I think. I wanted to be clever, so I did this instead:<br /><br /><pre><code>_1: one _2: two _3: three _4: four _5: five<br /><br /> ^(StringParameterSubstitution default)<br /> originalString: self;<br /> args: (thisContext stack copyFrom: 1 to: 5);<br /> expandedText</code></pre><br /><br />There are no references to any of the method arguments. Knowing that the stack is already an array with the arguments already placed in them, exactly what I want, I just grab that, instead of making my own array populated with the method arguments.<br /><br />Don't do this in production code. It's tricky and evil. But sometimes, it's good to remind yourself, or learn from others, what this great environment really is capable of doing. Who knows, having one be aware of it, there may come a point where playing with thisContext or the stack, may help you solve a real problem, in production code, or not.Travis Griggshttp://www.blogger.com/profile/01599271142862167244noreply@blogger.com1tag:blogger.com,1999:blog-7515306875906042828.post-70964509927069487152011-09-21T17:37:00.000-07:002011-09-21T17:45:44.479-07:00A thought about field identifiersWhen I was playing with template field substitution last couple of days, I was again reminded that the common Smalltalk substitution syntax (John Brant informs me that it is indeed in more than just VisualWorks and Squeak) is really frustrating to use for any sort of HTML/XML generation. The use of the < and > means you have to constantly escape those characters in your templates if you are generating any kind of output that you actually want to include the alligator brackets.<br /><br />And a thought occurred to me. As long as I'm not generating HTML or XML, I couldn't think of a better field identifying character to use. There are some others such as { } or [ ] that work visually as well. But for the last 15+ years, we've all been reading more and more and more of the "when Lisp met alligators" syntax. It's been pounded mercilessly into our brains. We've all gotten quite accustomed to parsing, in our heads, the text shows up between brackets, apart from the rest. Because of that, using them as field identifiers, is actually the best thing possible.<br /><br />It's the meta that's the achilles heel. When you want to use these same field separators to generate other field separators. In other words, I could posit that if HTML/XML had used { } to enclose tags, we'd find ourselves appreciating it in a substitution syntax the most, and hating it most when we tried to use it to generate more of the same.Travis Griggshttp://www.blogger.com/profile/01599271142862167244noreply@blogger.com3tag:blogger.com,1999:blog-7515306875906042828.post-61039270532028062092011-09-20T00:13:00.000-07:002011-09-20T00:26:19.068-07:00A Tragedy: When Localization met Interpolation (repost)<span style="font-style:italic;">This is <a href="http://www.cincomsmalltalk.com/userblogs/travis/blogView?showComments=true&printTitle=A_Tragedy:_When_Localization_met_Interpolation&entry=3401021725">a repost from my old blog</a>. When I end-of-lifed that Blog, I said might pull some of those over here. Lukas's comments on the previous <a href="http://objology.blogspot.com/2011/09/syntactic-tartness-for-macro-expansion.html">Syntactic Tartness for Macro Expansion</a> reminded me of this one. At the time I wrote that, I failed to give credit to Steve Dahl, who I had spent about 2 days kicking ideas back and forth with on Skype about this.</span><br /><br />Adrian Kuhn commented in a previous post:<br /><br />"In general, I think that Smalltalk desperately misses String interpolation!"<br /><br />Yes, I agree. But the devils running amuck in the details. The problem is that I18n translation made it first. There's two ways that I've seen to do translation: 1) statically recompile your program, making a sweep of all (or subset) of the strings in the program and translating them 2) doing translation at runtime, by looking up the string in some database. The first is pretty old school. :)<br /><br />The challenges with String Interpolation are a few:<br /><br /><h3>Far Reaching Changes</h3><br />You need to modify the compiler. You might be able to try to have a unary message that evaluates a string, compiling expressions on the fly in the context of the sender. In this case, the tools can do nothing to help you get the code right. They get confused, because you create variables that don't appear to be referenced. You can't do it right in the context of clean closures. So a parser/compiler change is definitely in order. And don't forget the usual rant, it's not enough to modify just the base compiler. You've got to look at it in the context of the debugger's ability to put breakpoints as well as step through code, in the context of the RB parser, which you want for rewrites and formatting, as well as code highlighting maybe. This does not make it undoable. It simply means it's not a quick thing you can whip up over the weekend.<br /><br /><h3>What Is It When?</h3><br />So lets say you have the expression:<br /><pre><code>string := '[[customer name]] is [[customer age]] years old'.</code></pre><br />If you use it as the argument of a nextPutAll: send, you probably want it to be in expanded form. But, if you want to look it up in an I18n dictionary, then you would rather have it in symbolic form.<br />You could detect string literals with whatever the expression preamble is ([[ in the examples above) at compile time and build some sort of complex literal that held both blocks as well as a string. But you'd still have to figure out how to resolve the message =. In an assert: form, you'd probably want expanded, but at dictionary look up time for translation you'd want the compressed form.<br /><br /><h3>What Happened Elsewhere</h3><br />It's been interesting to spend a little time looking at how Ruby and Python do these. <a href="http://www.yotabanana.com/hiki/ruby-gettext-faq.html#General+Questions">This page</a> makes it clear that the Ruby gettext guys also understood that it is hard to have your cake and eat it too when it comes to mixing interpolation and localization. The answer isn't as definitive with Python, but I think I've convinced myself it's a similar story after looking at docs for a little while.Travis Griggshttp://www.blogger.com/profile/01599271142862167244noreply@blogger.com0tag:blogger.com,1999:blog-7515306875906042828.post-55183052412114621132011-09-19T10:53:00.000-07:002011-09-19T22:27:07.965-07:00Syntactic Tartness for Macro Expansion<div>One thing that is very natural for Smalltalk image based programming, is to programmatically assemble source and install it into the very same running program. I've been using the RB Change framework to do just that with something I'm working on lately.</div><div><br /></div><div>To piece together the appropriate source, working with a template source and string, and then fill in the variables is something that's desirable. VisualWorks and Squeak (maybe some other Smalltalk use this same approach?) have an ability to expand templates strings with macro substitution. The template for a setter method might look something like</div><br /><pre><code>'<1s>: anObject<br /> <1s> := anObject'</code></pre><br />To fill out those fields, you send messages like <i>expandMacrosWith:</i>, <i>expandMacrosWith:with:</i>, <i>expandMacrosWith:with:with:</i>, and <i>expandMacrosWithArguments:</i>.<br /><br />I am no fan of this API. First, it is too verbose. When I'm looking at template substitution, I don't want a bunch of other longish selectors. They dilute the information I'm trying to glean as I piece together the template and what's being substituted.<br /><br />Secondly, I find it doesn't scale well when evolving the code. It's common that I start with a simple template in the first cut of code. Something like<br /><pre><code> 'Hello <1s>' expandMacrosWith: aName</code></pre><br />But as I refactor and discover more needs, the need to add parameters arises. As long as I only add two more parameters, I can just use the variants with the additional <i>with:</i> keywords.<br /><pre><code>'<1s> ^self <2s> <3s>'<br /> expandMacrosWith: aVariableName<br /> with: aBasicAccessingMethod<br /> with: sizeof + 1</code></pre><br />As soon, as I go to 4 fields though, I have to change gears and use the <i>expandMacrosWithArguments:</i> API and build the sequence myself. It could be argued that it's best to just always start with this version, but it's the very longest of the selectors. If you're using VisualWorks, and don't have the language syntax for array construction (e.g. {statement. statement. statement.}), then it's even funner, because you can use the <span style="font-style:italic;">Array with:with:with:with:</span> expression, but if you need to move to 5 fields, then you've got to change your code all around again.<br /><br />Over the years, I've tried a couple of different experiments to make this all something I liked a little better. They've used involved interesting binary selectors (e.g. "%") and proxy objects, or at least fun with doesNotUnderstand: messages. I thought I'd try something a little different. I wanted something that correlated well with the numbered fields, but was uber-terse as well.<br /><br />So I went with the shortest selector that could possibly work: _1:, _1:_2:, _1:_2:_3:, etc. Written in selector shorthand like that, it's pretty ugly. When actually used in code though, it improves some:<br /><pre><code>'<1s>At: anOffset<br /> ^self <2s> anOffset * <3p> + <4p>'<br /> _1: aVariableName<br /> _2: aBasicAccessingMethod<br /> _3: aByteSize<br /> _4: sizeof + 1</code></pre><br />and<br /><pre><code>'inspector<1s>Field<br /> %<inspectorFields><br /> ^Array with: (Tools.Trippy.DerivedAttribute<br /> label: ''<2s>''<br /> valueBlock: [self <2s>])'<br /> _1: upperVariableName<br /> _2: aVariableName</code></pre><br />and<br /><pre><code>'<1s><br /> ^(0 to: <2p>) collect: [:n | self <3s> <4p> + (n * <5p>)]'<br /> _1: aVariableName<br /> _2: anInteger - 1<br /> _3: aBasicAccessingMethod<br /> _4: sizeof + 1<br /> _5: aByteSize</code></pre><br />I don't dare call this syntactic sugar. The use of the underscores is too ugly to be sugary. It's a sort of bitter sweet thing, thus the "tart" label.<br /><br />It does solve two problems nicely. It is very terse. You see a minimal amount of "scaffolding" getting the job done, and are free to spend more time looking at the template and the substitutions. One could get that though by using inlined arrays with a shorter selector, something like:<br /><pre><code>'<1s><br /> ^(0 to: <2p>) collect: [:n | self <3s> <4p> + (n * <5p>)]' macro: {<br /> aVariableName.<br /> anInteger - 1.<br /> aBasicAccessingMethod.<br /> sizeof + 1.<br /> aByteSize.}</code></pre><br />One thing you lose with this though, is the strong association between each substitution and field. In the former example, when I glance at the template and see field 4, and then want to know what's being substituted, I see it instantly. I just find the 4 in the selector below. Without that direct link, I have to parse the array linearly to find it.<br /><br />I haven't used this _syntax enough to decide if it's usefulness would overcome its ugliness, but it was interesting to play with it and discover the visual readability aspect. I'll likely use it on some more non-production stuff to get a better feel.Travis Griggshttp://www.blogger.com/profile/01599271142862167244noreply@blogger.com3tag:blogger.com,1999:blog-7515306875906042828.post-1079629544961871892011-08-23T16:39:00.000-07:002011-08-23T18:07:16.774-07:00I've Inherited an Interesting DilemmaConsider two browser screenshots. The first is the class side of AlphaBlendedIcons, with the needsComment method selected.
<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYBsKKArvsGlZ3WdjpU9o-DH8fXC0n32GleRdz7dd-IjkVvAWO0giR2Ncmgkifh6ITWs9n4cDlWG6u-uuAdYRd1R0fa-Jo2uQdiZBaSWCxYKa4Q7VOFaMR9Dy_zmSTXI-_cR6ESpsgZfg/s1600/AlphaBlendedIconsHierarchy.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 622px; height: 233px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYBsKKArvsGlZ3WdjpU9o-DH8fXC0n32GleRdz7dd-IjkVvAWO0giR2Ncmgkifh6ITWs9n4cDlWG6u-uuAdYRd1R0fa-Jo2uQdiZBaSWCxYKa4Q7VOFaMR9Dy_zmSTXI-_cR6ESpsgZfg/s1600/AlphaBlendedIconsHierarchy.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5644201321510095266" /></a>
<br />
<br />This second is the instance side of NameSpace, the needsComment method selected.
<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnG_011zSMVwb4D_T9QDqEQuNRnq9O2P6sFT6__2IyvsRPEksdBmqaoRxBKheO26oL308BSPmaHubi4DWFQrxgPz9_sZjpUT34T6iElLIAYx7NTzEOyuxOMQJderWt7GSZ12ne5oaF4CA/s1600/NamespaceHierarchy.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 623px; height: 257px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnG_011zSMVwb4D_T9QDqEQuNRnq9O2P6sFT6__2IyvsRPEksdBmqaoRxBKheO26oL308BSPmaHubi4DWFQrxgPz9_sZjpUT34T6iElLIAYx7NTzEOyuxOMQJderWt7GSZ12ne5oaF4CA/s1600/NamespaceHierarchy.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5644201311367408434" /></a>
<br />
<br />In both cases, if you invoke the menu of the selectors list, and choose the <i>Hierarchy Implementors</i> option, you get the following two results, respectively...
<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZwZIs-zO9y6j3ZTMi-Qq-PMCmno8Uay5wY6Lqi2fpp5g_x9LNk_QqQrepMdqv5cG4QD45tSUZV8-Rug8M43XuLVPrb0jyqDApInZMmMBkUWkWGnydnpsGO6i1rQAAlvOkwqoRgRgrxY8/s1600/AlphaBlendedIconsImplementors.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 389px; height: 172px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZwZIs-zO9y6j3ZTMi-Qq-PMCmno8Uay5wY6Lqi2fpp5g_x9LNk_QqQrepMdqv5cG4QD45tSUZV8-Rug8M43XuLVPrb0jyqDApInZMmMBkUWkWGnydnpsGO6i1rQAAlvOkwqoRgRgrxY8/s1600/AlphaBlendedIconsImplementors.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5644201307708427618" /></a>
<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkFjt1sKG3XlbmpZtQHyeI9f8JHXitNwHnWhWizK0E1d4pFkG8RNphq9KTcar0iyXtdlDQpLCURc5zYKwP59j1L9PO6ZT4NPUp432jewkyGPGsTDqEgjUiR_HAOG7VdLNr2U6onVn4IXo/s1600/NamespaceImplementors.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 425px; height: 170px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkFjt1sKG3XlbmpZtQHyeI9f8JHXitNwHnWhWizK0E1d4pFkG8RNphq9KTcar0iyXtdlDQpLCURc5zYKwP59j1L9PO6ZT4NPUp432jewkyGPGsTDqEgjUiR_HAOG7VdLNr2U6onVn4IXo/s1600/NamespaceImplementors.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5644201313486928274" /></a>
<br />
<br />So what's the issue? The issue is the presence of ClassDescription in the Implementors browsers. Should it be there or not?
<br />
<br />On the one hand we have the notion of "hierarchy." In RB land, a <i>Hierarchy Browser</i> is a pretty common thing. It is a limited view of classes always going as high as Object. If you are on the instance side, it tops at Object. If you are on the class side, it tops at Object class. From this point, where the term "Hierarchy" refers to a browsing view or scope, it is surprising to see these entries for ClassDescription here when you ask for <i>Hierarchy Implementors</i>. What you expect to see, is only methods that you'd find as you navigated around in the Hierarchy Browser.
<br />
<br />Then there is a more pedantic side. It tends to be those who like to think of themselves as "I have ascended into the thirteenth circle of Smalltlak Uber-Knowledge." They know the zen-secret that there's a "crossing of the streams" that occurs at Object class. When a method is not found at Object class, the lookup for the method doesn't stop there, but continues up on the <i>instance</i> side at Class, then ClassDescription, then Behavior, and finally Object (instance). The little up/down arrows we place next to methods, even show this. If you look at the first one, it shows that there is a super implementor of <i>needsComment</i> for AlphaBlendedIcons, that is found farther up the search tree.
<br />
<br />These two notions of "hierarchy" when viewed from the class side, are at odds with another. When someone sees that menu option, which kind of "Hieararchy" are they more likely to think about at that moment. Should it be left the way it is, but reword the menu option to be "super/sub implementors?" What would you want it to do?
<br />
<br />
<br />
<br />
<br />Travis Griggshttp://www.blogger.com/profile/01599271142862167244noreply@blogger.com3tag:blogger.com,1999:blog-7515306875906042828.post-11417879781160007972011-07-21T10:36:00.000-07:002011-07-21T11:01:31.832-07:00OSX Lion: First Day ThoughtsI downloaded and installed <a href="http://en.wikipedia.org/wiki/Mac_OS_X#Version_10.7:_.22Lion.22">Lion</a> yesterday. Mostly, I'm still acclimating before drawing judgements and throwing darts. But one thing stands out already.<br /><br />There's this great story about <a href="http://www.folklore.org/StoryView.py?story=Round_Rects_Are_Everywhere.txt">how Steve Jobs convinced Bill Atkinson that rectangles with rounded corners were a good thing</a>. The idea being that we as humans tend to prefer them and so would naturally prefer them in our UIs. The rest is history. Over the years, more and more of our UI elements get sanded off corners.<br /><br />There is another pendulum that's been swinging along the Apple UI Design vector that annoys me. Color. Or lack thereof. It's no secret that Apple uses some of its frontline apps to feel out UI evolutions. Back in 2006, I remember discussing the latest iTunes version, and describing what I saw as an attempt on Apple's part to create "Lickable Motiff."<br /><br />The first thing that keeps hitting me with the subtle changes made to Mail and Finder, are that they both seem to have been through another bleach cycle. Mails top button bar has had all hue rinsed from it. Finder's side bar is nearly colorless. I keep thinking "Those icons are just glyphs from some foreign language. Ignore them." Maybe I should just spend more time in Terminal and use good ol' <span style="font-weight:bold;">ls</span>.<br /><br />I understand some of the influences that drive Apple designers to do this. It's easier to harmonize sets of icons if you use less colors. It helps deal with colorblindness issues. Etc.<br /><br />On the other hand... I'd like to take the Apple UI designers on a walk around their campus. And point at things. And say "See? Color!" If we get rounded rectangles (problematic as they are to graphics libraries) because they show up in real life, why can't we have some color too?<br /><br />Who knows, maybe everyone at Apple wears black turtlenecks now, and they all really see less color. Yeah, maybe that's it.Travis Griggshttp://www.blogger.com/profile/01599271142862167244noreply@blogger.com0tag:blogger.com,1999:blog-7515306875906042828.post-72013343614761119562011-07-14T09:12:00.000-07:002011-07-14T10:36:10.322-07:00Rope: an evolved ChainOne of the areas my <a href="http://objology.blogspot.com/2011/06/kicking-hash-out-of-dictionary.html">Chain</a> did not come out ahead was in the area of adding truly new keys to a Dictionary (as opposed to just updating what already was stored at a given key) or removing them. Both of these operations potentially involve <span style="font-style:italic;">#become:</span> sends. While VisualWorks <span style="font-style: italic;">#become</span>: is very fast, it's not as fast as some other operations. And not all Smalltalk implementations have a fast <span style="font-style: italic;">#become:</span>. Here's the chart of how well Chain compared to IdentityDictionary for original key add/removal.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvLUMAEjiDI-_XXJ3MgslPciSSNmiRzu6G9-2rMQL9ZlEVjaDh39Lru8zOE7bOQUMBCKM8KkIDmU-uRKTMpYBuELYUYvUagtoqrMy-tArjnJ_62hAhYjET9IZjkS3ePcnO3FJ1TIlXHvs/s1600/chart.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 600px; height: 75px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvLUMAEjiDI-_XXJ3MgslPciSSNmiRzu6G9-2rMQL9ZlEVjaDh39Lru8zOE7bOQUMBCKM8KkIDmU-uRKTMpYBuELYUYvUagtoqrMy-tArjnJ_62hAhYjET9IZjkS3ePcnO3FJ1TIlXHvs/s1600/chart.png" alt="" id="BLOGGER_PHOTO_ID_5621904623800205906" border="0" /></a><br />While I was on vacation, John Brant pinged me encouraging me to use a <span style="font-style: italic;">#changeClassTo:</span> strategy instead of #<span style="font-style: italic;">become:</span>. To do so, first I cloned the <span style="font-style: italic;">Chain</span> class as a new class called <span style="font-style: italic;">Rope</span> (and the <span style="font-style: italic;">Empty</span> variant as well).<br /><br />Then we make <span style="font-style: italic;">EmptyRope</span> a subclass of <span style="font-style: italic;">Rope</span>. This is important, because we need an <span style="font-style: italic;">EmptyRope</span> to have the same instance variable layout as a <span style="font-style: italic;">Rope</span>. <span style="font-style: italic;">#changeClassTo:</span> will only change the type or class of an object, if they have the same instance variable layout. We'll just make sure that never actually set or reference the <span style="font-style: italic;">key</span>, <span style="font-style: italic;">value</span>, or <span style="font-style: italic;">nextKnot</span> variables in the <span style="font-style: italic;">EmptyRope</span> subclass.<br /><br />In the case of adding a new key/value pair, when our <span style="font-style: italic;">#at:put:</span> reaches the end of the list, the <span style="font-style:italic;">EmptyRope</span> subclass can just implement it as<br /><pre><code>at: aKey put: anObject<br /> self<br /> setKey: aKey<br /> value: anObject<br /> next: EmptyRope basicNew.<br /> self changeClassTo: Rope<br /></code></pre>And the only thing #removeKey: has to do in addition to the original #replace: trick is do a<br /><pre><code> previousKnot changeClassToThatOf: self</code></pre>to get the last link to be an EmptyRope.<br /><br />Most of the rest of the code stays the same. We can reduce a couple of duplicates since the classes are now in a parent-child relationship, rather than a sibling one.<br /><br />The at:put:/removeKey: test is the interesting one to rerun and see how things fair. First of all, we compare the new <span style="font-style:italic;">Rope</span> approach to the original <span style="font-style:italic;">Chain</span> one.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzk5_vgl9WNzXiMAmqc_Q82LQKxjqY3CVRQI_EnvBPWSHkv0B0_PKYcdgKMcJPrvH8IaMHucqhabHa2Ij0zKTAspQnliKxtYuZAfvWIX4PEF8QZunFBMAkKVu2ZB5vlWq8-uEUT883Q_A/s1600/chart2.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 600px; height: 150px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzk5_vgl9WNzXiMAmqc_Q82LQKxjqY3CVRQI_EnvBPWSHkv0B0_PKYcdgKMcJPrvH8IaMHucqhabHa2Ij0zKTAspQnliKxtYuZAfvWIX4PEF8QZunFBMAkKVu2ZB5vlWq8-uEUT883Q_A/s1600/chart2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5629257473849362994" /></a><br />That shows some improvement across the board. The real test though, is how does it compare to the original IdentityDictionary?<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYcGhyphenhyphen0_gtEeDK4qr6oVI8FawPV2c3eub12ARJmxb0Igp1nKNbYaT_GDi9gxkTMxUVJGxGM7fvwa5i7VKNrgzw0L_4ANJPUK2w2VAHc5Pfm3nnERjvt8OLhi7oOMigE1qVx1JaXGU3HS8/s1600/chart1.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 600px; height: 75px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYcGhyphenhyphen0_gtEeDK4qr6oVI8FawPV2c3eub12ARJmxb0Igp1nKNbYaT_GDi9gxkTMxUVJGxGM7fvwa5i7VKNrgzw0L_4ANJPUK2w2VAHc5Pfm3nnERjvt8OLhi7oOMigE1qVx1JaXGU3HS8/s1600/chart1.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5629258483585426050" /></a><br />The good news is, it's actually faster. Not by a huge amount. But it removes it from something you have to worry about.<br /><br />I've replicated the Rope code up to the Open Repository (package <span style="font-style:italic;">TAG-Ropes</span>). If you know you're using smallish dictionaries (size <= 15) and need to care about performance, then this might be the thing for you.<br /><br />It would be interesting to see how such an approach fared in <a href="http://smalltalk.gnu.org/">GNU Smalltalk</a> or one of the other Smalltalks.Travis Griggshttp://www.blogger.com/profile/01599271142862167244noreply@blogger.com1tag:blogger.com,1999:blog-7515306875906042828.post-6738338042576443632011-07-13T18:02:00.000-07:002011-07-14T13:40:20.438-07:00Syntax Highlighting your Blog with highlight.jsI've had some people ask me how I got syntax highlighting for Smalltalk code on my blogger blog. Here's what I did:<ol><br /><li><h4>Download highlight.js</h4>Go to <a href="http://softwaremaniacs.org/soft/highlight/en/download/">Software Maniac's download page</a>. Turn off the languages you're not interested in. Check the checkbox for Smalltalk. And hit the download button. You'll get a zip file that when unarchived has a whole directory structure of files, including the customized highlight.js file for your efforts. You need this customized version because it doesn't support Smalltalk in the stock version.</li><br /><li><h4>Host it somewhere</h4>Find a net presence to host it at. Hopefully, you've either got your own or know someone who can host it for you. I was fortunate to fall into the second category.</li><br /><li><h4>Update your blog template</h4>Edit your blog template (in <b>Edit HTML</b> link under the <b>Design</b> tab of your blog. You add the following lines near the end of your template<br /><pre><code><br /> <script src="http://yourhostedsite/highlight.pack.js" type="text/javascript"><br /> <script type="'text/javascript'"><br /> hljs.initHighlightingOnLoad();<br /> </script><br /></code></pre><br />You need to edit the yourhostedsite part appropriately.<br /></li><br /><li><h4>Add additional CSS for your blog</h4>You need to choose a css style you like from the downloaded package's style directory. Then use <span style="font-weight:bold;">Design</span> -> <span style="font-weight:bold;">Template Designer</span> -> <span style="font-weight:bold;">Advanced</span> -> <span style="font-weight:bold;">Add CSS</span> (at the bottom) and paste the contents of your preferred styles .css file into that.<br /></li><br /><li><h4>Enclose code blocks appropriately</h4>To have the highlighter apply to a chunk of code, you place your code in between <i>pre</i> and <i>code</i> tags. E.g.<br /><br /><pre><code><br /> <i>...smalltalk code snippets...</i><br /></code></pre></li></ol><br />At least... that's how I remember how I did it. It's been a month or so now. Hope that helps. And would love to hear of errors/corrections in the above.Travis Griggshttp://www.blogger.com/profile/01599271142862167244noreply@blogger.com4tag:blogger.com,1999:blog-7515306875906042828.post-70255930849351810672011-07-13T15:10:00.000-07:002011-07-13T15:27:31.219-07:00Making Performance Comparison ChartsMy last post was submitted just before embarking on a multi-week multi-state driving family vacation. While I was gone, I received a couple of requests regarding the code actually used to draw the performance comparison charts in that post.<br /><br />I used <a href="http://cairographics.org/">CairoGraphics</a> to do it. Those that know me are likely not surprised by this. The code is just a big workspace blob, and is somewhat procedural.<br /><br />One of the challenges I wrestled with was how to turn numbers the numbers into percent increase or decreases. The easy method of just dividing one value by the other gives values that are not linear when drawn graphically. Or put another way, that is twice as fast draws differently than something that is twice as slow.<br /><br />I used this fun snippet to convert from aTime and bTime values into such a comparison:<br /><br /><pre><code><br /> (((originalTime max: newTime) /<br /> (originalTime min: newTime)) - 1.0) * (originalTime - newTime) sign</code></pre><br /><br />The large workspace blob that works with a variable called <i>percents</i> follows below, I put comment annotations to delineate the various sections. It may serve as a useful Cairo snippet.<br /><br /><pre><code><br /><br />| max min surface bandHeight xInc text extents y |<br /><br />"compute some dimensions and create the surface"<br />max := (percents fold: [:a :b | a max: b]) ceiling max: 0.<br />min := (percents fold: [:a :b | a min: b]) floor min: 0.<br />surfaceHeight := (max - min) * 75 min: 300.<br />bandHeight := surfaceHeight / (max - min).<br />surface := ImageSurface format: CairoFormat argb32<br /> extent: 600 @ surfaceHeight.<br />xInc := surface width / percents size.<br /><br />surface newCairoContextWhile: <br /> [:cr | | matrix |<br /><br />"setup a default font"<br /> cr<br /> selectFontFace: 'Arial'<br /> slant: FontSlant normal<br /> weight: FontWeight bold.<br /><br />"flip the coordinate system so we draw from the bottom up, rather than normal y is down style"<br /> cr fontSize: 11.<br /> cr translate: 0 @ surface height.<br /> cr scale: 1 @ -1.<br /> matrix := cr fontMatrix.<br /> matrix scale: 1 @ -1.<br /> cr fontMatrix: matrix.<br /><br />"do rounded clip/border"<br /> cr rectangle: (Point zero corner: surface extent) fillet: 10.<br /> cr clipPreserve.<br /> cr source: ColorValue black.<br /> cr fill.<br /><br />"shift up to base line"<br /> cr translate: 0 @ min * bandHeight negated.<br /><br />"draw columns"<br /> percents keysAndValuesDo: <br /> [:size :percent |<br /> | rectangle baseHue x |<br /> cr saveWhile: <br /> [cr<br /> rectangle: (Point zero<br /> extent: surface width @ (percent positive ifTrue: [max] ifFalse: [min])<br /> * bandHeight)<br /> regular.<br /> cr clip.<br /> rectangle := Rectangle<br /> left: (size - 1) * xInc + 3<br /> right: size * xInc - 3<br /> top: 5<br /> bottom: -5.<br /> percent > 0<br /> ifTrue: [rectangle top: percent * bandHeight]<br /> ifFalse: [rectangle bottom: percent * bandHeight].<br /> cr rectangle: rectangle regular fillet: 5.<br /> baseHue := (1 / 6 + (percent / 3) min: 1 / 3) max: 0.<br /> cr source: (ColorValue hue: baseHue saturation: 1 brightness: 0.85).<br /> cr fillPreserve.<br /> cr source: (ColorValue hue: baseHue saturation: 0.85 brightness: 1).<br /> cr strokeWidth: 4.<br /> cr stroke].<br /><br />"draw the column label"<br /> text := size printString.<br /> size = 1 ifTrue: [text := 'size=' , text].<br /> extents := cr textExtents: text.<br /> x := (size - 0.5) * xInc - extents width half.<br /> y := percent positive ifTrue: [extents height negated - 2] ifFalse: [2].<br /> y := y max: min * bandHeight + 2.<br /> cr moveTo: x @ y.<br /> cr source: ColorValue white.<br /> cr showText: text].<br /><br />"draw axis lines"<br /> min to: max<br /> by: 1 / 2<br /> do: <br /> [:n |<br /> cr moveTo: 0 @ (n * bandHeight).<br /> cr lineTo: surface width @ (n * bandHeight)].<br /> cr source: ColorValue white.<br /> cr strokeWidth: 0.25.<br /> cr stroke.<br /><br />"draw axis"<br /> cr source: ColorValue white.<br /> min to: max<br /> do: <br /> [:n |<br /> n isZero<br /> ifFalse: <br /> [text := (n + n sign) printString , 'x'.<br /> extents := cr textExtents: text.<br /> y := ((n * bandHeight max: min * bandHeight + 4 + extents height half)<br /> min: max * bandHeight - 4 - extents height half) - extents height half.<br /> cr moveTo: 4 @ y.<br /> cr showText: text.<br /> cr moveTo: (surface width - 4 - extents width) @ y.<br /> cr showText: text]].<br /> cr moveTo: Point zero.<br /> cr lineTo: surface width @ 0.<br /> cr strokeWidth: 1.<br /> cr stroke.<br /><br />"draw label"<br /> text := '#at:put: - Chain vs CompactDictionary'.<br /> extents := cr textExtents: text.<br /> cr moveTo: (surface width half - extents width half)<br /> @ (max * bandHeight - 4 - extents height).<br /> cr showText: text].<br /><br />"write the file"<br />surface writeToPng: 'chart.png'<br /></code></pre>Travis Griggshttp://www.blogger.com/profile/01599271142862167244noreply@blogger.com0