I wrote a program to download a few thousand web pages containing
information I needed, parse the pages for that information, and store
it in an ODBC database for later querying, data mining, etc.
The information on the pages had to be cross referenced with existing
database tables to ensure validity of data, making sure foreign keys
with code tables, dates, etc matched. I took this as a good opportunity
to use Dylan restarts and exceptions.
In the program I made sure exceptions were thrown whenever data from
the web pages did not match expected information in the database. For
each exception I created a restart protocol and made sure I had restart
handlers to allow correcting of the exceptions. Some of the exceptions
could be programatically fixed - so I had handlers that did this. An
example was where a lookup on a person's name failed. I had a handler
that tried soundex checking on the name to find a match and retry if
there was one with the new name.
There were a few cases where I couldn't have the program handle it or I
didn't think it worth it, so I specialised 'restart-query' on the
restart class to prompt for the correct information and made sure I had
a restart handler where the exception was signalled.
The program had been running for 10 hours and working fine. The
database was being populated and the automatic correction of exceptions
via the handlers worked well. Then up popped the Dylan exception error
message (in Functional Developer) saying that an exception had occurred
- no such record existed for a given date. Damn.
But in the list of available restarts in the Functional Developer debug
box, was the ability to choose a restart handler from those I'd made
available. Nicely printed in the list was the restart handlers I had in
- Enter data for missing record.
- Enter new date for current record.
I chose 'enter data for missing record', and my 'restart-query' method
was called prompting me to manually enter the missing data. I did this
and the program continued, completing two hours later.
The ability to create restart protocols and be able to choose them at
exception time, and prompt the user for correct values saved me *heaps*
of time. The ease of creating the restart mechanisms was a pleasant
Chris Double <ch...@double.co.nz> wrote in message
> How about a quickie example of code to illustrate your technique?
Here is a quick example. One of the items on the web page is a city
name. I need to look up the database using the name and get and ID
number for it. Sometimes the city name is mis-spelled on the web page
and so the ID is not found. It was a console program so the following
using console input/output. This was typed in and cut down from the
original so may contain syntax errors but it shows the general idea of
what I used.
// Exception and restart protocols
// Any method that signals the <no-city-id-found> exception
// can be recovered from using two restart protocols.
// A handler for <no-city-found> can signal <use-city-id-restart>
// to return a specific ID to the method that tried to find the
// ID originally.
// Alternatively, <try-city-name-restart> can be signalled to
// retry the method call using a different name (different spelling
// for example).
define class <no-city-id-found> (<error>)
slot error-city-name :: <string>, init-keyword: name:;
define class <use-city-id-restart> (<restart>)
slot restart-city-id :: <integer>, init-keyword: id:;
define method restart-query( restart :: <use-city-id-restart>)
format-out("Enter city id to use: \n");
let id-as-string = read-line(*standard-input*);
restart.restart-city-id := string-to-integer(id-as-string);
end method restart-query;
define class <try-city-name-restart> (<restart>)
slot restart-city-name :: <string>, init-keyword: name:;
define method restart-query( restart :: <try-city-name-restart>)
format-out("Enter city name to use: \n");
let name = read-line(*standard-input*);
restart.restart-city-name := name;
end method restart-query;
// Method used to return an ID given a city name
define method get-city-id(name)
// Provides the action to perform if a restart provides
// an explicit ID to use.
let handler (<use-city-id-restart>
format-string: "Enter city id for %s.",
format-arguments: vector(name))) =
// A restart handler that tries again using a different
// city name (perhaps there was a mis-spelling with the
// original attempt).
let handler (<use-city-name-restart>
format-string: "Enter city name for %s.",
format-arguments: vector(name))) =
// Tries method again with new name
// Retrieve an id given the name using sql-odbc
let query = format-to-string(
"select city_id from cities where city_name='%s'",
let result = execute(query);
if(result & result.size > 0)
first(first(result)); // retrieves id from result rows
error(make(<no-city-id-found>, name: name));
// Test caller for above method - one error results
// allowing the user to manually restart (see text at end).
define method foo()
get-city-id("Auckand"); // *** (A) ***
// Call get-city-id, but have an exception handler
// to automatically restart.
define method bar()
let handler (<no-city-id-found>) =
// Oops, no city - get an alternative spelling.
// by replacing all 'Mt' with 'Mt.'.
let original-name = condition.error-city-name;
let new-name = replace-string(original-name, "Mt ", "Mt. ");
if(new-name ~= original-name)
// Cause the restart with the new name.
error(make(<use-city-name-restart>, name: new-name));
// Sometimes in the original web pages as Mt. Cook, Mt Cook, etc.
// The below is not found, the exception is caught by the above
// handler, which causes a restart to try with the alternative
// name. This succeeds and the program continues. If that fails,
// then the exception is raised again, caught by the handler,
// it sees that there is no new variation of the string to try
// and passes to the next handler. The next handler (as there is
// none) passes to the FD exception dialog as per foo() example.
In the foo() method (*** (A) *** above) the get-city-id fails because
the name is spelt wrong. There is no local handler in scope so
Function Developer throws up an dialog box explaining the error and
displaying the possible restarts. These include the descriptions
placed in the 'let handler' in the original method:
- Enter city id for Auckand
- Enter city name for Auckand
The user can choose either restart and the restart-query method for
the restart gets called, prompting for either the correct ID or an
alternative name to try.
Hope that helps explain it a bit. It's my first real foray into
exceptions and restarts so I'd be interested in other approaches. The
thing I like about the above is if I don't handle an exception, the
program can still be restarted/continued from the FD final exception