tag:blogger.com,1999:blog-99453632024-03-13T20:11:32.741-07:00Dale WilsonI've been programming for 30+ years in more languages that I can count. I've been weaving (as in cloth) for several years. Weaving and computers mix well. My newest obsession (if you don't count the bike I just bought) is learning to play the upright bass. My even newer obsession is contra dancing.<br/>I am also <a href="http://www.twainquotes.com/Refinement.html">"the kind of person that keeps a parrot" (see the Mark Twain quote)</a>Dale Wilsonhttp://www.blogger.com/profile/06832140112844486034noreply@blogger.comBlogger74125tag:blogger.com,1999:blog-9945363.post-2806302332038352482012-03-19T12:37:00.001-07:002012-03-19T13:15:34.436-07:00Agile programming in extremis -- the final episode.See the previous post (<a href="http://wilsond.blogspot.com/2012/03/agile-programming-in-extremis.html">Agile programming in extremis</a>) for the start of the story. <br/><br/>
The code needed one final refactoring to reach it's ultimate state. (8 files, 64 lines of production code. 160 lines of test code (yes, the test got smaller!) The observation that triggered this one was, “Some of the tests depend on other objects working correctly. The test for each object should stand alone.”
<pre>
public class EventListener{
private EventComparer eventComparer;
private DuplicateEventHandler duplicateEventHandler;
private Event previousEvent = null;
public EventListener(
DuplicateEventHandler duplicateEventHandler){
this.(duplicateEventHandler, new EventComparer());
}
EventListener(
DuplicateEventHandler duplicateEventHandler,
EventComparer eventComparer)
this.duplicateEventHandler = duplicateEventHandler;
this.eventComparer = eventComparer;
}
public void onEvent(Event currentEvent)
{
bool previousEventIsValid = (previousEvent != null);
if(previousEventIsValid && eventComparer.isSameEvent(
previousEvent, currentEvent)) {
duplicateEventHandler.handleSameEvent(currentEvent);
}
previousEvent = currentEvent.cloneEvent();
}
};
public class EventComparer{
private EventLocationComparer eventLocationComparer;
private EventTimeComparer eventTimeComparer;
public EventComparer(){
this(new EventLocationComparer(), new EventTimeComparer());
}
EventComparer(
EventLocationComparer eventLocationComparer,
EventTimeComparer eventTImeComparer){
this.eventLocationComparer = eventLocationComparer;
this.eventTimeComparer = eventTimeComparer;
}
public bool isSameEvent(
Event currentEvent, Event previousEvent){
return eventLocationComparer.isSameLocation(
currentEvent, prevousEvent) &&
eventTimeComparer.isSameTime(
currentEvent, previousEvent);
}
};
public class EventLocationComparer{
static final int X_LIMIT = 200;
static final int Y_LIMIT = 200;
public bool isSameLocation(
Event currentEvent, Event previousEvent){
return
(isSamePoint(currentEvent.getX(), previousEvent.getX(),
X_LIMIT) &&
(isSamePoint(currentEvent.getY(), previousEvent.getY(),
Y_LIMIT);
}
public bool isSamePoint(
int current, int previous, int tolerance){
return math.abs(current - previous) < tolerance;
}
};
public class TimeComparer{
static final long TIME_LIMIT = 500L;
public bool isSameTime(
Event currentEvent, Event previousEvent){
return currentEvent.getTime() - previousEvent.getTime()
< TIME_LIMIT;
}
};
public class EventListenerTest{
private EventListener testObject;
@Mock
private EventComparer eventComparer;
@Mock
private Event eventOne;
@Mock
private Event eventTwo;
@Before
public void setup(){
testObject = new EventListener(
duplicateEventHandler, eventComparer);
}
@Test
public void whenEventComparerReturnsTrueHandleSameEvent(){
when(eventComparer.isSameEvent).
withArguments(eventOne, eventTwo).
thenReturn(true);
testObject.onEvent(eventOne);
testObject.onEvent(eventTwo);
verify(testObject).handleSameEvent());
}
@Test
public void whenEventComparerReturnsFalseDoNotHandleSameEvent(){
when(eventComparer.isSameEvent).
withArguments(eventOne, eventTwo).
thenReturn(false);
testObject.onEvent(eventOne);
testObject.onEvent(eventTwo);
verify(testObject,never()).handleSameEvent());
}
};
public class EventComparerTest{
private EventComparer testObject;
@Mock
private EventLocationComparer eventLocationComparer;
@Mock
private EventTimeComparer eventTimeComparer;
@Mock
private Event eventOne;
@Mock
private Event eventTwo;
@Before
public void setup(){
testObject = new EventComparer(
eventLocationComparer, eventTimeComparer);
}
@Test
public void testIfSameLocationAndSameTimeThenReturnTrue(){
when(EventLocationComparer.isSameLocation).
withArguments(eventOne, eventTwo).
thenReturn(true);
when(EventTimeComparer.isSameTime).
withArguments(eventOne, eventTwo).
thenReturn(true);
assertTrue(testObject(eventOne, eventTwo));
}
// additonal tests for the other cases go here.
}
public class EventLocationComparerTest{
private EventLocationComparer testObject;
private Event event1 = new Event(parameter);
@Before
public void setup(){
testObject = new EventLocationComparer();
}
@Test
public void thisIsATypicalTest(){
Event event2 = new Event(various parameters
to test boundary conditions);
bool testResult = testObject.isSameLocation(test1, test2);
assertTrue(testResult);
}
@Test
public void thisIsAnotherTypicalTest(){
Random random = new Random();
int testValue1 = random.next();
int testLimit = random.next();
int testValue2 = testValue1
plus or minus testLimit
plus or minus one.
bool testResult = testObject.isSamePoint(
testValue1, testValue2, testLimit);
assertTrue(testResult);
}
};
public class EventTimeComparerTest{
private EventTimeComparer testObject;
@Before
public void setup(){
testObject = new EventTimeComparer();
}
@Test
public void thisIsATypicalTest(){
Event event2 = new Event(various parameters
to test boundary conditions);
bool testResult = testObject.isSameTime(
test1, test2);
assertTrue(testResult);
}
};
</pre>
This is not a criticism of Agile methodologies in general. It's just an observation that moderation and common sense can be appropriate in Agile development, too. There must be a happy-state somewhere between the first and the last versions of this code.
I'll mention one more interesting point. After all the above work, the system still had a major bug (a show-stopper as it turns out). The "handleSameEvent method which is not shown here, was just as thoroughly tested. Alas, even though it passed the tests, it did not, in fact, do the right thing. One wonders (OK, I wonder) if the time that was invested in rewriting and re-rewriting this working and tested code had been invested in diagnosing the real problem whether the system as a whole might be in better shape.Dale Wilsonhttp://www.blogger.com/profile/06832140112844486034noreply@blogger.com0tag:blogger.com,1999:blog-9945363.post-63880328085472578172012-03-19T12:37:00.000-07:002012-03-19T12:37:00.229-07:00Agile programming in extremis -- third refactoring's a charm.See the previous post (<a href="http://wilsond.blogspot.com/2012/03/agile-programming-in-extremis.html">Agile programming in extremis</a>) for the start of the story. <br/><br/>
The problem with the code from the previous post is "All those private methods are untestable." To resolve this we refactor again -- ending up with 8 files containing 50 lines of production code and approximately 180 lines of test code, like so:
<pre>
public class EventListener{
private EventComparer eventComparer = new EventComparer();
private DuplicateEventHandler duplicateEventHandler;
private Event previousEvent = null;
public EventListener(
DuplicateEventHandler duplicateEventHandler){
this.duplicateEventHandler = duplicateEventHandler;
}
public void onEvent(Event currentEvent)
{
bool previousEventIsValid = (previousEvent != null);
if(previousEventIsValid
&& eventComparer.isSameEvent(
previousEvent, currentEvent)) {
duplicateEventHandler.handleSameEvent(currentEvent);
}
previousEvent = currentEvent.cloneEvent();
}
};
public class EventComparer{
private EventLocationComparer eventLocationComparer
= new EventLocationComparer();
private EventTimeComparer eventTimeComparer =
new EventTimeComparer();
public bool sameEvent(
Event currentEvent, Event previousEvent){
return
eventLocationComparer.isSameLocation(
currentEvent, prevousEvent) &&
eventTimeComparer.isSameTime(
currentEvent, previousEvent);
}
};
public class LocationComparer{
static final int X_LIMIT = 200;
static final int Y_LIMIT = 200;
public bool isSameLocation(
Event currentEvent, Event previousEvent){
return (isSamePoint(
currentEvent.getX(),
previousEvent.getX(),
X_LIMIT) &&
(isSamePoint(
currentEvent.getY(),
previousEvent.getY(),
Y_LIMIT);
}
public bool isSamePoint(
int current, int previous, int tolerance){
return math.abs(current - previous) < tolerance;
}
};
public class TimeComparer{
static final long TIME_LIMIT = 500L;
public bool isSameTime(
Event currentEvent, Event previousEvent){
return currentEvent.getTime() - previousEvent.getTime()
< TIME_LIMIT;
}
};
public class EventListenerTest{
private EventListener testObject;
private Event event1 = new Event(parameter);
@Mock
private DuplicateEventHandler duplicateEventHandler;
@Before
public void setup(){
testObject = new EventListener(duplicateEventHandler);
testObject.onEvent(event1);
}
@Test
public void thisIsATypicalTest(){
Event event2 = new Event(various parameters
to test boundary conditions);
testObject.onEvent(event2);
verify(duplicateEventHandler).handleSameEvent(event2);
// or depending on the parameters to event2...
verify(duplicateEventHandler, never()).
handleSameEvent(event2);
}
};
public class EventComparerTest{
private EventComparer testObject;
private Event event1 = new Event(parameter);
@Before
public void setup(){
testObject = new EventComparer();
}
@Test
public void thisIsATypicalTest(){
Event event2 = new Event(various parameters
to test boundary conditions);
bool testResult = testObject.sameEvent(test1, test2);
assertTrue(testResult);
}
};
public class EventLocationComparerTest{
private EventLocationComparer testObject;
private Event event1 = new Event(parameter);
@Before
public void setup(){
testObject = new EventLocationComparer();
}
@Test
public void thisIsATypicalTest(){
Event event2 = new Event(various parameters
to test boundary conditions);
bool testResult = testObject.isSameLocation(
test1, test2);
assertTrue(testResult);
}
@Test
public void thisIsAnotherTypicalTest(){
Random random = new Random();
int testValue1 = random.next();
int testLimit = random.next();
int testValue2 = testValue1
plus or minus testLimit plus or minus one.
bool testResult = testObject.isSamePoint(
testValue1, testValue2, testLimit);
assertTrue(testResult);
}
};
public class EventTimeComparerTest{
private EventTimeComparer testObject;
@Before
public void setup(){
testObject = new EventTimeComparer();
}
@Test
public void thisIsATypicalTest(){
Event event2 = new Event(various parameters
to test boundary conditions);
bool testResult = testObject.isSameTime(test1, test2);
assertTrue(testResult);
}
};
</pre>
It may be a bit long, but hey, it's testable. (..or not. See the next post.)Dale Wilsonhttp://www.blogger.com/profile/06832140112844486034noreply@blogger.com0tag:blogger.com,1999:blog-9945363.post-6243144810742546472012-03-19T12:07:00.000-07:002012-03-19T12:39:16.795-07:00Agile Programming in extremis -- re-refactoring.See the previous post (<a href="http://wilsond.blogspot.com/2012/03/agile-programming-in-extremis.html">Agile programming in extremis</a>) for the start of the story. <br/><br/>
The next person to look at the code said: "What does isSameEvent do? I can't tell from the code." The changes resulted in two files containing 39 lines of production code and 50 lines of test code.
<pre>
public class EventListener{
static final int X_LIMIT = 200;
static final int Y_LIMIT = 200;
static final long TIME_LIMIT = 500L;
private DuplicateEventHandler duplicateEventHandler;
private Event previousEvent = null;
public EventListener(
DuplicateEventHandler duplicateEventHandler){
this.duplicateEventHandler = duplicateEventHandler;
}
public void onEvent(Event currentEvent)
{
bool previousEventIsValid = (previousEvent != null);
if(previousEventIsValid
&& isSameEvent(previousEvent, currentEvent)) {
duplicateEventHandler.handleSameEvent(currentEvent);
}
previousEvent = currentEvent.cloneEvent();
}
private bool isSameEvent(
Event currentEvent, Event previousEvent){
return isSameLocation(currentEvent, prevousEvent) &&
isSameTime(currentEvent, previousEvent);
}
private bool isSameLocation(
Event currentEvent, Event previousEvent){
return (isSamePoint(
currentEvent.getX(),
previousEvent.getX(),
X_LIMIT) &&
(isSamePoint(
currentEvent.getY(),
previousEvent.getY(),
Y_LIMIT);
}
private bool isSameTime(
Event currentEvent, Event previousEvent){
return currentEvent.getTime() - previousEvent.getTime()
< TIME_LIMIT;
}
private bool isSamePoint(
int current, int previous, int tolerance){
return math.abs(current - previous) < tolerance;
}
};
public class EventListenerTest{
private EventListener testObject;
private Event event1 = new Event(parameter);
@Mock
private DuplicateEventHandler duplicateEventHandler;
@Before
public void setup(){
testObject = new EventListener(
duplicateEventHandler);
testObject.onEvent(event1);
}
@Test
public void thisIsATypicalTest(){
Event event2 = new Event(varying parameters
to test boundary conditions);
testObject.onEvent(event2);
verify(duplicateEventHandler).handleSameEvent(event2);
// or depending on the parameters to event2...
verify(duplicateEventHandler, never()).
handleSameEvent(event2);
}
};
</pre>Dale Wilsonhttp://www.blogger.com/profile/06832140112844486034noreply@blogger.com0tag:blogger.com,1999:blog-9945363.post-82589640453711387752012-03-19T12:01:00.000-07:002012-03-20T11:44:46.732-07:00Agile programming in extremis -- the first refactoring.See the previous post (<a href="http://wilsond.blogspot.com/2012/03/agile-programming-in-extremis.html">Agile programming in extremis</a>) for the start of the story. <br/><br/>
The first agile developer to examine this code said: “It’s not clear what that if statement is testing.” and produced the following improved version (2 files; 28 lines of production code; around 50 lines of test code):
<pre>
public class EventListener{
static final int X_LIMIT = 200;
static final int Y_LIMIT = 200;
static final long TIME_LIMIT = 500L;
private DuplicateEventHandler duplicateEventHandler;
private Event previousEvent = null;
public EventListener(
DuplicateEventHandler duplicateEventHandler){
this.duplicateEventHandler = duplicateEventHandler;
}
public void onEvent(Event currentEvent)
{
bool previousEventIsValid = (previousEvent != null);
if(previousEventIsValid &&
isSameEvent(previousEvent, currentEvent)) {
duplicateEventHandler.handleSameEvent(currentEvent);
}
previousEvent = currentEvent.cloneEvent();
}
private bool isSameEvent(
Event currentEvent, Event previousEvent){
return
((math.abs(currentEvent.getX() – previousEvent.getX())
< X_LIMIT) &&
(math.abs(currentEvent.getY() – previousEvent.getY())
< Y_LIMIT) &&
(currentEvent.getTime – previousEvent.getTime()
< TIME_LIMIT));
}
};
public class EventListenerTest{
private EventListener testObject;
private Event event1 = new Event(parameter);
@Mock
private DuplicateEventHandler duplicateEventHandler;
@Before
public void setup(){
testObject = new EventListener(duplicateEventHandler);
testObject.onEvent(event1);
}
@Test
public void thisIsATypicalTest(){
Event event2 = new Event(various parameters
to test boundary conditions);
testObject.onEvent(event2);
verify(duplicateEventHandler).handleSameEvent(event2);
// or depending on the parameters to event2...
verify(duplicateEventHandler, never()).handleSameEvent
(event2);
}
};
</pre>Dale Wilsonhttp://www.blogger.com/profile/06832140112844486034noreply@blogger.com0tag:blogger.com,1999:blog-9945363.post-70996391232804786542012-03-19T11:51:00.001-07:002012-03-19T11:51:53.911-07:00Agile programming in extremisI was recently working on a project with a team that had adopted Agile development methods with a vengeance. In the next few posts, I'll report on the evolution of some code during repeated refactorings.<br />
<br />
To protect the innocent and obscure the guilty, I am paraphrasing the actual code and omitted some of the boilerplate and other detail, but rest assured that Dave Barry would agree that "I am not making this up."<br />
<br />
To get started, here is the original code. There are two files. Before I formatted it for this blog it containing 23 lines of production code and around 50 lines of test code (of which 23 are shown here.)<br />
<br />
<pre>
public class EventListener{
static final int X_LIMIT = 200;
static final int Y_LIMIT = 200;
static final long TIME_LIMIT = 500L;
private final DuplicateEventHandler duplicateEventHandler;
private Event previousEvent = null;
public EventListener(
DuplicateEventHandler duplicateEventHandler){
this.duplicateEventHandler = duplicateEventHandler;
}
public void onEvent(Event currentEvent)
{
if( previousEvent != null &&
(math.abs(currentEvent.getX() – previousEvent.getX())
< X_LIMIT) &&
(math.abs(currentEvent.getY() – previousEvent.getY())
< Y_LIMIT) &&
(currentEvent.getTime – previousEvent.getTime()
< TIME_LIMIT)) {
duplicateEventHandler.handleSameEvent(currentEvent);
}
previousEvent = currentEvent.cloneEvent();
}
};
public class EventListenerTest{
private EventListener testObject;
private Event event1 = new Event(parameter);
@Mock
private DuplicateEventHandler duplicateEventHandler;
@Before
public void setup(){
testObject = new EventListener(duplicateEventHandler);
testObject.onEvent(event1);
}
@Test
public void thisIsATypicalTest(){
Event event2 = new Event(various parameters
to test boundary conditions);
testObject.onEvent(event2);
verify(duplicateEventHandler).handleSameEvent(event2);
// or depending on the parameters to event2...
verify(duplicateEventHandler, never()).handleSameEvent(
event2);
}
};
</pre>
If you are a programmer, now would be a good time to examine the code above and decide what changes, if any, you would make to improve it.Dale Wilsonhttp://www.blogger.com/profile/06832140112844486034noreply@blogger.com0tag:blogger.com,1999:blog-9945363.post-69552069202604483762012-03-19T11:36:00.002-07:002012-03-19T11:36:34.039-07:00Three and a bit years lapse....I haven't added anything to this blog since January 2009. Lots has happened, but apparently a facebook status update is sufficient. Now I have something more meaty to talk about, so....Dale Wilsonhttp://www.blogger.com/profile/06832140112844486034noreply@blogger.com0tag:blogger.com,1999:blog-9945363.post-38740133335676730872009-01-28T11:37:00.000-08:002009-01-28T11:51:04.448-08:00TardisSomeday my Tardis is going to have been on backorder by now, but apparently some people have their's already. <br /><br />I say this because I have a new <a href="http://h10010.www1.hp.com/wwpc/us/en/sm/WF25a/321957-321957-64295-3841267-306995-3872994.html">netbook </a>on order. <br /><br />I was an early adopter on this one. It went on sale the 20th and I ordered that morning.<br /><br />The original scheduled ship date was 1/27 (yesterday as I write this) but instead I got an email that said:<br /><br /><blockquote>Dear WILSON DALE<br /><br />Due to a delay in fulfilling your order # xxxxx placed on 1/20/2009, it may miss the estimated ship date that was on your original order confirmation. We sincerely apologize for this inconvenience.<br /></blockquote><br />However there are already five "customer reviews" of this computer out on the HP web site, so either these folks have their Tardis up and running or they are reviewing a computer that they haven't even seen yet. <br /><br />For what it's worth: 3 of 5 (60%) customers recommend this product.<br /><br />[Oh, alright, maybe they got advanced copies for review, or are basing their review on what they saw at CES, but I doubt it, and in either case that doesn't qualify as a "customer review" in my book.]Dale Wilsonhttp://www.blogger.com/profile/06832140112844486034noreply@blogger.com0tag:blogger.com,1999:blog-9945363.post-60218975531211102432008-08-10T13:00:00.000-07:002008-11-10T12:49:43.128-08:00My Contra DanceI've been doing a lot of contra dancing recently, and have tried my hand at writing some dances. I've also been experimenting with Google Docs as a way to publish them.<br />For example:<br /> <br /><a title="Fly Around My Pretty Little Miss" href="http://docs.google.com/Doc?id=dcg3h6cf_5fdpmggc5">Fly Around My Pretty Little Miss</a>Dale Wilsonhttp://www.blogger.com/profile/06832140112844486034noreply@blogger.com0tag:blogger.com,1999:blog-9945363.post-42820144805431258222008-06-21T18:55:00.000-07:002008-06-21T19:05:31.652-07:00Contra DancingAs I mention in my profile, I have a new obsession --<a href="http://www.childgrove.org"> contra dancing</a> with occasional forays into <a href="http://members.aol.com/paradiseMO/english.html">English Country Dancing</a>. Obsessions come; obsessions go; sometimes they stick around. I'm hoping this one will stick. It s great fun, great exercise, and I've met some really wonderful people at dances.<br /><br />Any good obsession should fit in with my previous obsessions. The connection to folk music and upright bass is obvious. Computers are a bit more of a stretch, but in my (ahem) spare time, I have started working on a program to help design dances and/or catalog existing dances. Then there's the weaving.<br /><br />A contra dance is all about patterns fitting in a fairly constrained framework. Weaver's will feel right at home. The question is can there be cross-over. Could one weave a contra dance, or dance a weave structure. Hmmmm.....<br /><br />If you're in the St. Louis area check the Childgrove site linked above, and come to a dance (usually Sunday night.) You'll be glad you did.<br /><br />Oh, and the link to parrots? Well, Willow went to a flash dance recently. She was a big hit.Dale Wilsonhttp://www.blogger.com/profile/06832140112844486034noreply@blogger.com0tag:blogger.com,1999:blog-9945363.post-38569352171325618052008-01-16T13:23:00.000-08:002008-01-16T13:41:12.708-08:00Artificial Inscrutability vs Human Pattern Recognition: A ChallengeI was messing with the automatic language translation sites on the Internet. I started with the lyrics of a song, translated them from English to French to German, back to French, then back to English, and came up with:<br /><br /><blockquote>The scandal is currently sufficient<br />May with the sun them on you<br />that supplements expensive you;<br />And the pure light with you<br />to lead your house in the kind.</blockquote><br /><br />Anyone recognize the original song?<br /><br />Hints:<br /><br /><ul><li>The song came from an album released in 1968 so there's an age bias in this challenge.</li><li>The album has been reissued on CD -- I was pleased.</li><li>I believe the lyrics were actually borrowed from a traditional folk song.</li><li>Somewhere along the line the translation acquired an extra line (and some extra concepts!) The original verse was four lines long.</li><li>The original lyrics were comprehensible, although I've got to admit you could probably substitute the translated version into the song and few people would notice.</li></ul>Dale Wilsonhttp://www.blogger.com/profile/06832140112844486034noreply@blogger.com4tag:blogger.com,1999:blog-9945363.post-67849614023525414712007-11-29T07:49:00.000-08:002007-11-29T08:22:44.682-08:00SimplicityMy previous rant was in response the frustration that built up while I was reading various standards documents and also using some ACE code [1]. In the interests of simplicity, however I should have just quoted Stephen Dewhurst from his book <i>C++ Common Knowledge</i>:<br /><br /><blockquote>It's surprising how much of advanced object-oriented programming is basic common sense surrounded by impenetrable syntax.</blockquote><br /><br />Dale<br /><hr><br />[1] Pop quiz: An ACE_Map_Manager does not "manage" maps, it <b><i>is</i></b> a map. A map is a useful container for data. There are a couple of ways to describe the function it provides:<br /><br />As a map: Given a <span style="font-weight:bold;">key</span>, map that into a corresponding <span style="font-weight:bold;">value</span>.<br />As a dictionary: Given a <span style="font-weight:bold;">word</span>, provide a <span style="font-weight:bold;">definition</span>.<br />As an associative array: Given an <span style="font-weight:bold;">index </span>of arbitrary type, return the associated <span style="font-weight:bold;">value</span>.<br /><br />These points of view are equivalent (although the dictionary view implies that keys and values are text which is too restrictive). They describe useful ways to think about ACE_Map as a tool for solving a real programming problems. In each case there are two interesting values: The key (or word, or index), and the associated value (or definition).<br /><br />In ACE, these are called int_id and ext_id (not necessarily in that order).<br /><br />Question: Which one is the key and which one is the value?<br /><br />Hint: int stands for internal (not the C++ data type int); and ext stands for external. <br /><br />So int_id must be the data that is stored inside the map and ext_id is name the external world uses to get to that data.<br /><br />No, now that I think of it int_id is the name used internally to identify the data and ext_id is the data that is of interest to the external world. <br /><br />Hmmmm..<br /><br />Answer: I have no idea. I have to look it up (in the code because the documentation doesn't help!) every time I use an ACE_Map or else find some existing code that uses it and copy/paste it into the code I am writing.<br /><br />Extra Credit Question: ACE has been around well over ten years now and is used in thousands of applications. How many programmer-years have been wasted trying to remember which "id" is which?Dale Wilsonhttp://www.blogger.com/profile/06832140112844486034noreply@blogger.com1tag:blogger.com,1999:blog-9945363.post-44073304482759562732007-11-26T15:13:00.000-08:002008-01-17T04:54:21.267-08:00'Tis a gift to be simpleThis used to be a long rambling blog entry in which I complained about the tendency of technical folks, to oversimplify complex problems, then add the complexity back in by using obscure terminology accessible only to the in-crowd.<br /><br />The next entry, which in the topsy-turvy world of blogs is up ^^^ there, and which you've probably already read, quoted Stephen Dewherst who said it much more clearly and concisely.<br /><br />So I rewrote this entry.<br /><br />Hey if I can't change history in my own blog, where can I change it?Dale Wilsonhttp://www.blogger.com/profile/06832140112844486034noreply@blogger.com4tag:blogger.com,1999:blog-9945363.post-75685905917364375272007-11-14T13:29:00.000-08:002007-11-14T13:42:56.377-08:00Getting better all the timeAs always this works better if you read the blog in reverse order -- going back to "An out-of-box experience." I've been recording the impressions of a brand new iMac user.<br /><br />Several people recommended Quicksilver for my new Mac. I finally got it installed and running yesterday, and you know what? I don't miss the start menu any more! Quicksilver is truly the missing link in MacOS.<br /><br />And, since the Logitech keyboard has a real, live delete key, I'm running out of things to complain about.<br /><br />Let the record show that Spaces combined with Windows Remote Desktop Client (RDC) running in full screen mode makes working remotely on a windows machine a pleasure. Take some time to configure RDC, though, it's defaults are silly, and because I grabbed the beta version it's documentation is all stubs. Unfortunately most of the Fn keys are interpreted locally rather than being sent to the windows machine. Maybe I'll have to get the non-beta version just to read the doc. There--I found something to complain about anyway -- even if it was Microsoft software.Dale Wilsonhttp://www.blogger.com/profile/06832140112844486034noreply@blogger.com2tag:blogger.com,1999:blog-9945363.post-21136486428881385062007-11-08T09:01:00.000-08:002007-11-08T09:20:34.517-08:00MacOS is exactly like Windows except everything is different.A couple of days of silence is good news.<br /><br />The iMac up and running. I'm finding my way around. Just about everything I have tried so far is a lot harder on Mac then it would be on Windows, but of course that's because I'm try to do Windows stuff. <br /><br />Dear Mac folks, keep that in mind when you say Windows is harder to use than Mac. If you try to do Mac stuff on Windows, well D'oh!<br /><br />I haven't run into any more BSOF level issues, just a continuous, low-level state of frustration. Typical issues include:<br /><p/><br /><span style="font-weight: bold; font-style: italic; color: rgb(255, 102, 0);">Right click doesn't work to bring up menus. </span> Of course, once I figured out that you had to actually turn the right click on in system preferences, it works. But for heavens sake, why have such a stupid default. Two buttons on the mouse that do exactly the same thing is silly -- unless of course you are a true believer that one button should be enough for anyone (and 640K RAM is good enough for anything you'd ever want to do on a personal computer, right?) [Note: a two hand/two finger click is *NOT* and acceptable substitute for a right click]<br /><p/><br />Speaking of two hands and two fingers, <span style="font-weight: bold; font-style: italic; color: rgb(255, 102, 0);">where's the @#$%^&* delete key!</span> Please don't tell me I don't need it. I know I don't need it -- I want it 'cause it makes life MUCH easier.<br /><p/><br /><span style="font-weight: bold; font-style: italic; color: rgb(255, 102, 0);">I can't get back to my home page in Safari</span><span style="color: rgb(255, 102, 0);">.</span> I went through ALL the menus, looking for a "go to home" option. You can't do it through menus. You can't do it through a toobar button[*]. I can't find a keyboard shortcut to do it. For a while I simply used "open a new Safari window" but that's an unpleasant mini-hack. [*]Finally someone told me I needed to customize my toolbar to put the "go to home page" button on it. For heaven's sake, why have such a stupid default. Going "home" is such a common thing to want to do, why should I have to turn it on -- and why didn't Safari's help tell me how to do it!<br /><p/><br /><span style="font-weight: bold; font-style: italic; color: rgb(255, 102, 0);">I miss the Start menu.</span> For all the flack it's taken over the years, the Start menu works really well. It caches recently used applications so it adapts to your normal usage patterns, yet for unusual situations the "All Programs" option lets you enter a nicely hierarchical organized list of the applications on this system.<br /><br />Sure I could double click on the hard drive, then double click on applications to get to the equivalent of "All Programs", but Start menu is a lot more convenient -- it tends to "do-the-right-thing[TM]" automatically, and when it doesn't it listens politely when you explain things to it.<br /><p/><br />PS: I've started rolling my own start menu by putting a shortcut to the Application folder on the dock. For some reason <span style="font-weight: bold; font-style: italic; color: rgb(255, 102, 0);">it wouldn't let me put it where I wanted it</span><span style="color: rgb(255, 102, 0);"> </span>-- with the other frequently used applications. Instead it insisted that it had to be over on the right side of the dock. I'm sure there's a lame excuse (er. I mean a perfectly valid and logical reason) for this restriction, but the motivation escapes me.<br /><p/><br />And there's more. You get the idea. Once I have adapted to the differences, I'm sure that MacOS will blend into the background and I can concentrate on doing the stuff that interests me rather than playing with the (admittedly pretty) operating system controls, just like I do on Windows where CTRL-ALT-= pops open the calculator; CTRL-ALT-T gets me notepad; CTRL-ALT-D opens a new DOS window, etc. [And don't tell me about the dashboard -- I know about it -- it's just like Windows except completely different (grin)]<br /><br />Speaking of missing keys, and strange keyboards. The Logitech wireless keyboard & mouse came last night. O Frabjous Day, Calloo, Callay, I can type again! [Anyone wanna buy a Apple Wireless keyboard -- barely used.]<br /><p/><br />And to end on a positive note. I was able to read recordings I made with my <a href="http://www.rolandus.com/products/productdetails.aspx?ObjectId=757">digital recorder</a> in and edit them with an <a href="http://audacity.sourceforge.net/">open source audio editor</a> then play them back on my "home theater" speakers with no grief. That's what I'm talking about when I mean the OS should disappear into the background and let me focus on the work I want to do.Dale Wilsonhttp://www.blogger.com/profile/06832140112844486034noreply@blogger.com1tag:blogger.com,1999:blog-9945363.post-47761389840813066752007-11-03T06:19:00.000-07:002007-11-03T07:05:29.532-07:00How I spent the last 12 hours<div>Read the previous two blog entries first.</div><div><br /></div>Let's see -- <div><br /><ul><li>Retrying the install didn't fix the problem.<br /></li><li>Booting in single user mode and hiding my preferences didn't fix the problem (got that idea from the <a href="http://docs.info.apple.com/article.html?artnum=106464">this article</a> in the apple knowledge base)<br /></li><li>Booting in single user mode and running dscl as described in <a href="http://docs.info.apple.com/article.html?artnum=306840">this article</a> didn't work at all -- the "launchctl" command failed -- never mind actually running dscl.<br /></li><li>Various other attempts happened. I was starting to get seriously worried, or at least to think I'd have to bundle the thing back into the box and tote it over to the nearest Apple store for help.</li></ul></div><div><br /></div><div><humor> One of the joys of Windows is how *many* times you get to install it on the same machine. Yet another feature Apple has copied from Microsoft</humor></div><div><br />Finally I found this <a href="http://discussions.apple.com/thread.jspa?messageID=5733847&#5733847">thread</a> on the Leopard install discussion page that suggested using the "Archive and Install" w/o saving user settings during the upgrade. That worked (although I got to play twenty questions again) <div><br /></div><div>It's up!</div><div><br /></div><div>-------</div><div>And to respond to some of the comments to previous posts.</div><div><br /></div><div>When I went into this my good friends who use Macs all told me -- you'll be up and running within minutes of opening the box. It's not like Windows. This is <awe>Macintosh!</awe></div><div><br /></div><div>I'm not really criticising them (or Apple). I'm just reporting what really happened and how it make me as a new Mac owner feel about the experience. That means I don't have to be "fair" If it pisses me off, it pisses me off even if there is a perfectly good excuse (er, I mean reason) for the problem.</div><div><br /></div><div>In response to the comment "most mac users are waiting for the ".1" release." How Microsoftian of them! But seriously, if an experienced computer user who is trying hard to play it straight [except for the phone number thing] using a brand new, all-Apple, out-of-the-box machine encounters this troubles, how is good ol' Aunt Tilly gonna feel? If I had a lot invested in existing work on the machine, I might wait for .1, too. I'm still waiting on Vista --- although since I need to buy a copy of Windows to run on this machine under VMWare, I'll probably end up running Vista on THIS machine. Strange, but true.</div><div><br /></div><div>Oh yeah, one of the "brags" of the Mac crowd is they don't stuff your machine with cripple-ware. Um--guys--there's a hobbled copy of Office on this machine! Admittedly its not as cluttered as a new Dell would be but...</div><div><br /></div><div>Anyway, I have a new wireless-Mac keyboard on order from Logitech. It comes with a wireless mouse, too, which is good cause the mighty mouse, although functional, doesn't feel especially good when I use it.</div><div><br /></div><div>In retrospect I should have taken the stock system from the Apple store, ordered a 2gig memory module and installed it myself (to get to 3G rather than the 2x1G I have now) and shopped for a wireless keyboard/mouse. </div><div><br /></div><div>Now, let's have some fun!</div><div><br /></div><div>Dale</div><div><br /></div><div><br /></div><div><br /></div><div><br /></div></div>Dale Wilsonhttp://www.blogger.com/profile/06832140112844486034noreply@blogger.com2tag:blogger.com,1999:blog-9945363.post-15711896928457782862007-11-02T07:18:00.000-07:002007-11-02T08:11:08.392-07:00Look, it's a Tiger; no, it's a Leopard; no, it's a paperweight, NO.....Blog entry #2 about my brand new iMac. See the previous entry for background:<br /><br />Aha, I found where they told me they hadn't installed Leopard on my new iMac. All I had to do is find the Leopard CD -- break the shrink-wrap license announcement thereby revealing the news obscured by the seal that even though Leopard had shipped, they hadn't installed in my new machine so I get to do the upgrade myself.<br /><br />Gee Apple, you know how to show a geek a good time. Now Aunt Tilly who bought the iMac cause she heard it was a good system for a novice, is not going to be quite so thrilled. (-0 for Apple 'cause I already dinged 'em for this)<br /><br />Reminds me of an old joke about a college professor who walked into the classroom and wrote on the board, "CS 150 will be meeting here rather than in Foster Hall room 35."<br /><br />Anyway, I followed instructions.<br /><br />The first thing I found out was that the Leopard update couldn't find my wireless mouse and keyboard. I had to boot back into Tiger, start the boot process, then quick turn off the mouse and keyboard so I could turn them back on when prompted to.<br /><br />---oookay----<br /><br />[Here passeth two hours.]<br /><br />Finally the upgrade completes and automatically reboots my system. Wow, look there's the Leopard star-field background. I'm almost getting excited. Oops. Can't find the mouse. Turn off the mouse; turn off the keyboard; turn off the computer; turn on the computer; turn on the mouse when prompted; turn on the keyboard when prompted. Aha. It's up and asking me for my password.<br /><br />So I entered the password. The screen goes pale blue. It's not nearly as attractive as the BSOD, but it will do. Then the Leopard background reappears and look there's a password prompt. Er...<br /><br />I repeated the cycle many times just to be sure that I was really trapped in a loop. I was even able to verify that I was entering the password correctly because when I intentionally got it wrong, the dialog box did this cute little shake (I could almost hear it saying tsk, tsk..) and didn't cycle through the BSOF (that's "Frustration" rather than "Death") -10 for Apple.<br /><br />Of course I tried variations on the theme, but it always ended back at the BSOF cycle. A visit to <a href="http://apple.com">apple's web site</a>(via my windows machine) yielded a couple of ideas [Look the "insert link" worked in Firefox on Windows -- take *that* safari]<br /><br />When last seen, I held the 'C' down while restarting. This booted from the CD which started the upgrade process all over. I waited until it estimated 2 hours and 15 minutes, then headed for work where I'm entering this blog entry from a Windows machine using Firefox. <br /><br />If that doesn't work the next step is to boot into single user mode and try to fix things from the command prompt. <br /><br />Is Aunt Tilly having fun, yet?Dale Wilsonhttp://www.blogger.com/profile/06832140112844486034noreply@blogger.com1tag:blogger.com,1999:blog-9945363.post-31335888870140100072007-11-01T22:46:00.000-07:002007-11-01T23:51:36.097-07:00An out-of-box experienceMy iMac arrived today. The box says it's a 24 inch, widescreen computer. When did we start selling computers by the inch?<br /><br />In fact, I'm typing this blog entry in using it. When I told my macophile friends at work that I'd finally succumed to the lure-of-Apple, one of them asked me to report my experieces as a long time user of many other systems.<br /><br />It seems like a worthy blog topic.<br /><br />To capture the essence of my very first exposure to Mac: Mixed bordering on unpleasant.<br /><br />[Sorry macomaniacs, but it's true.]<br /><br />Issues (so far).<br /><br />The first shock was the keyboard. This is one's my own fault. <br /><br />When I was shopping I looked at the iMac at the Apple store where, of course they had wired keyboards. I am rather opinionated about my keyboards -- I use my own Das Keyboard (I would make this a hot-link to http://www.daskeyboard.com/ but blogger doesn't recognize control-shift-a when I type it on the Apple keyboard -- strke n+1<br />) (Thanks to Peter and Avani for gifting me the Das Keyboard.)<br /><br />Where was I. O yeah. I tried the new mac keyboard in the store -- it wasn't great, but it was acceptable. However, I didn't want to be tethered -- I wanted a wireless keyboard and mouse, <br /><br />When I told the guy at the Apple store that I wanted the wireless keyboard and mouse, he said that made it a custom system so they couldn't sell it at the store. I'd have to order it thru the web site (which I did.) -1 Apple for an annoy-your-customers-when-they're-ready-to-buy policy<br /><br />Silly me, I assumed the wireless keyboard would be just the Mac keyboard with the cord chopped off. Wrong. Not only did they chop the cord off, they also chopped of half the keyboard -- in particular the number pad and navigation keys disappeard leaing only a whimpy set of arrow keys tucked down in the corner. To be fair, if I use this keyboard for a while I may like having the arrows nestled under my right pinkie, but I'll still miss the num pad, home, pgUp, and friends.<br /><br />-1 Apple for producing a brain-dead keyboard. -1 to me for not shopping better.<br /><br />A word here about the enclosed documentation. I think Apple got this one right. There was enough information written in clear English with illustrations. Other languages were available, too. No making up your own iconographic language to achieve equal-opportunity miscommunication. +1 for Apple<br /><br />So I turned it on. It figured out that there was no mouse attached and showed me a picture of the wireless mouse with a clear animation showing the mouse's power switch being turned on. I obeyed, and it synched-up nicely with the bluetooth mighty mouse. +1 for Apple<br /><br />Likewise it automatically detected the keybaord. Another +1.<br /><br />It found my wireless network and painlessly established the connection. A definate +3. In fact I'm on line now (obviously!) without answering any questions about the wireless network except which of the networks it found I wanted to use. <br /><br />It asked for my Apple ID & password for the account I had to set up to place the order at the Apple store. It used that to retrieve my name, address, etc (a nice touch), and went so far as to propose a user ID for me on this machine based on my name -- letting me override it, of course, if I wished to. Ok that gets yet another +1.<br /><br />Then it started playing 20 questions. In particular it want to know my phone number -- which I hadn't supplied to the Apple store and didn't want to supply now! I'm not that kind of guy -- I don't give out my phone number to just anyone! (I HATE HATE HAtE phone spam -- and no, I don't trust Apple.) It insisted! It would not let me use the computer unless I told it my phone number. You had to enter a phone number that conformed to US phone number conventions to proceed (ok--they already knew I lived in the US. I assume the validation was locale specific.)<br /><br />Gee, I sure hope they don't try to call me at 911-555-5555 <evil grin> You meet interesting people that way.<br /><br />-5 for Apple, +3 for me.<br /><br />And then it wanted to take my picture. It didn't explain why, but it wouldn't proceed until I let it. Now I'm feeling testy. -5 Apple; -2 for me (I'm not looking my best in the middle of the night when I did this!)<br /><br />Next it produce a list of software for which upgrades were available. After asking nicely for my permission it download the upgrades, installed them and asked me to let it reboot. +2 for this seamless process.<br /><br />Finally, we made it thru that and I ran into the biggest shock. I waited for Leopard to come out before I bought the iMac, but after surviving the install process I discovered I was now running on Tiger. Grrrrrrr <pun intended> -10 for Apple<br /><br />There's a Leopard CD in the box (no Tiget CD), but I thought by going directly to Apple to buy the machine I would bypass the need to upgrade immediately. -3 for Apple. Other than including the CD, there's nothing that lets me know that I should upgrade. If I was a typical novice computer user I might well run Tiger for a long time before discovering that THEY PUT ThE WRONG OS ON THE MACHINE. -3 for Apple.<br /><br />So that's the story so far. I'm still on Tiger, but Safari is working (oops -- I just tried to spell check this document using the blogger built-in spell checker and ended up with a massive javascript error message. -1 for Apple <sigh> The cool thing is the javascript error message contains a list of my misspelled words. +1 for me ;-)<br /><br />(and where's the @#$%^&*( delete key!)<br /><br />DaleDale Wilsonhttp://www.blogger.com/profile/06832140112844486034noreply@blogger.com5tag:blogger.com,1999:blog-9945363.post-32533166843069988292007-08-07T07:51:00.000-07:002007-08-07T08:47:02.799-07:00Obvious IconsThere's a theory that an icon is more recognizable than a word. <humor> That explains why you have to have tool tips so you can hover the mouse over the icon to find out what it does</humor>.<br /><br />How many programs let you hover over a word to generate a little picture of what the word means?[1]<br /><br />What brought this to mind was 3.5" floppy disk icon that means "save this file" in a lot of windows programs. How long do you think it will be before new computer users have no idea what a 3.5" disk used to look like even though they know that a bluish square with a whitish rectangle on it means "save this file?" <br /><br />It is said that icons are more locale-independent than English words. I think we're just inventing a new pictographic [2] language.<br /><br />----------------<br />[1] ok, gmail shows you the person's picture when you hover over their name.<br />[2]The first time I posted this, I spell checked it and absentmindedly let it "correct" pictographic to photographic. I guess I should have just used an icon ;-)Dale Wilsonhttp://www.blogger.com/profile/06832140112844486034noreply@blogger.com1tag:blogger.com,1999:blog-9945363.post-48585265574857446042007-07-02T08:42:00.000-07:002007-07-02T08:47:04.713-07:00If I wrote a mail client....If I wrote a email client, it would do a sanity check on dates before sending messages. I had the date time set wrong on my machine recently and sent a message out on July 18th (it's presently July 2nd) Now everyone who received that message is going to be annoyed by having it sort to the top (or bottom) of their inbox until July 18th actually rolls around (unless, of course they actually keep a clean inbox ;-) )<br /><br />And while I'm at it, my mail client would scan the text for the words "attachment," "attached," "attaching," etc. If any of these appeared but there was no attachment, my client would prompt me: "Did you forget to attach a file?"Dale Wilsonhttp://www.blogger.com/profile/06832140112844486034noreply@blogger.com0tag:blogger.com,1999:blog-9945363.post-29889804674874068682007-06-29T08:30:00.000-07:002007-06-29T08:34:23.629-07:00OK, this brought back some memories:<br /><br /><a href="http://www.wired.com/culture/art/news/2007/07/IBM1401_Musical">http://www.wired.com/culture/art/news/2007/07/IBM1401_Musical</a><br /><br />That's why there was an AM radio sitting on top of the CPU box on the 1401 at KU.<br /><br />One could also play music on the chain printer. The chain spun at a constant speed and a hammer hit the character as it came by, so by modifying the sequence of characters to be printed....Dale Wilsonhttp://www.blogger.com/profile/06832140112844486034noreply@blogger.com0tag:blogger.com,1999:blog-9945363.post-60214372375882262892007-06-13T15:18:00.000-07:002007-06-18T14:44:25.669-07:00Program Language Hamming SpaceA couple of computer language related events came together in the last few days to form a train of thought.<br /><br />One was reading <a href="http://justin-michel.spaces.live.com/blog/">Justin's blog</a> discussing strongly typed vs loosely typed variables with side trips into compiler-detected/assigned types for variables. Along the way Justin also discussed case sensitive languages (he's ag'in' 'em)<br /><br />Another was a question Rich (who doesn't have a blog) asked me about a Python program.<br /><br />The Python programmer, who obviously came from a C++ or Java background had was hoping for some function overloading. To paraphrase:<br /><br /><span style="font-family:courier new;"></span><blockquote><span style="font-family:courier new;"></span></blockquote><span style="font-family:courier new;"></span><pre><br /> class Widget:<br /> def tweak(filbert):<br /> print "tweak with one argument\n"<br /><br /> def tweak(pecan, almond):<br /> print "tweak with two arguments\n"<br /></pre><br /><br />It didn't work. That makes sense because Python is a duck typed language that gives you incredibly flexible argument passing support so you really can't overload functions based on signature.<br /><br />What concerned Rich is that it not only didn't work, it also didn't generate a "compiler" error. Python just blithely and silently replaced the first definition of tweak with the second one.<br /><br />My first response to Rich was to explain why Python worked the way it did. That's fine, but a better discussion might be how can Python be improved to allow this type of error to be detected while not eliminating the possibility of replacing an class's methods at runtime.<br /><br />Of course you might think (with some merit) that replacing an class's methods at runtime is an evil, bad, nasty, cruel thing to do and should be forbidden altogether, but I disagree. There are occasions, in which it's the right thing to do. One example is my "Aspect Oriented Python" demonstration program that I include in my "Power Python" talk. It shouldn't be impossible to replace a method; it should just be a little bit harder than it was in Rich's example: maybe a "redef" keyword.<br /><br />But this is not about Python (or VB). It's about a programming language's Hamming space.<br /><br />Hamming space, and <a href="http://en.wikipedia.org/wiki/Hamming_distance">Hamming distance</a> is a concept that helps design error detecting and correcting codes. The goal is to detect and/or correct mistakes as long as they aren't too bad. A typical encoding scheme can correct any single bit error and detect all double bit errors. Errors involving three or more bits lead to unpredictable results -- the encoded value just might be "corrected" into to the wrong value.<br /><br />Now consider the difference between a valid and correct program and a program that is wrong. One could imagine a programming language in which any statement containing a single error could be corrected and any statement containing more than one error could be detected as being wrong. You might notice, however, that I'm not defining "error" here. In a case sensitive language, an error might consist of a single character of the wrong case (widget when I meant Widget) or we might consider "case mismatch" to be a single error no matter how many letters are involved (newwidget when I mean NewWidget). Or how about this error: int pi = 3.14159;<br /><br />I'm not advocating error correcting languages (Justin is!). I'd just as soon correct my own mistakes, thank you, but I would like a language that could detect many more of my errors than today's languages do. One of the reasons I don't especially like perl is that it has a very low Hamming distance between syntactically valid programs. I can put a perl statement together in all sorts of strange and mysterious ways and perl kindly tries to make sense of it. That's OK as long as it guesses right. What really annoys me, however, is that *you* can put together a perl statement in strange ways and if I want to understand and/or maintain the program I have to out guess the compiler to figure out what's really going on.<br /><br />But this is not about perl. It's about a programming language's Hamming space.<br /><br />One measure of "goodness" for a programming language should be the "distance" between valid programs. Making a minor coding mistake should produce a compiler/interpreter error message. It should not produce a syntactically correct, but semantically flawed running program. The only way to achieve this is to have a larger Hamming distance between syntactically valid programs.<br /><br />Alas, none of the programming languages commonly in use lives up to this standard.<br /><br />At least it's job security for programmers.Dale Wilsonhttp://www.blogger.com/profile/06832140112844486034noreply@blogger.com3tag:blogger.com,1999:blog-9945363.post-1176220846154757432007-04-10T08:55:00.000-07:002007-04-10T09:02:00.380-07:00I've been blog tagged: 5 thingsWeiqi Gao blog tagged me. That means he demanded that I post in my blog five things that most people don't know about me.<br /><br /><span style="font-size:180%;"><span style="font-family:georgia;">Five things you might not know about me.</span></span><br /><br /><span style="color: rgb(51, 51, 255); font-weight: bold;">1) Divorce</span><br /><br />There has been a fairly long gap in this blog. That's because last October my wife Tina announced that she was moving out and she wanted a divorce. It's not final yet -- we're trying to negotiate a settlement before we get the lawyers involved. In the light of this, a lot of things -- like blogs -- seem less important.<br /><br />Thanks Weiqi for breaking me out of the slump.<br /><br /><span style="color: rgb(51, 51, 255); font-weight: bold;">2) Diving</span><br /><br />From the time I was 9, until I graduated from High School, I was a competitive springboard diver. Highlights of my diving career include, Ohio AAU Age Group state champion 12 & under, top twelve (meaning I made the finals, but didn't win) in the Southeast Regional AAU Age Group Championship when I was 15, and a three (out of a possible three) athletic letters at Wichita High School West.<br /><br />I never won the Kansas high school state championship, though, because Tom Pettit who went to Wichita High School North could always beat me. His front 1 1/2 somersault with 2 twists had a degree of difficulty of 2.6 while my best dive, a front 1 somersault with 2 twists was only worth 2.3.<br /><br />For several years I coached the diving team for my local subdivision pool. It was a wonderful experience for me and I got to watch some great kids grow into twenty somethings (so far.)<br /><br />You learn a lot in diving beyond the physical skills. Probably the most important lesson for me was controlled risk taking (or courage if you want to think of it that way.) A lot of time when I was diving I was deeply scared -- I have a very good imagination and I could think of all sorts of ways to get hurt if things went wrong! I still remember watching a friend break most of his toes hitting the board. In fact I hit the board painfully several times myself. Nonetheless when you dive you learn to have confidence in your skills and perform in public in spite of the risks.<br /><br />This doesn't mean all my risk taking was reasonable or controlled My extracurricular diving activities included cliff diving and bridge diving -- probably the less said about these the better. I didn't kill myself.<br /><br /><br /><span style="font-weight: bold; color: rgb(51, 51, 255);">3) White Water Canoing</span><br /><br />Speaking of risk taking. In my late twenties (before the kids were born) my wife and I got involved in whitewater canoing. Have you ever seen the movie Deliverance? It was filmed on the Chattahoochee river in Georgia. I've been down that river in an open canoe. No banjo's, though when I was there.<br /><br /><span style="font-weight: bold; color: rgb(51, 51, 255);">4) Music</span><br /><br />And speaking of banjos...<br /><br />A lot of my friends know I and trying to learn to play the guitar -- I'm not very good, yet. Not many people know that I have been playing harmonica and flute (all sorts) for many years. That's because I mostly play for my own enjoyment. <br /><br />Sometimes I play harmonica in public -- blues, traditional folk, or improvization, but I haven't played recorder (wooden flute) in public since Jeff Dearinger made me join his recorder quartet back in Lawrence, KS. I played tenor in the quartet, but I also own an alto, and a soprano (like the ones they make unsuspecting elementary kids play.)<br /><br />Most mornings I greet the day by playing something on my Native American Flute. I have never played it in public except one time last summer when I went down to Elephant Rocks State Park in southeast Missouri. I hid in the rocks and played for a while. I hope some passers by were mystified and entertained.<br /><br />And recently I acquired a tin whistle -- possibly the most annoying instrument known to mankind when it's in the wrong hands. I like to think that my playing, though not great, yet, is not painful.<br /><br /><br /><span style="color: rgb(51, 51, 255); font-weight: bold;">5) Road Trip</span><br /><br />And finally, back in 1969 I was planning to go to a concert in with a bunch of friends. We didn't have tickets, but we'd heard that you could get in without them. Even if you couldn't get in there would be a great gathering outside the concert. Unfortunately I broke my glasses right before we left. Since I'm pretty useless without glasses (although I managed to dive blind for many years!) I stayed in Lawrence long enough to get a new pair. That's why I didn't make it to Woodstock.Dale Wilsonhttp://www.blogger.com/profile/06832140112844486034noreply@blogger.com1tag:blogger.com,1999:blog-9945363.post-1148425597568770882006-05-23T16:04:00.000-07:002006-05-24T14:19:17.100-07:00The Software Crisis Solved (yet again)Thirty five years ago someone offered to pay me for writing a computer program. "What a great way to make a living," I thought.<br /><br />When the program was working, I proudly showed the result to my customer. He had the sheer audaucity to check the results by hand. When the numbers didn't add up, I was, shall we say, chagrined.<br /><br />Since that time I have written a lot more programs for money, but I've never forgotten the lesson learned on that first assignment. Writing programs that are not only "done" but are also demonstratably correct has been an important goal in my professional life.<br /><br />Thus when I see the teaser "How to Prevent Software Disasters" on the front of the June 2006 "Scientific American," and an article "Dependable Software by Design" by Daniel Jackson, you can be sure it had my attention.<br /><br />After the usual scare stories -- in this case the ones involving the Denver Airport, the IRS, the FBI, and the FAA -- along with cautionary scenarios of out-of-control computers running planes, trains, and automobiles, financial markets, and machine tools (the author forgot medical equipment, oh, never mind,that showed up in a sidebar a couple of pages later) the article got down to business.<br /><br />My first warning that this was going to be yet another silver-bullet article came when I read, "Almost all grave software problems can be traced to conceptual mistakes made before programming started."<br /><br />Um, excuse me, out here in the field it turns out that the overwhelming majority of mistakes have to do with the little fiddly bits, not the grand scheme of things. For every Denver Airport Baggage system that couldn't possibly work due to faulty analysis of the problem to be solved, there are hundreds or thousands of "little problems" like the Mars Observer missing the planet because someone used the wrong measurement system, or the software viruses that were able to cripple the internet because some programmer neglected to include a simple check for the length of an incoming message.<br /><br />"Ok," I thought. "We'll ignore that one and move on. There's often a pony to be found in this type of article." And then I came to the statement: "The idea is to simulate every state that the software can take to determine that none leads to a failure." I must admit I broke out laughing at that point.<br /><br />Consider the climate control system in my car. It's not very complicated, A rough estimate of the "possible states" for the heater and air conditioning controls on the dashboard is around 2000. The estimate is rough because the temperature control is analog so I'm estimating about ten possible settings. Factoring in the radio gets the number of possible states for the dashboard controls well into the millions, and we haven't even handled half of the dashboard. I daresay generating all possible states for my single automobile would take modern computers (all of them!) more time than has passed since the automobile was invented.<br /><br />Yes, it turns out the author is aware of the state explosion problem, and he didn't really mean "simulate every state..." He really meant "use some heuristics to search for possible pathological states..." Apparently those states that are identified by the developer as being pathological (unreachable files in his example.) Once again, referring to my experience in "the real world" it's not the pathological cases that you can think up ahead of time that get you. It's the ones that come as a surprise. "Gosh, I never imagined that a trader would enter the price in the quantity field and vice versa. There goes the company."<br /><br />Simulating a model is just another form of testing in which a negative result (we found a failure case) is important and significant, but a positive result (no problems found here!) can be used, along with $5.00 to buy a fancy cup of coffee at Starbucks. The authors are describing an automated testing tool that is driven from a model of the program behavior.<br /><br />To make this system work, the author has invented a new language to describe the design of the program, Alloy. Presumably Alloy expresses the design at a higher more abstract level than the typical languages (Java, Ruby, C++, Python, ...) used by programmers, and therefore allows a higher level of validation (aka testing) to be applied to the design. The question not answered by the author is what relationship exists (if any) between the Alloy model of the program design and the actual program as executed by the computer?<br /><br />Some possibilities include automatically generating the "low level" computer code from the high level model, automatically verifying the correspondence between the model and the actual implementation, or depending on the intellegence and understanding of the software analysts and developers involved in the project to be sure that the relationship holds as expected. Each of these possibilities has corresponding problems.<br /><br />If the actual code is auto-generated what you have is an executable model. In fact you've invented a new programming language. Now rather than programming in Java, the software developers will be programming in Alloy. This is not necessarily a bad thing, but it means the Alloy modeling language needs to be rich enough to address all the issues that software developers must worry about when writing programs. And, just as mistakes are often made by programmers writing in Java or Python, mistakes will be made in Alloy. If it turns out that Alloy is a good language and can hide more of the complexity of programming that can traditional languages, then progammers will make fewer mistakes. However if Alloy is not seen as a programming language and analyzed as such, we'll never know how expressive and safe it is.<br /><br />Suppose we try the approach of automatically verifying the equivalence between the Alloy model and the "real" program. Once again Alloy is a programming language, but this time it does not need to handle all the low level details. They can be addressed in the "actual" programming language (goodness, I hope the programmer gets the details right.) All that needs to happen is that the high level abstraction expressed in Alloy must be compared to the abstraction expressed in Java or Python. To put this another way, we need a tool that can examine a Java program and understand what it is supposed to do.<br /><br />Making this comparison is an interesting, and quite difficult problem to solve. One wonders, we had a way to extract the higher level meaning of a program from the lower level C# or VB or Java source, why we can't just validate this representation of what the program means directly using the same techniques that are used to validate an Alloy program (er... model.)<br /><br />This does, however, raise and interesting possibility. Rather than inventing a new higher level language in which to express the program, maybe it would be more productive to have two teams writing the same program and an automated comparison of the results of the two team's work. Surely it is easier to compare two C++ programs to each other than it is to compare a program written in an abstract modelling language to one written in a low level implementation language.<br /><br />As an aside, let me mention the stiff resistance such a plan will encounter in the "real world" of managers and business people who already think that it takes too long to develop software so don't even think of using two people to do one job! I happen to know this because many developers would like to spend half their time writing code that tests to be sure the other half of our code behaves the way we expect it to. We call it "unit testing" and it's mighty difficult to get the funding to do it from some short sighted organizations.<br /><br />The remaining approach for verifying the correspondence between the Alloy model and the actual code is to depend on the native intelligence and experience of the software developers. This is a good thing to the extent that it helps the developers involved analyze and understand the program at a higher level than the one at which they normally work. In fact I speculate that a great deal of the benefit claimed for Alloy comes because of just this effect. In order to create an Alloy model the developers must come to a deeper understanding of the basic design of the program, and in doing so, they end up creating a better system.<br /><br />So, I congratulate the author on addressing a difficult topic and bringing some new ideas to the table (or at least some interesting new variants of the old ideas that have been around for a while) but I do wish he had done a better job of understanding the meaning of this work. I cringe when he suggests that "governments may even establish inspection and licensing regulations that enforce high-quality program construction techniques" implying that modeling the program in Alloy is such a good thing that it should be imposed by fiat.Dale Wilsonhttp://www.blogger.com/profile/06832140112844486034noreply@blogger.com1tag:blogger.com,1999:blog-9945363.post-1136483309806242282006-01-05T09:33:00.000-08:002006-01-05T09:48:29.820-08:00Functional Programming Observation Continues...A follow-up to <a href="http://http://wilsond.blogspot.com/2005/12/observation-concerning-functional.html">my previous post on functional programming in C++</a>.<br />Compare <pre><br /> size_t total = 0;<br /> for(size_t i = 0; i < elements_size(), ++i)<br /> total += elements_[i].bytes();<br /></pre>to<pre><br /> size_t total = 0;<br /> std::for_each(elements_.begin(), elements_.end(), count_bytes(total));<br /> return total;<br /></pre><br />Neither one is "right." <br /><br />What I want is to hide the details of iterating through a collection while revealing what is actually being done. In fact, I want to say:<pre><br /> size_t total = 0;<br /> for (each element in elements_)<br /> total += element.bytes();<br /></pre>(Python anyone?)<br /><br />The "for-loop" approach fails on the "hide the iteration" criteria [although I've been using for loops for so long that <span style="font-family: courier new; font-weight: bold;">for(size_t i = 0; i < elements_size(), ++i)</span> is a single conceptual chunk for me.]<br /><br />The "for-each" approach also fails to hide the iteration {although familiarity with STL makes <span style="font-family: courier new; font-weight: bold;">begin() ... end()</span> into a single conceptual chunk, also.]<br /><br />The "for_each" approach also hides what's actually being done -- it depends on a descriptive name (count_bytes is not bad) to provide a hint.<br /><br />The "for-loop" approach shows the actual work (which is good, but clutters it up with a leftover detail from the indexing process ("elements_[i]" rather than simply "element")<br /><br />I wonder if there's some way to convince the compiler to recognize:<pre><br /> size_t total = 0;<br /> while(each(element, elements_))<br /> total += element.bytes();<br /></pre>for any arbitrary collection (elements_) of data type (element). This would of course require that the collection obey STL rules.Dale Wilsonhttp://www.blogger.com/profile/06832140112844486034noreply@blogger.com3tag:blogger.com,1999:blog-9945363.post-1135897622344476612005-12-29T14:47:00.000-08:002006-01-05T05:53:37.176-08:00An observation concerning functional programming in C++The following code was extracted from a live program.<br /><pre><br /> namespace<br /> {<br /> class count_bytes<br /> {<br /> public:<br /> count_bytes(size_t& total)<br /> : total_(total)<br /> {<br /> }<br /> void operator()(<br /> const Element& element<br /> )<br /> {<br /> total_ += element.bytes();<br /> }<br /> private:<br /> size_t& total_;<br /> };<br /> }<br /> size_t<br /> ElementSet::bytes() const<br /> {<br /> size_t total = 0;<br /> std::for_each(elements_.begin(), elements_.end(), count_bytes(total));<br /> return total;<br /> }<br /></pre><br />By my count that's 26 lines of code. To be fair I should admit that I tightened it up a bit. The original was well over 30 lines.<br /><br />Compare that to:<br /><pre><br /> size_t<br /> ElementSet::bytes() const<br /> {<br /> size_t total = 0;<br /> for(size_t i = 0; i < elements_.size; ++i)<br /> {<br /> total += elements_[i].bytes();<br /> }<br /> return total;<br /> }<br /></pre><br /><br />Ten lines (And yes, you could use an iterator rather than an index.)<br /><br />Functional Programming Motto: You may have to type a whole lot more, but at least your code will be harder to understand.<br /><br />Actually functional programming is just fine when you need it. The problem happens when it's the "Wrong Tool For The Job[TM]." There seems to be a lot of that goin' 'round [sigh].Dale Wilsonhttp://www.blogger.com/profile/06832140112844486034noreply@blogger.com7