develooper Front page | perl.perl6.language | Postings from September 2005

Exceptuations

Thread Next
From:
Yuval Kogman
Date:
September 25, 2005 07:28
Subject:
Exceptuations
Message ID:
20050925142818.GT5797@woobling.org
Hi,

Suppose I'm writing a file browser, with a pane on the left to
display the filesystem hierarchy, and a frame on the right to
preview the file.

Suppose I have a convenience function, preview_file which takes a
path and returns a value that the frame display view knows to
render.

Let's define this for HTML files, where the desired preview is a
summary of the text:

	use Text::Summarize;
	use HTML::ToAscii;

	multi sub preview_file ($filename where /\.html$/ ) {
		my $handle = open($filename, :r); # might fail if $filename is unreadable
		return summarize(html2ascii(=$handle)); # might fail if HTML is invalid
	}

And this code is called when the user clicks on a file in the pane:

	class NiftyUI {
		use fatal;

		method handl_some_click ($file) {
			$.right_frame.display(preview_file($file.name));
		}

		method handle_event ($event) {
			$?SELF.dispatch_event($event);

			CATCH {
				when NiftyBackend::Exception {
					$?SELF.display_error_box($!);
				}

				default { die $! };
			}
		}
	}

With the current shape of the code if any of the possible failures
in the backend code happen, they are reported in a message dialog.

Now, let's say we would like to add a feature, that lets the user
change the mode of the file if it's unreadable.

Several approaches to doing this:

* give the backend an abstract object, a Frontend of sorts:

	$frontend.ask_user("do you want to make the file readable?")

* throw internal exceptions, and let the frontend handle the
exception and retry the action:

	method handle_some_click ($file) {
		$.right_frame.display(preview_file($file.name));

		CATCH {
			when IO::Errors::PERMISSION_DENIED {
				if ($?SELF.ask_user_to_chmod_file($file)) {
					make_readable($file);
					$?SELF.handle_some_click($file); # retry the event
				} else { die $! }
			}
		}
	}

I propose a new model - each exception has a continuation that
allows it to be unfatalized.

The exception object has info on whether the fatality of the
exception was due to a die, or a use fatal, and lets the exception
handler decide accordingly.

Then we have code that looks like this:

	method handle_some_click ($file) {
		$.right_frame.display(preview_file($file.name));

		CATCH {
			when IO::Errors::PERMISSION_DENIED {
				if ($?SELF.ask_user_to_chmod_file($file)) {
					make_readable($file);
					$!.continue(open($file, :r)); # the return value of the failed open is
					# overridden by the return value of this (hopefully successful) open.
				} else { die $! }
			}
			when HTML::ToAscii::Exception { # badly formed
				$!.continue("The HTML file contained a syntax error");
				# this string is passed to summarize instead of the undef exception object
			}
		}
	}

The value this has is in handler cascading... If, for example,
make_readdable($file) failed too, then an exception handler around
handle_some_click could handle display a sudo box, to try it again,
and eventually continue back into the make_readable($file) call,
which will then continue into the failed open.

-- 
 ()  Yuval Kogman <nothingmuch@woobling.org> 0xEBD27418  perl hacker &
 /\  kung foo master: /me tips over a cow: neeyah!!!!!!!!!!!!!!!!!!!!!!


Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About