After much pain mucking about in namespaces, I finally managed to load GemStone code saved in Store using atomic loading.
Atomic loading first loads code into a temporary namespace called the shadow namespace. Once everything is compiled there and it looks good, the system is re-compiled into the real namespace and the shadow namespace disappears.
With GemStone code, I thought I had everything setup to compile the code into the shadow namespace but when I did the actual compile, the code ended up in the real namespace right away. This is bad because in the shadow world, the system would add the compilerClass and classCompilerClass methods to tell the shadow class what the compiler is. You don't want to move these methods into the real namespace because it would cause crashes - which it did.
To allow me to quickly see what was going on, I wrote a tool that simply showed a tree view with a portion of the namespace hierarchy that I was interested in along with the shadow hierarchy. It also showed the identityHashes of the namespaces so I could quickly tell what namespace I was looking at in an inspector. If the namespace contained shared variables or classes, it would show those in the tree and if the classes had methods, it would show those as well. Here's what it looked like.
Now for the problem. When you login to GemStone as a user (for example, David), GemKit creates a namespace for you called Root.GemStone.AllUsers.David. It also aliases this as Root.GemStone.CurrentUser. You can see that in the above image.
When you create the shadow environment, Store creates a mimic of the namespace structure. Since CurrentUser wasn't marked as a namespace link, Store didn't create it in Shadow. Ok, fair enough. I augmented the system to create CurrentUser in the shadow namespace. Sadly, that wasn't enough to fix the problem.
Now when I try to compile, the system locates Root.GemStone in the shadow namespace (so far so good) and asks it to resolve the symbol #UserGlobals. This is where it will put the class it's compiling. UserGlobals isn't in GemStone directly but rather in CurrentUser which is in the imports of GemStone. The problem: The imported namespace CurrentUser is coming from the real environment instead of the shadow environment. It finds UserGlobals in the real world and compiles the class directly there.
Aaarg - ok, I go and smash the imports in the shadow GemStone namespace to point to the CurrentUser in the shadow. Now it complains about a method conflict during the install. When you try to move the shadow classes to the real environment, you must first move the class then move the methods. When you move a GemStone class to the real world, you automatically get a class method called gsst_definition. When we try to move the gsst_definition method from the shadow world to the real world, the system detects that there's a conflict and stops.
As it turns out, you have to change both imported namespaces in the shadow GemStone to point to the shadow and you also need to change the imported namespaces in the shadow GemStone.Globals to point back to the shadow GemStone instead of the real GemStone.
<sigh> After all that, atomic loading works with GemStone code. Yay! I think I'll pop a bottle of bubbly and share it with my wife tonight.
For my next challenge: Cincom switched Store to use Glorp instead of the older direct mechanism. GemKit has tools which are coded for the old mechanism and need to be modified to work with Glorp.
Friday, December 16, 2011
Sunday, December 11, 2011
Space Wars game update
I did some more work on the game I described in my last post. New features:
- Multiple enemy ships
- Ships disappear when hit
- Sound effects on fire
- Planets in the background
- Display of number of ships left
- When all ships are destroyed, a new round starts with 10 more ships
Friday, December 9, 2011
Space Wars game in Smalltalk
My 11 year old son recently asked me to teach him how to program so he can write his own games. Unfortunately, the kind of game he has in mind is a first-person shooter which isn't exactly the easiest way to teach an 11 year old how to program.
We talked about different kinds of games we could write. He had no interest at all in text games like "20 questions" or "guess a number", so I told him about a game I wrote on my old 1802 computer back in the early 1980's. It was based on the Battlestar Galactica TV series that I was watching at the time. You would fly around a star field shooting at Cylon ships. Tyler thought that sounded fun so we started writing it in Smalltalk.
The video below shows our progress so far. I actually wrote most of it and I'm slowly explaining to him how it works. It's piqued his curiosity, however. So far, you can fly around and shoot at ships. The ships don't explode yet, but it still shows some good progress.
We talked about different kinds of games we could write. He had no interest at all in text games like "20 questions" or "guess a number", so I told him about a game I wrote on my old 1802 computer back in the early 1980's. It was based on the Battlestar Galactica TV series that I was watching at the time. You would fly around a star field shooting at Cylon ships. Tyler thought that sounded fun so we started writing it in Smalltalk.
The video below shows our progress so far. I actually wrote most of it and I'm slowly explaining to him how it works. It's piqued his curiosity, however. So far, you can fly around and shoot at ships. The ships don't explode yet, but it still shows some good progress.
Sunday, November 27, 2011
GemKit for VisualWorks 7.8 Preview
I have a GemKit for VisualWorks 7.8 preview available at the following URL:
http://simberon.com/downloads/GemKit-VW78-Preview.zip
Follow the instructions in the readme.txt to load it. Please report any issues to david@simberon.com.
http://simberon.com/downloads/GemKit-VW78-Preview.zip
Follow the instructions in the readme.txt to load it. Please report any issues to david@simberon.com.
Sunday, October 30, 2011
Subtleties of Atomic Loading
Most of the time, atomic loading works like a charm. Every once in a while, though, it gives you very confusing results.
I ran into one of these with the GemKit upgrade to VisualWorks 7.8. I'd already determined that the package defined a compiler along with a class that used that compiler in one package. That's a no-no. I was getting into infinite loops that way. I split the methods compiled by the new compiler into another package and that seemed to fix the problem.
The key phrase there is "seemed to". I started finding that I was getting Message Not Understood errors by things that were supposed to be references to classes but were references to namespaces instead. For example, code that read Core.Array was being compiled as if it was just Core. I looked at how it was being compiled and it turned out that it was using the new compiler defined in the previous package. The problem was that it wasn't supposed to use that compiler. It was supposed to use the standard Smalltalk compiler.
As it turns out, there's code in the compilerClass method of the class that enables the special compiler on subclasses of this class, not on this class itself. The code looked like this:
So, if the method being compiled is in GbcHierarchyRoot itself use the default compiler, otherwise, use the GbcCompiler for the subclass methods.
The problem here is that when you're doing atomic loading, at the time that this method is run, the class is in a different namespace than GBC.GbcHierarchyRoot. As such, the test was failing and using the GbcCompiler even for the methods in GbcHierarchyRoot itself.
The solution to this problem was to change the method as follows:
Now the test works properly and it uses the default compiler class for GbcHierarchyRoot methods. There were several methods that used this pattern, so I changed them all.
I'm making some progress but it's slow and complicated working out the interactions between atomic loading and the GemKit code. My goal is to keep the atomic analysis loader turned on all the time and still be able to load GemStone code from Store. So far, I seem to be able to at least load the GemKit code. The next step is to load actual GemStone code from Store.
I ran into one of these with the GemKit upgrade to VisualWorks 7.8. I'd already determined that the package defined a compiler along with a class that used that compiler in one package. That's a no-no. I was getting into infinite loops that way. I split the methods compiled by the new compiler into another package and that seemed to fix the problem.
The key phrase there is "seemed to". I started finding that I was getting Message Not Understood errors by things that were supposed to be references to classes but were references to namespaces instead. For example, code that read Core.Array was being compiled as if it was just Core. I looked at how it was being compiled and it turned out that it was using the new compiler defined in the previous package. The problem was that it wasn't supposed to use that compiler. It was supposed to use the standard Smalltalk compiler.
As it turns out, there's code in the compilerClass method of the class that enables the special compiler on subclasses of this class, not on this class itself. The code looked like this:
GBC.GbcHierarchyRoot class method: compilerClass ^self == GBC.GbcHierarchyRoot ifTrue: [super compilerClass] ifFalse: [GBC.GbcCompiler]
So, if the method being compiled is in GbcHierarchyRoot itself use the default compiler, otherwise, use the GbcCompiler for the subclass methods.
The problem here is that when you're doing atomic loading, at the time that this method is run, the class is in a different namespace than GBC.GbcHierarchyRoot. As such, the test was failing and using the GbcCompiler even for the methods in GbcHierarchyRoot itself.
The solution to this problem was to change the method as follows:
compilerClass ^name = #GbcHierarchyRoot ifTrue: [super compilerClass] ifFalse: [GBC.GbcCompiler]
Now the test works properly and it uses the default compiler class for GbcHierarchyRoot methods. There were several methods that used this pattern, so I changed them all.
I'm making some progress but it's slow and complicated working out the interactions between atomic loading and the GemKit code. My goal is to keep the atomic analysis loader turned on all the time and still be able to load GemStone code from Store. So far, I seem to be able to at least load the GemKit code. The next step is to load actual GemStone code from Store.
Friday, October 7, 2011
GemKit and Atomic Loading revisited
Ok, I spoke too soon. It turns out that the loading of GbcManagement didn't actually work properly. There were 26 methods in GbcHierarchyRoot which didn't install. After a bit of tracing, I discovered why. The GbcHierarchyRoot class defines a new compiler for instance and class methods and that compiler was loaded in the same package as the GbcHierarchyRoot class. This means that GbcHierarchyRoot could load before the compiler, load the methods to change to the compiler that's not loaded, then fail to compile anything else.
The solution to this is to create another package which has all the GbcHierarchyRoot methods and is loaded after GbcManagement. This allows everything to load cleanly.
Now, on to more problems...
The solution to this is to create another package which has all the GbcHierarchyRoot methods and is loaded after GbcManagement. This allows everything to load cleanly.
Now, on to more problems...
Thursday, October 6, 2011
GemKit and Atomic Loading
In VisualWorks 7.8, I have to turn off atomic loading in order to load the package GbcManagement. If I didn't, the load crashed with a memory overflow.
After tracing down the problem, I found that the load itself actually worked fine. It was the step right afterward that did a purgeUnusedBindings that was failing - specifically in a method called relinkSystem. The relinkSystem method looped through allInstances of DeferredBinding. Maybe it was hitting an object that was garbage but hadn't yet been garbage collected. So, I added a call to do a garbage collect just before the allInstances. That did the trick. GbcManagement loads perfectly every time now.
It goes to show that you have to be careful with allInstances.
After tracing down the problem, I found that the load itself actually worked fine. It was the step right afterward that did a purgeUnusedBindings that was failing - specifically in a method called relinkSystem. The relinkSystem method looped through allInstances of DeferredBinding. Maybe it was hitting an object that was garbage but hadn't yet been garbage collected. So, I added a call to do a garbage collect just before the allInstances. That did the trick. GbcManagement loads perfectly every time now.
It goes to show that you have to be careful with allInstances.
Sunday, October 2, 2011
GemKit in VW7.8
So, I have GemKit running in 7.5. I think I need to get newer versions of GemBuilder/S but it basically runs. I thought I'd try loading it into 7.8 to see what happens.
It took several tries to get GemKit loaded into 7.8. I learned that:
I can see already that my challenges in this project will be:
It took several tries to get GemKit loaded into 7.8. I learned that:
- You can't load Searchlight before loading GemKit. There's an interaction that causes problems. I'll investigate that later.
- You must turn off Atomic Loading to load GemKit. When Atomic Loading was turned on, I got into an ugly infinite loop that I couldn't interrupt and that allocated memory until it ran out and crashed. It wasn't a pretty picture
I can see already that my challenges in this project will be:
- Getting GemKit working with Atomic Loading
- Resolving the conflict between GemKit and Searchlight
- Adapting GemKit to the new Glorp interface to Store
- Adapting GemKit to the new Store Repository browsing tools. The classes that GemKit used to extend to do this are no longer there.
- Adapting to the newest changes to the merge tool
- Getting Refactorings to work on GemStone code (if this is even possible)
- Performance
Loading GemKit
So, I've been able to get a 32 bit GemStone database running and connect to it from GemBuilder. The next step is to load GemKit in VW7.5. This is a fairly straightforward process. The packages are all in the Cincom Public Store Repository. You need to load them in the following order.
With all of these packages loaded in, I need to populate my GS_Globals package with the classes in GemStone. I first connect into GemStone with my user id and open a VisualWorks browser. From the browser, I select GS_Globals and from a context menu select "GemKit Compare With GemStone" This gave me a window that allowed me to select all the methods and click on "Update Image". It seems a bit odd that it asks me to define shared variables called Nil, True and False. I say no to each one and the load finishes without any problems. That question about shared variables is mentioned in the documentation method GbcSourceManager class >> documentation_installation. It's also described in the method GbcSharedVariable >> methodsNeverToDefineInClient where the array #(#nil #true #false) is commented out and the returned answer is just #(). It might be best if we can keep the original code here. I'll investigate.
That's pretty much it. I've been able to push code back and forth to GemStone and it seems to be working reasonably well.
Next up: My first attempts with GemKit in VW7.8
- GbcManagement 4.0 0.48.0
- GbcReleaseTools 4.0 062.0
- GS_AllUsers_SystemUser empty 04.0
- GS_Globals empty 11.0
- GS_GemKit 4.0 000.0
With all of these packages loaded in, I need to populate my GS_Globals package with the classes in GemStone. I first connect into GemStone with my user id and open a VisualWorks browser. From the browser, I select GS_Globals and from a context menu select "GemKit Compare With GemStone" This gave me a window that allowed me to select all the methods and click on "Update Image". It seems a bit odd that it asks me to define shared variables called Nil, True and False. I say no to each one and the load finishes without any problems. That question about shared variables is mentioned in the documentation method GbcSourceManager class >> documentation_installation. It's also described in the method GbcSharedVariable >> methodsNeverToDefineInClient where the array #(#nil #true #false) is commented out and the returned answer is just #(). It might be best if we can keep the original code here. I'll investigate.
That's pretty much it. I've been able to push code back and forth to GemStone and it seems to be working reasonably well.
Next up: My first attempts with GemKit in VW7.8
Getting started with GemKit - part 1
I've started working on upgrading GemKit to VisualWorks 7.8. The first order of business is to get GemKit running in the latest environment known to work. That would be VisualWorks 7.5. Although that version is reported to be working in VisualWorks 7.6, it hasn't been tested much in that environment so I figured that 7.5 is a better starting point.
Before I can get GemKit running, though, I need GemStone. I have two choices - I can run the 32 bit version on my Windows laptop which is the most convenient or I can run the 64 bit version on my 64-bit Ubuntu Linux system. Unfortunately, the processor on my laptop doesn't have the ability to run VMWare so I can't run a virtual Linux OS on the laptop.
I decide that I actually need both 32 and 64 bit GemStone versions and I'd like to make sure GemKit runs on both. So, while I upgrade Linux to the latest version of Ubuntu, I grab GemStone 6.5.8 and install it.
Installing GemStone on Windows takes some effort but if you follow the instructions carefully you can do it. One step of the instructions, however, is that you need to put your gemstone.key file into the sys directory. Lovely. Where do I get gemstone.key? Good news! There's a key in the Cincom installation directory for VW7.8 under contributed/GemStone/Server/Windows. Yay! Bad news: the key expires April 15th, 2010 - about a year before VW7.8 was released. Fortunately, I can fool GemStone by setting my system clock back. It's not pretty but it works.
Ok, I have GemStone 6.5.8 running. Now I need GemKit. Fortunately, there are parcels available in the contributed directory. Ok, that's another problem. Who would have thought that the contributed directory of VisualWorks would have 32 bit version of the server but 64 but versions of GemBuilder Smalltalk? I'm sure that it will come in handy when I connect to the Linux system, but for now, I need to find a 32 bit version of GemBuilder.
The GemStone community site allows you to download lots of versions of GemBuilder/Smalltalk. All of them are 64 bit versions, though. I finally managed to track down 32 bit versions in the Cincom Public Store Repository. These seem like older versions but with a few tweaks and fixes I can get them to connect to GemStone 6.5.8. Yay!
Installing GemStone 64 on Ubuntu Linux is a bit more challenging. One part of the process involves verifying the checksums of all the files. This process fails left right and center with error messages. I suspect that this is because the installation instructions are for RedHat Linux and not Ubuntu. Fortunately, I can skip this step and continue to install the files. When I get to the point where I need a licence key, I decide to put it aside for now. I'm working through my channels to get valid license keys for everything.
In part 2, I'll talk about getting GemKit running.
Before I can get GemKit running, though, I need GemStone. I have two choices - I can run the 32 bit version on my Windows laptop which is the most convenient or I can run the 64 bit version on my 64-bit Ubuntu Linux system. Unfortunately, the processor on my laptop doesn't have the ability to run VMWare so I can't run a virtual Linux OS on the laptop.
I decide that I actually need both 32 and 64 bit GemStone versions and I'd like to make sure GemKit runs on both. So, while I upgrade Linux to the latest version of Ubuntu, I grab GemStone 6.5.8 and install it.
Installing GemStone on Windows takes some effort but if you follow the instructions carefully you can do it. One step of the instructions, however, is that you need to put your gemstone.key file into the sys directory. Lovely. Where do I get gemstone.key? Good news! There's a key in the Cincom installation directory for VW7.8 under contributed/GemStone/Server/Windows. Yay! Bad news: the key expires April 15th, 2010 - about a year before VW7.8 was released. Fortunately, I can fool GemStone by setting my system clock back. It's not pretty but it works.
Ok, I have GemStone 6.5.8 running. Now I need GemKit. Fortunately, there are parcels available in the contributed directory. Ok, that's another problem. Who would have thought that the contributed directory of VisualWorks would have 32 bit version of the server but 64 but versions of GemBuilder Smalltalk? I'm sure that it will come in handy when I connect to the Linux system, but for now, I need to find a 32 bit version of GemBuilder.
The GemStone community site allows you to download lots of versions of GemBuilder/Smalltalk. All of them are 64 bit versions, though. I finally managed to track down 32 bit versions in the Cincom Public Store Repository. These seem like older versions but with a few tweaks and fixes I can get them to connect to GemStone 6.5.8. Yay!
Installing GemStone 64 on Ubuntu Linux is a bit more challenging. One part of the process involves verifying the checksums of all the files. This process fails left right and center with error messages. I suspect that this is because the installation instructions are for RedHat Linux and not Ubuntu. Fortunately, I can skip this step and continue to install the files. When I get to the point where I need a licence key, I decide to put it aside for now. I'm working through my channels to get valid license keys for everything.
In part 2, I'll talk about getting GemKit running.
Friday, August 19, 2011
New Fluid Positioning Progress
In a previous post I showed what I was working on for a feature called Fluid Positioning for VisualWorks. I've created a new video to show the progress of the feature. You can watch a higher quality version of it on the Simberon Web Site. There's also an MPEG version available if you prefer.
Wednesday, August 10, 2011
GemKit
I announced earlier that I'll be porting GemKit to VisualWorks 7.8. This work is approved and will go forward but we've had other priorities that needed to be addressed first. I'll keep you informed of the progress and when the work will begin.
My apologies for the delays.
My apologies for the delays.
Quick Look at Smalltalk Course
Simberon will be delivering the course "A Quick Look at Smalltalk" on September 26th and 27th, 2011. This course teaches the basics of the Smalltalk language, libraries and environments without getting into version control or user interface development.
This will be a WebCast course so you can attend the course from anywhere you have an Internet connection.
For details and to register, visit http://simberon.com/quickstwc.htm.
This will be a WebCast course so you can attend the course from anywhere you have an Internet connection.
For details and to register, visit http://simberon.com/quickstwc.htm.
Monday, June 13, 2011
GemKit for VisualWorks 7.8
Well, I've received permission to make the following announcement. I'll be working with Cincom to upgrade GemKit to VisualWorks 7.8. Here's the current status:
1) We have confirmation of the work request. Work should begin sometime in July.
2) We have permission to fully disclose and share all work and all progress we make with the Smalltalk community.
3) I will document all steps, progress, issues, problems and successes here on my blog:
http://simberon.blogspot.com
4) I will publish major versions to the Cincom public Store repository
5) I will setup another public Store repository from Simberon for holding intermediate versions which aren't yet ready for public use but may be of interest to others
6) I will coordinate with anyone involved to merge their changes into my versions and publish them
7) I've setup a mailing list at gemkit@simberon.com. Send an e-mail to me at david@simberon.com to add or remove your name from the mailing list.
If you have any questions or comments, feel free to e-mail them to me.
1) We have confirmation of the work request. Work should begin sometime in July.
2) We have permission to fully disclose and share all work and all progress we make with the Smalltalk community.
3) I will document all steps, progress, issues, problems and successes here on my blog:
http://simberon.blogspot.com
4) I will publish major versions to the Cincom public Store repository
5) I will setup another public Store repository from Simberon for holding intermediate versions which aren't yet ready for public use but may be of interest to others
6) I will coordinate with anyone involved to merge their changes into my versions and publish them
7) I've setup a mailing list at gemkit@simberon.com. Send an e-mail to me at david@simberon.com to add or remove your name from the mailing list.
If you have any questions or comments, feel free to e-mail them to me.
Fluid Positioning
I'm working on a feature for VisualWorks called Fluid Positioning. The idea is to simplify the layout of user interfaces in the UI Painter by making the layout automatically fill in the Position tab information in a natural way.
The following video shows what I have working so far. The video below is a bit blurry. A better version can be found on the Simberon Web Site. The demo shows a lot of flickering of the user interface. This is because I've disabled some optimizations at this stage to allow me to see exactly what's going on at all times. When I re-enable the optimizations, most of the flicker should disappear.
The following video shows what I have working so far. The video below is a bit blurry. A better version can be found on the Simberon Web Site. The demo shows a lot of flickering of the user interface. This is because I've disabled some optimizations at this stage to allow me to see exactly what's going on at all times. When I re-enable the optimizations, most of the flicker should disappear.
Friday, March 18, 2011
Yesterday, I parked my van in the parking lot and went to work as usual. When I returned at the end of the day, I found this:
I was more confused than anything - how could anyone park that close? Neither of us would be able to open our driver's side doors at all. The driver of the other vehicle had to get out on the passenger side. But it makes no sense - how could you get the two vehicles this close without scraping up each of them? There were no scrape marks at all.
A few minutes later, the owner of the other vehicle arrived. He was just as baffled. He claimed that he parked properly, left by the driver's side door, and went to work.
As it turns out, that morning there was a small layer of ice in the parking lot. Through the day, the temperature rose, the ice melted and the car slid sideways slightly downhill into my van. We called a tow truck to pull my van sideways to the right. After that, I could back straight up. There was no damage at all.
If there was damage, however, it would have been a confusing call to the insurance company. "You see, both cars were turned off and in park at the time of the collision. Neither driver was present. The colliding car moved sideways toward the driver's side door." It would have been a fun conversation.
Monday, February 21, 2011
OCSTUG meeting Feb 24, 2011
Ottawa Carleton Smalltalk Users Group:
The Great Apple Experiment Software
David Buck A few weeks ago, our local Scout troop did an experiment to earn their science badges. David wrote some software in Smalltalk to organize the experiment and calculate the results? How do you capture over 2000 numbers collected from 20 scouts and calculate results within an hour? Come and find out.
Location: The Code Factory
246 Queen Street, Ottawa
Thursday, February 24th, 2011 at 6:00pm
The Great Apple Experiment Software
David Buck A few weeks ago, our local Scout troop did an experiment to earn their science badges. David wrote some software in Smalltalk to organize the experiment and calculate the results? How do you capture over 2000 numbers collected from 20 scouts and calculate results within an hour? Come and find out.
Location: The Code Factory
246 Queen Street, Ottawa
Thursday, February 24th, 2011 at 6:00pm
Thursday, February 3, 2011
Great Apple Experiment results
Well, the Great Apple Experiment by my son's scout troop is finished. We had all the scouts rank their own apples then rank all of the other apples on a scale from 1 (best looking) to 9 (worst looking). Excluding the scout's ranking of their own apples, I averaged the ratings for each type of apple and got the following results:
The Lemon line clearly shows a benefit with 62 apple slices being ranked as 1. The open air apples were the worst with 109 being ranked as 9. The remaining apples seemed to follow a very similar distribution.
The results don't demonstrate any positive effect of saying nice things to apples or any negative effect of saying bad things to apples. We're left with the null hypothesis that saying things to apples is ineffective.
Thanks to all the scouts who participated and congratulations on earning your science badges.
- Love
- Mean: 4.23
- Standard Deviation: 2.43
- Mean rating of scout's own apples: 3.31
- Hate
- Mean: 4.54
- Standard Deviation: 2.34
- Mean rating of scout's own apples: 3.5
- Neutral
- Mean: 4.82
- Standard Deviation: 2.41
- Mean rating of scout's own apples: 4.9
- Control
- Mean: 4.18
- Standard Deviation: 2.41
- Mean rating of scout's own apples: 3.8
- Lemon
- Mean: 3.48
- Standard Deviation: 2.57
- Mean rating of scout's own apples: 3.38
- Open Air
- Mean: 7.50
- Standard Deviation: 2.11
- Mean rating of scout's own apples: 7.75
The Lemon line clearly shows a benefit with 62 apple slices being ranked as 1. The open air apples were the worst with 109 being ranked as 9. The remaining apples seemed to follow a very similar distribution.
The results don't demonstrate any positive effect of saying nice things to apples or any negative effect of saying bad things to apples. We're left with the null hypothesis that saying things to apples is ineffective.
Thanks to all the scouts who participated and congratulations on earning your science badges.
Sunday, January 30, 2011
The Great Apple Software
I bet you guessed that there was a software aspect to my last post on the Great Apple Experiment. You were right.
First, there's the fairly easy job of randomly assigning numbered jars to Scouts and marking them for their purpose: Love, Hate, etc. This was a fairly straightforward job of maintaining a collection of jars and a collection of spots (22 Scouts x 6 jars per Scout = 132 spots). Then I just use a pseudo-random number generator to pick a random jar and assign it to a random spot until all the jars are gone.
The harder part is collecting the results from the experiment. My goal is to have all 22 Scouts rank all 132 apple slices and to crunch the numbers before the end of the meeting. Entering the 2904 numbers by hand will be a serious challenge within the one-hour limit I have. So, I decided to speed up the process. I can bring a scanner to the meeting and scan the score cards to produce black and white bitmap images. The only problem now is capturing the results from the bitmaps.
OCR technology would be rather hard to use - especially when the Scouts will be writing the results by hand with pencils. I decided to go with a "fill in the dots" approach. Now the problem is that the scanner isn't perfect and the page can be rotated by several degrees. I can't just look at an x/y point on the page and expect the circle to be there.
Here's a scanned score card:
And here's a magnified version of the top left part of the card with red marks added by my software:
The circles are arranged in 4 columns of 33 rows of circles. At the top of the page, I include a circle at the top left and top right of the page for registration marks.
I was able to locate the registration marks and find their centers. Based on this information, I knew the orientation of the page compared to a carefully-scanned sample. I was able compute the centers of the circles on the sample sheet and stored that information in the program. Then, with the scanned image, I take each point (the centers of the circles shown as red dots in the image above), transform them according to the registration marks, draw a 21x21 square around them (shown as red squares), and capture the bits (1 or 0) in each square. Any square that had more than 200 black bits (0's) was counted as ON.
This actually worked (wow) and I was able to get reliable results. The result is an array of 132 numbers from 1 to 9 or 0 if no circle was filled in. This is exactly the information I need to run the stats.
You'll notice that I print a series of bars at the top of the page. My thought was that I'd scan the Scout's number by scanning it and collecting which bars were long and which were short. I'd always start and end with a long bar then within the code, short bars are 0 and long are 1 making a binary number representing the Scout's number. I decided in the end not to use it because it doesn't take long for me to manually enter the Scout number and it allows me to print generic score cards instead of ones specific to each Scout. I expect that some of the Scouts will mess up the score cards and will need to re-do them. It's a whole lot easier if I can just give them a generic sheet.
Part 2 of the experiment is on Tuesday. I'll let you know how it goes.
First, there's the fairly easy job of randomly assigning numbered jars to Scouts and marking them for their purpose: Love, Hate, etc. This was a fairly straightforward job of maintaining a collection of jars and a collection of spots (22 Scouts x 6 jars per Scout = 132 spots). Then I just use a pseudo-random number generator to pick a random jar and assign it to a random spot until all the jars are gone.
The harder part is collecting the results from the experiment. My goal is to have all 22 Scouts rank all 132 apple slices and to crunch the numbers before the end of the meeting. Entering the 2904 numbers by hand will be a serious challenge within the one-hour limit I have. So, I decided to speed up the process. I can bring a scanner to the meeting and scan the score cards to produce black and white bitmap images. The only problem now is capturing the results from the bitmaps.
OCR technology would be rather hard to use - especially when the Scouts will be writing the results by hand with pencils. I decided to go with a "fill in the dots" approach. Now the problem is that the scanner isn't perfect and the page can be rotated by several degrees. I can't just look at an x/y point on the page and expect the circle to be there.
Here's a scanned score card:
And here's a magnified version of the top left part of the card with red marks added by my software:
The circles are arranged in 4 columns of 33 rows of circles. At the top of the page, I include a circle at the top left and top right of the page for registration marks.
I was able to locate the registration marks and find their centers. Based on this information, I knew the orientation of the page compared to a carefully-scanned sample. I was able compute the centers of the circles on the sample sheet and stored that information in the program. Then, with the scanned image, I take each point (the centers of the circles shown as red dots in the image above), transform them according to the registration marks, draw a 21x21 square around them (shown as red squares), and capture the bits (1 or 0) in each square. Any square that had more than 200 black bits (0's) was counted as ON.
This actually worked (wow) and I was able to get reliable results. The result is an array of 132 numbers from 1 to 9 or 0 if no circle was filled in. This is exactly the information I need to run the stats.
You'll notice that I print a series of bars at the top of the page. My thought was that I'd scan the Scout's number by scanning it and collecting which bars were long and which were short. I'd always start and end with a long bar then within the code, short bars are 0 and long are 1 making a binary number representing the Scout's number. I decided in the end not to use it because it doesn't take long for me to manually enter the Scout number and it allows me to print generic score cards instead of ones specific to each Scout. I expect that some of the Scouts will mess up the score cards and will need to re-do them. It's a whole lot easier if I can just give them a generic sheet.
Part 2 of the experiment is on Tuesday. I'll let you know how it goes.
Saturday, January 22, 2011
The Great Apple Experiment
My son's Scout troop has taken me up on my offer to help them get their science badges. To do this, we're going to run the Great Apple Experiment with the whole troop. Basically, Nikki Owen made a claim that cutting an apple in half and talking nicely to one half and meanly to the other half makes the nice one rot more slowly than the bad one. Rebecca Watson ran an experiment on this claim and invited everyone to write their results on a Facebook page.
We have 22 Scouts in the troop and I'm going to get them all to run the apple experiment. We'll start by discussing bias, blinding and controls. We'll then cut up the apples, place them into marked containers, and have the scouts talk to them every day until the following week's meeting. We'll then have all the Scouts assess all of the apple slices and collect the results.
I don't have much time to develop and get feedback on the protocol for the experiment, so I thought I'd post it here and get feedback.
Protocol (thanks to PZ Myers for his suggestions):
If you have any comments or suggestions on this protocol, please e-mail me at david@simberon.com or post them as comments to this post.
Thanks
We have 22 Scouts in the troop and I'm going to get them all to run the apple experiment. We'll start by discussing bias, blinding and controls. We'll then cut up the apples, place them into marked containers, and have the scouts talk to them every day until the following week's meeting. We'll then have all the Scouts assess all of the apple slices and collect the results.
I don't have much time to develop and get feedback on the protocol for the experiment, so I thought I'd post it here and get feedback.
Protocol (thanks to PZ Myers for his suggestions):
- Discard any apples that are obviously bruised or nicked
- Wash the apples and slicer before beginning
- Slice apples into 8 pieces (Rebecca suggests 2 pieces but I don't have a budget for that many apples considering the other things I want to do)
- Apple slices are placed into clear containers which are marked by unique numbers
- Each Scout gets 6 apple slices (Love, Hate, Neutral, Untouched Control, Known Preservative, Open Air) chosen at random from the available containers
- For Known Preservative, we'll coat the apple slices with lemon juice
- For Open Air the slice will be kept in a container with no lid. We will use paper towel on top held on by an elastic band to prevent any insects from entering.
- Each container will have a window into which we'll slide a label saying "Love", "Hate" etc. This is to help prevent mistakes while the Scouts have the apples home during the week.
- The container numbers are recorded indicating which number goes to which Scout and what the label on that container reads (Love, Hate, etc).
- Each container will be sealed with a lid that won't be removed for the remainder of the experiment
- The Scouts take the containers home.
- Each day, they will take the Love container to another room, say nice things to it and return it.
- They then take the Hate container to another room, say bad things to it and return it
- They then take the Neutral container to another room, say neutral things to it and return it.
- All other containers remain untouched for the week
- At the end of the week, the Scouts will bring the containers to the meeting
- Each Scout will rate his own 6 slices (knowing which is which) on a scale from 0 (not rotted at all) to 10 (very rotten). We'll provide photos of each level for reference.
- Each Scout will then remove the labels Love, Hate etc. leaving only the numbers on the containers and place them in a line on a table
- A Scout leader will re-arrange all containers randomly
- Each Scout will rank each apple (skipping their own which they've already ranked)
- Scouts cannot show their rankings to other Scouts or discuss the experiment with other Scouts until the experiment is complete.
- Each slice will be grouped by category - Love, Hate, Neutral, etc excluding the Scout's assessment of his/her own slices.
- In each category, the ratings will be averaged and standard deviation calculated
- Each Scout's assessment of his or her own slices will be compared to the averages to determine if the Scouts exhibited bias in assessing their own slices without blinding
- All results will be published on the Internet with photos and videos
If you have any comments or suggestions on this protocol, please e-mail me at david@simberon.com or post them as comments to this post.
Thanks
Thursday, January 20, 2011
OCSTUG meeting Jan 27, 2011
Building Courses in Smalltalk
David Buck
Building a course that supports several programming languages can be tough. Using conventional tools like PowerPoint requires you to maintain multiple copies of each file even though most slides in the file are identical between the courses. In this presentation, David will show the toolset he built to allow him to develop multi-language courses while keeping the content centralized in one copy of the files.
Location: The Code Factory
246 Queen Street, Ottawa
Thursday, January 27th, 2011 at 6:00pm
There will be a $5 charge to pay for the room.
David Buck
Building a course that supports several programming languages can be tough. Using conventional tools like PowerPoint requires you to maintain multiple copies of each file even though most slides in the file are identical between the courses. In this presentation, David will show the toolset he built to allow him to develop multi-language courses while keeping the content centralized in one copy of the files.
Location: The Code Factory
246 Queen Street, Ottawa
Thursday, January 27th, 2011 at 6:00pm
There will be a $5 charge to pay for the room.
Tuesday, January 18, 2011
Multiple Return Values in Smalltalk
In the comments of my "Two-Sided Commit" post, I referred to a technique I heard about from my friend Anthony Lander. I thought I'd write that up explicitly.
Sometimes, you'd really like a method to return multiple values. Suppose, for example, we have a Color object that stores its value as integers for Red, Green and Blue. We may want to compute the Hue, Saturation and Value of the color. If we had individual methods for hue, saturation and value, we'd have to perform the calculation three times. Alternatively, we could write a method for the object called asHSV which returns the hue, saturation and value in an Array. Arrays, though, are messy to deal with because the values are indexed by an integer - you can't use the names hue, saturation, and value. We could also create an object for an HSV representation of the color but that introduces a lot of complexity. The HSV color should work interchangeably with an RGB color and that amount of work may not be justified.
Rather than have multiple return values (or returning an array of values), we can use a three-parameter block and name the parameters:
aColor asHSVDo: [:hue :saturation :value | ... ]
Now, the asHSVDo: method can perform the conversion once and run the block with the calculated values. We get to use nice names for the variables and we don't create extra objects we don't need.
Sometimes, this turns out to be a nice alternative when you think you need multiple return values.
Sometimes, you'd really like a method to return multiple values. Suppose, for example, we have a Color object that stores its value as integers for Red, Green and Blue. We may want to compute the Hue, Saturation and Value of the color. If we had individual methods for hue, saturation and value, we'd have to perform the calculation three times. Alternatively, we could write a method for the object called asHSV which returns the hue, saturation and value in an Array. Arrays, though, are messy to deal with because the values are indexed by an integer - you can't use the names hue, saturation, and value. We could also create an object for an HSV representation of the color but that introduces a lot of complexity. The HSV color should work interchangeably with an RGB color and that amount of work may not be justified.
Rather than have multiple return values (or returning an array of values), we can use a three-parameter block and name the parameters:
aColor asHSVDo: [:hue :saturation :value | ... ]
Now, the asHSVDo: method can perform the conversion once and run the block with the calculated values. We get to use nice names for the variables and we don't create extra objects we don't need.
Sometimes, this turns out to be a nice alternative when you think you need multiple return values.
Friday, January 14, 2011
Two-sided commit
I've encountered an interesting problem for which I've come up with an interesting solution.
My application needs to read records from several input queues and write thode records into a database. The input queues aren't in the database and have their own commit and abort abilities. The problem is that to avoid losing records, I have to coordinate the commits. I need to:
I'd like to isolate knowledge of multiple queues from the caller who writes to the database:
[message := queueManager getMessage.
message notNil] whileTrue: [
self beginTransaction.
self writeMessageIntoDatabase: message.
self commitTransaction
ifTrue: [???? commit]
ifFalse: [???? abort]]
The problem is that I don't know which queue I took the message from - the queue manager isolates me from that knowledge. But I need that knowledge so I can commit or abort the proper queue later.
What are my options? I could have getMessage return both a message and a queue. Then I'd know which queue to commit. It kind of breaks my encapsulation, though. I still need my method to know about the existence of multiple queues and how to commit or abort them.
I could have the queueManager keep track of which queue it pulled the message from so I could tell the queueManager to commit or abort and it would then tell the appropriate queue to commit or abort. This seems really messy, though.
My solution is to do this:
queueManager withAllMessagesDo: [:message |
self beginTransaction.
self writeMessageIntoDatabase: message.
self commitTransaction]
The queueManager delegates the message as-is to each of the queues. Each queue can then extract a message and run the block on it. If it returns true, the queue commits and if it returns false, the queue aborts.
The nice thing about this solution is that all the knowledge of multiple queues and even knowledge of how to commit or abort the queues is encapsulated into the queues and doesn't affect the high level code that needs to write to the database.
My application needs to read records from several input queues and write thode records into a database. The input queues aren't in the database and have their own commit and abort abilities. The problem is that to avoid losing records, I have to coordinate the commits. I need to:
- Read a record from one of the queues
- Write the object into the database
- Commit the database write
- Commit the read from the queue
I'd like to isolate knowledge of multiple queues from the caller who writes to the database:
[message := queueManager getMessage.
message notNil] whileTrue: [
self beginTransaction.
self writeMessageIntoDatabase: message.
self commitTransaction
ifTrue: [???? commit]
ifFalse: [???? abort]]
The problem is that I don't know which queue I took the message from - the queue manager isolates me from that knowledge. But I need that knowledge so I can commit or abort the proper queue later.
What are my options? I could have getMessage return both a message and a queue. Then I'd know which queue to commit. It kind of breaks my encapsulation, though. I still need my method to know about the existence of multiple queues and how to commit or abort them.
I could have the queueManager keep track of which queue it pulled the message from so I could tell the queueManager to commit or abort and it would then tell the appropriate queue to commit or abort. This seems really messy, though.
My solution is to do this:
queueManager withAllMessagesDo: [:message |
self beginTransaction.
self writeMessageIntoDatabase: message.
self commitTransaction]
The queueManager delegates the message as-is to each of the queues. Each queue can then extract a message and run the block on it. If it returns true, the queue commits and if it returns false, the queue aborts.
The nice thing about this solution is that all the knowledge of multiple queues and even knowledge of how to commit or abort the queues is encapsulated into the queues and doesn't affect the high level code that needs to write to the database.
Google Maps Fail
My wife and my son are taking a CPR course tomorrow. To find the location, I consulted Google Maps and got the following street view for the location of the course.
Ok, honey. I'll drop you off on the side of the freeway. Have a nice day.
Ok, honey. I'll drop you off on the side of the freeway. Have a nice day.
Thursday, January 13, 2011
Challenge: Scramble Squares
A few years ago, I was sitting in the waiting room of my dentist's office waiting for my kids to finish their appointments. On the table was a small wooden puzzle containing nine square pieces. Each piece had half a picture along each edge. The puzzle was to arrange the pieces into the 3x3 grid so that each edge matched on either side. The head on one side of the edge matched the tail on the other side and the colors were the same.
Here's a simplified picture of a scrambled puzzle with all the pieces.
When it's solved, the colors all line up and each arrow head is connected to an arrow tail. In the picture above only two edges match up - the blue arrow in the first row and the red arrow in the last column.
I tried solving it but found it amazingly difficult. The receptionist looked over and said "Nobody's ever solved that." I took that as a challenge. I went home and wrote a program in Smalltalk to solve the puzzle. I went back with my solution and amazed them by instantly putting all the pieces in the right places with the right orientations.
So, I challenge you. Can you solve this puzzle? Please don't put solutions into the comments - e-mail them to me at david@simberon and I'll announce the winner who solved the puzzle first. Feel free to write the program in any language. Next week, I'll post the Smalltalk source for my solution.
Have fun!
Here's a simplified picture of a scrambled puzzle with all the pieces.
When it's solved, the colors all line up and each arrow head is connected to an arrow tail. In the picture above only two edges match up - the blue arrow in the first row and the red arrow in the last column.
I tried solving it but found it amazingly difficult. The receptionist looked over and said "Nobody's ever solved that." I took that as a challenge. I went home and wrote a program in Smalltalk to solve the puzzle. I went back with my solution and amazed them by instantly putting all the pieces in the right places with the right orientations.
So, I challenge you. Can you solve this puzzle? Please don't put solutions into the comments - e-mail them to me at david@simberon and I'll announce the winner who solved the puzzle first. Feel free to write the program in any language. Next week, I'll post the Smalltalk source for my solution.
Have fun!
Tuesday, January 11, 2011
Refactoring (Smalltalk) Web Course
Simberon will be holding the course Refactoring Object Oriented Design as a webcast course during the week of March 7th, 2011. For details and to register, go to the Simberon web site.
The Fun of Software Development
I first started writing computer programs back in 1978 on my home-built computer. Back then, software development was fun and I wrote lots of creative little programs just because it pleased me.
In the professional software development world, it's easy to get caught up in requirements gathering or maintenance activities and forget that software development should be fun.
In this blog, I hope to re-kindle that creative and fun spark by proposing projects you can work on to improve your software development skills and to enjoy it at the same time.
If you have any suggestions for fun projects, let me know and I'll try to incorporate your ideas.
Have fun!
In the professional software development world, it's easy to get caught up in requirements gathering or maintenance activities and forget that software development should be fun.
In this blog, I hope to re-kindle that creative and fun spark by proposing projects you can work on to improve your software development skills and to enjoy it at the same time.
If you have any suggestions for fun projects, let me know and I'll try to incorporate your ideas.
Have fun!
Monday, January 10, 2011
Welcome to my Blog
My name is David Buck and I've been a software developer for 32 years now. My programming language of choice these days is Smalltalk but I'm interested in object oriented development, agile development, and software development practices. I host the Simberon Design Minute and I'm a co-host of the Independent Misinterpretations podcast.
You may know me as the DKB from DKBTrace - the ray tracing program that kicked off POVRay.
In this blog, I hope to provide food for thought for software developers and suggestions for helping developers improve the quality of their code. I also enjoy teaching about software development and encouraging people to have fun with software development. In addition to ray tracing graphics software, I've worked with simulated physics in ElastoLab, artificial intelligence with neural networks, 3D VRML worlds (see Pool World and Beach World) and computer music.
I welcome you to join me in an exploration of the world of software development.
I welcome your comments and your suggestions.
You may know me as the DKB from DKBTrace - the ray tracing program that kicked off POVRay.
In this blog, I hope to provide food for thought for software developers and suggestions for helping developers improve the quality of their code. I also enjoy teaching about software development and encouraging people to have fun with software development. In addition to ray tracing graphics software, I've worked with simulated physics in ElastoLab, artificial intelligence with neural networks, 3D VRML worlds (see Pool World and Beach World) and computer music.
I welcome you to join me in an exploration of the world of software development.
I welcome your comments and your suggestions.