Showing posts with label squeak. Show all posts
Showing posts with label squeak. Show all posts

Sunday, July 13, 2008

Functional Programming Has Warped Me

Look at this method that I recently wrote in a class for describing my database. The responsibility of this method is to commit the current session (which is encapsulated from the user) after x number of database events (adds, updates, deletes, etc). I need his functionality because I was bulk inserting objects that I had read from an XML feed. Here's what I first came up with:

commitEvery: aNumber during: aBlock
| count |
count := 1.
[self session beginUnitOfWork.
aBlock value:
[count := count + 1.
count > aNumber
ifTrue:
[self session commitUnitOfWork.
self session beginUnitOfWork.
count := 1]]].
self session commitUnitOfWork

The method takes a one argument block that will be called with a zero argument block that when called will increase the count and decide whether to commit and start a new transaction if it has reached the number given. At the end, I commit whatever active transaction there is. The thing I like about this is that I put the logic of counting and when to commit in one place. Here's how it would be used:

populate
| db |
db := self new.
[db
commitEvery: 1000
during:
[:afterAdd |
ITunesSAXHandler readUsing:
(ITunesTracksHandler onEachTrackDo:
[:each |
db addTrack: each.
afterAdd value])]]
ensure: [db disconnect]

Notice how "afterAdd" is used. It is passed into the block that will handle all of the transaction adds. It is called after an item has been added. I like this code because it doesn't worry about anything, but adding things to the database. It also makes it obvious how often we will commit. But, I'm unhappy with commitEvery:during:. The reason is because it just seems like I could make it more generic and at the same time easier to test without resorting to mocks. I abstracted out the counting and put it as a method on BlockContext (this is Squeak):

duringDoEvery: aNumber onBegin: beginBlock onEnd: endBlock
| count result |
count := 1.
beginBlock value.
result := self value:
[ count := count + 1.
count > aNumber ifTrue:
[ endBlock value.
beginBlock value.
count := 1 ] ].
endBlock value.
^result

This turns our first method into this:

commitEvery: aNumber during: aBlock
^aBlock
duringDoEvery: 1000
onBegin: [ self session beginUnitOfWork ]
onEnd: [ self session commitUnitOfWork ]

It's a mini-DSL! Let's write a simple test. Note, this is not an SUnit test, it simply writes to the transcript. But, I wanted to show it without all of the assertions:

[:afterAdd |
| count |
count := 0.
10 timesRepeat:
[Transcript show: (count := count + 1) printString; cr.
afterAdd value]]
duringDoEvery: 5
onBegin: [Transcript show: 'begin'; cr]
onEnd: [Transcript show: 'end'; cr]

Gives this output:

begin
1
2
3
4
5
end
begin
6
7
8
9
10
end
begin
end

Everything looks wonderful until we hit the last two lines. It seems pointless and unnecessary to do a begin and end with nothing in between. Our code will need to get a little bit more complex. Here's my next try:

duringDoEvery: aNumber onBegin: beginBlock onEnd: endBlock
| count result hasBegun |
count := 1.
hasBegun := false.
result := self value:
[ :eachBlock |
count := count + 1.
hasBegun ifFalse:
[ beginBlock value.
hasBegun := true ].
eachBlock value.
count > aNumber ifTrue:
[ endBlock value.
hasBegun := false.
count := 1 ] ].
hasBegun ifTrue: [ endBlock value ].
^ result

It's more complicated. And now, I'm passing another block around to get its value. The reason was because to properly remove empty begin/end combinations, I needed to know when the before was and when the after was. This means passing a block to the afterAdd from before. So, now the test code looks like this:

[:onEachAdd |
| count |
count := 0.
10 timesRepeat:
[onEachAdd value:
[Transcript show: (count := count + 1) printString; cr]]]
duringDoEvery: 5
onBegin: [Transcript show: 'begin'; cr]
onEnd: [Transcript show: 'end'; cr]

I renamed afterAdd to onEachAdd which is a better name and indicates its role better. Now, the reason I wrote this post is that I sat back and went, "Wow. I have something easily testable, but might hurt some brains." My next thought was, "Why would this hurt someone's brain?" The answer is all of the indirection of the blocks. I will be quite honest at this point, I would normally start turning all of these blocks into objects and have better names for what they were doing. Blocks worked perfectly in this context and if you think about it. This code is not much different than what Collection>>streamContents: does.

This is one of the things that learning functional programming has done to me. The above code looks and feels natural. Passing a block into a method that will accept another block doesn't bother me at all. I love the DSL-like nature of the implementation too, but I can see where it might confuse (Hell, inject:into: still does). This is where objects come in because now we can create our small objects with more meaningful names than just value or value:, this will at least make the indirection less painful. Thought this was fun exercise to show off something else that you can do with higher order functions. Squeak on.

Monday, May 12, 2008

My Passion

I guess I don't conceal my passion and love for Smalltalk very well:
...seeing Blaine’s eyes light up every time he got to either hear or utter a sentence that included the word “Smalltalk”.

To me, Smalltalk defines elegance. If you want to see elegance in computing, go download Squeak right now. The very least that will happen is it will warp your mind and make you a better programmer. It might even make you see the world a little differently.

Seaside Presentation

Just a reminder I have my Seaside presentation (scaled down a bit from the BarCampKC one ala no database mappings) and my Smalltalk Coding competition (100% Seaside) here. So, if you missed the presentation, feel free to download the Seaside presentation image and the coding competition. Lots of good examples to pour through in each. I'm going to try to put instructions with the new presentation and get it published. But, in the meantime, you can check out these older (not that old) materials. I will also remind folks to read Introduction to Seaside. It's a great and fast introduction into the coolest web framework on the planet!

Saturday, January 05, 2008

Reflection on Everything

Smalltalk allows you to reflect on everything. You can not reflect on objects to find their instance variables and methods. But, you can also reflect on code running and on the stack. It's powerful stuff. Why would you need this extra power? You might be a lazy developer. You might constantly remind yourself, "I work in a dynamic language and it should work for me!" Let's go digging shall we?

One thing you have to be careful in dynamic languages are message not understood or method missing errors. One easy mistake to make is to do the following:
[someObject doSomething] 
on: MessageNotUnderstood
do: [:ex |
"log something"
^self]

What's wrong with the above code? Well, are you trying to catch if doSomething is not understood by someObject? If so, the call can still succeed and there could be a nasty bug further down in the code. The handler will be giving misinformation on the true problem. Frustrating to say the least. It's better to do something like this:
[someObject doSomething]
on: MessageNotUnderstood
do: [:ex |
(ex message selector == #doSomething and: [ex receiver == someObject])
ifTrue: ["log"
^self]
ifFalse: [ex pass]]

Yuck. But, it does check the receiver and selector to make sure we captured the right exception. Lots of typing for something simple. Granted you shouldn't be doing a lot of guarding against MessageNotUnderstoods (polymorphism anyone?). But, sometimes it is necessary. Besides, we wouldn't have this fun little blog post would we. Basically, the above method checks for the right selector and receiver that we expected to have a MessageNotUnderstood and if it did we do our logging code. If not, it's something we didn't account for and is a bug, thus we "pass" the exception to the next handler. But, how can we prevent ourselves from all of this typing?

Squeak has this method implemented:
BlockConext>>onDNU: selector do: handleBlock
"Catch MessageNotUnderstood exceptions but only those of the given selector (DNU stands for doesNotUnderstand:)"

^ self on: MessageNotUnderstood do: [:exception |
exception message selector = selector
ifTrue: [handleBlock valueWithPossibleArgs: {exception}]
ifFalse: [exception pass]
]

It does not check the receiver, but that's OK. It turns our code into this:
[someObject doSomething] onDNU: #doSomething do: ["log something" ^self]

Nice. It's much more succinct, but I don't like the duplication of "doSomething". What are we to do? I came up with this method:
BlockConext>>onImmediateNotUnderstoodDo: anExceptionBlock 
^ self
on: MessageNotUnderstood
do: [:problem |
| myContext problemContext |
myContext := thisContext home.
problemContext := problem signalerContext sender sender sender.
(problemContext == myContext
and: [self method messages includes: problem message selector])
ifTrue: [anExceptionBlock
valueWithPossibleArgs: (Array with: problem)]
ifFalse: [problem pass]]

Here's what it makes our code look like now:
[anObject doSomething] onImmediateNotUnderstoodDo: ["log something" ^self]

It looks like what we started with, but this version is safe. It will pass the MessageNotUnderstood exception if the send did not happen directly from the block we defined.

What is the method above doing? The method I wrote reflects on the stack and the compiled code. I use the stack to find the receiver that should be in the block. The compiled code is needed to find all of the selectors that are called directly from the block. Pretty cool, huh?

Smalltalk is one of the few languages where you can reflect on everything on the stack. Stack frames are objects too. It makes doing difficult things possible.

Sunday, November 11, 2007

A Happy Diversion

I was smitten with my quick snippet of code for my Monticello window in a flap. As with all little things in Squeak, it's hard to just stop. You're always thinking of little things or tools that would be nice. Everything in Squeak is easily explorable and changeable. It's too much fun to search and see the results of your labor instantly. Addictions are not pretty.

Another idea came to me. The tab for my Monticello flap was boringly called "Monticello". What a waste of real estate. My second problem was that I'm always getting confused at which project I'm in. I normally use different background colors to make it easy. But, I still get confused. I was blessed with a small brain. I curse it everyday. What is a poor little Squeaker to do? I thought I could put the name of the current project on the Monticello tab instead of the one it has. I wondered how hard would that be? And off I went...

How can I do that? I could change the tab name when the project changes. Eureka! That's what I will do. I remembered I wrote a little utility to change background images and I had to know when the current project changed. I started my hunt there and event handlers are available via the morphic world to know when a project is entered or exited. We only care about when a world is left. Why? The event handlers has to be registered on the world itself and we don't want to have to keep track of every project and world. It's easier to put a handler on the current project and know when it is left.

Let's start with this method for setting the world we're currently in:
world: aMorph 
world == aMorph ifTrue: [^ self].
world ifNotNil: [world removeActionsWithReceiver: self].
world := aMorph.
world ifNotNil:
[self changeTextOnTab.
world
when: #aboutToLeaveWorld
send: #leavingWorld
to: self]

Basically, this method removes all events from the old world since we will not care about it any longer. Then, we change the text on our tab and add our event handler. Next, how do we change the text on the tab? In the menu for a tab, is an item to change the name of the tab. Using the handy morphic handles, I inspect the menu item itself and find the method it will call when it is invoked. Here's the code I came up with:
changeTextOnTab
self monticelloFlapTab ifNotNilDo: [:tab | tab changeTabText: Project current name]

The simple method changeTabText: does all of the magic. We just get the name of the current project. I haven't showed you how to get the tab itself. I look through the methods on the class side of Flaps to reveal its magic:
monticelloFlapTab

^Flaps globalFlapTab: 'Monticello'

The good news is that it will always find the flap this way no matter the name. The only thing left is to what to do during the handling of leaving the current world. The dilemma now is how do we know the next world? Some more digging and we can be ask ed to be called later. Perfect. Here's the code for the handler:
leavingWorld
WorldState addDeferredUIMessage: [self world: Project current world]

And that's it! The same method can be called to seed our initial world as well. Now, a quick glimpse to the bottom of the screen gives my project name. Easy. Now, I can get back to updating my Seaside presentation.

Why I Love Squeak


addMonticelloFlap
| newTab monticelloBrowser |
newTab := Flaps newFlapTitled: 'Monticello' onEdge: #bottom.
self currentWorld addMorphFront: newTab.
newTab adaptToWorld: self currentWorld.
Flaps addGlobalFlap: newTab.
newTab showFlap.
monticelloBrowser := MCWorkingCopyBrowser new.
(monticelloBrowser window)
openInWorld: newTab referent extent: monticelloBrowser defaultExtent;
topLeft: newTab referent topLeft + (10 @ 10).
newTab referent height: monticelloBrowser window height + 20.
newTab hideFlap

The above code snippet adds a new global flap to the Squeak environment. Inside the flap is the Monticello browser. I've been doing this in my Squeak images for a long time, but doing the above by hand for each image. I went into the system and figured out what each of the menu items did to create the above. This is simple in Squeak because everything in Morphic is inspectable. It allowed me to create the script you see.

The reason for having a Monticello browser in a global flap is so that you can always have it open in every project and easily accessible. I like to have a Squeak project for everything I do and like having one Monticello browser.

The above was not much effort and took little time to figure out. Love is.

Saturday, November 10, 2007

Seaside Presentation

I updated my Seaside Presentation available on SqueakMap to Seaside version 2.8. Make sure you update your SqueakMap. The new version is 1.70. I plan on reworking it completely soon. As I upgraded the code, I was unhappy with what I saw. It needs to be refactored heavily. I want to add more code that shows off the cool new add-ons for Seaside as well. Enjoy!