File: tut_exceptions.xml

package info (click to toggle)
rubybook 0.2-2
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 4,252 kB
  • ctags: 1,043
  • sloc: xml: 60,486; makefile: 25
file content (562 lines) | stat: -rw-r--r-- 20,275 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
<ppdoc>
<copyright>
    Copyright (c) 2001 by Addison Wesley Longman.  This
    material may be distributed only subject to the terms and
    conditions set forth in the Open Publication License, v1.0 or
    later (the latest version is presently available at
    http://www.opencontent.org/openpub/).
</copyright>
<chapter name="Exceptions, Catch, and Throw">
<p/>
So far we're been developing code in Pleasantville, a wonderful
place where nothing ever, ever goes wrong.
Every library call
succeeds, users never enter incorrect data, and resources are
plentiful and cheap.  Well, that's about to change. Welcome to the
real world!
<p/>
In the real world, errors happen. Good programs (and programmers)
anticipate them and arrange to handle them gracefully. This isn't
always as easy as it might be. Often the code that detects an error
does not have the context to know what to do about it. For example,
attempting to open a file that doesn't exist is acceptable in some
circumstances and is a fatal error at other times. What's your
file-handling module to do?
<p/>
The traditional approach is to use return codes. The <meth>open</meth>
method returns some specific value to say it failed. This value
is then propagated back through the layers of calling routines
until someone wants to take responsibility for it.
<p/>
The problem with this approach is that managing all these error codes
can be a pain. If a function calls <meth>open</meth>, then <meth>read</meth>,
and finally <meth>close</meth>, and each can return an error indication, how
can the function distinguish these error codes in the value it returns
to <em>its</em> caller?
<p/>
To a large extent, exceptions solve this problem. Exceptions let you
package up information about an error into an object. That exception
object is then propagated back up the calling stack automatically
until the runtime system finds code that explicitly declares that it
knows how to handle that type of exception.
<section>The Exception Class</section>
<p/>
The package that contains the information about an exception is an
object of class <classname>Exception</classname>, or one of class <classname>Exception</classname>'s
children. Ruby predefines a tidy hierarchy of exceptions, shown in
Figure 8.1 on page 93. As we'll see later, this hierarchy
makes handling exceptions considerably easier.
<p/>
<figure type="figure">Figure not available...</figure>
<p/>
When you need to raise an exception, you can use one of the built-in
<classname>Exception</classname> classes, or you can create one of your own. If you
create your own, you might want to make it a subclass of
<exception>StandardError</exception> or one of its children. If you don't, your exception
won't be caught by default.
<p/>
Every <classname>Exception</classname> has associated with it a message string and a
stack backtrace. If you define your own exceptions, you can add
additional information.
<section>Handling Exceptions</section>
<p/>
Our jukebox downloads songs from the Internet using a TCP socket. The
basic code is simple:
<p/>
<codefragment>
<alltt><fullcode><![CDATA[  opFile = File.open(opName, "w")
  while data = socket.read(512)
    opFile.write(data)
  end
]]></fullcode>
opFile<nbsp/>=<nbsp/>File.open(opName,<nbsp/>"w")
while<nbsp/>data<nbsp/>=<nbsp/>socket.read(512)
<nbsp/><nbsp/>opFile.write(data)
end
</alltt>
</codefragment>
<p/>
What happens if we get a fatal error halfway through the download? We
certainly don't want to store an incomplete song in the song list.
``I Did It My *click*''.
<p/>
Let's add some exception handling code and see how it helps.
We
enclose the code that could raise an exception in a
<kw>begin</kw>/<kw>end</kw> block and use <kw>rescue</kw> clauses to tell Ruby the
types of exceptions we want to handle. In this case we're interested
in trapping <classname>SystemCallError</classname> exceptions (and, by implication, any
exceptions that are subclasses of <classname>SystemCallError</classname>), so that's what
appears on the <kw>rescue</kw> line.  In the error handling block, we
report the error, close and delete the output file, and then reraise
the exception.
<p/>
<codefragment>
<alltt><fullcode><![CDATA[!-  opName = "/tmp/testfile"
!-  socket = $stdin
  opFile = File.open(opName, "w")
  begin
    # Exceptions raised by this code will
    # be caught by the following rescue clause
    while data = socket.read(512)
      opFile.write(data)
    end

  rescue SystemCallError
    $stderr.print "IO failed: " + $!
    opFile.close
    File.delete(opName)
    raise
  end
]]></fullcode>
opFile<nbsp/>=<nbsp/>File.open(opName,<nbsp/>"w")
begin
<nbsp/><nbsp/>#<nbsp/>Exceptions<nbsp/>raised<nbsp/>by<nbsp/>this<nbsp/>code<nbsp/>will
<nbsp/><nbsp/>#<nbsp/>be<nbsp/>caught<nbsp/>by<nbsp/>the<nbsp/>following<nbsp/>rescue<nbsp/>clause
<nbsp/><nbsp/>while<nbsp/>data<nbsp/>=<nbsp/>socket.read(512)
<nbsp/><nbsp/><nbsp/><nbsp/>opFile.write(data)
<nbsp/><nbsp/>end
<p/>
rescue<nbsp/>SystemCallError
<nbsp/><nbsp/>$stderr.print<nbsp/>"IO<nbsp/>failed:<nbsp/>"<nbsp/>+<nbsp/>$!
<nbsp/><nbsp/>opFile.close
<nbsp/><nbsp/>File.delete(opName)
<nbsp/><nbsp/>raise
end
</alltt>
</codefragment>
<p/>
When an exception is raised, and independent of any subsequent
exception handling, Ruby places a reference to the <exception>Exception</exception>
object associated with the exception in the global variable <var>$!</var>
(the exclamation point presumably mirroring our surprise that any of
<em>our</em> code could cause errors). In the previous example, we used
this variable to format our error message.
<p/>
After closing and deleting the file, we call <meth>raise</meth> with no
parameters, which reraises the exception in <var>$!</var>. This is a
useful technique, as it allows you to write code that filters
exceptions, passing on those you can't handle to higher levels.  It's
almost like implementing an inheritance hierarchy for error
processing.
<p/>
You can have multiple <kw>rescue</kw> clauses in a <kw>begin</kw> block, and
each <kw>rescue</kw> clause can specify multiple exceptions to catch.  At
the end of each rescue clause you can give Ruby the name of a local
variable to receive the matched exception. Many people find this more
readable than using <var>$!</var> all over the place.
<p/>
<codefragment>
<alltt><fullcode><![CDATA[!-string = "1"
  begin
    eval string
  rescue SyntaxError, NameError => boom
    print "String doesn't compile: " + boom
  rescue StandardError => bang
    print "Error running script: " + bang
  end
]]></fullcode>
begin
<nbsp/><nbsp/>eval<nbsp/>string
rescue<nbsp/>SyntaxError,<nbsp/>NameError<nbsp/>=&gt;<nbsp/>boom
<nbsp/><nbsp/>print<nbsp/>"String<nbsp/>doesn't<nbsp/>compile:<nbsp/>"<nbsp/>+<nbsp/>boom
rescue<nbsp/>StandardError<nbsp/>=&gt;<nbsp/>bang
<nbsp/><nbsp/>print<nbsp/>"Error<nbsp/>running<nbsp/>script:<nbsp/>"<nbsp/>+<nbsp/>bang
end
</alltt>
</codefragment>
<p/>
How does Ruby decide which rescue clause to execute? It turns out that
the processing is pretty similar to that used by the <kw>case</kw>
statement. For each <kw>rescue</kw> clause in the <kw>begin</kw> block, Ruby
compares the raised exception against each of the parameters in turn.
If the raised exception matches a parameter, Ruby executes the body of
the <kw>rescue</kw> and stops looking. The match is made using
<tt>$!.kind_of?(<em>parameter</em>)</tt>, and so will succeed if the parameter
has the same class as the exception or is an ancestor of the
exception.  If you write a <kw>rescue</kw> clause with no parameter list,
the parameter defaults to <exception>StandardError</exception>.
<p/>
If no <tt>rescue</tt> clause matches, or if an exception is raised outside
a <tt>begin</tt>/<tt>end</tt> block, Ruby moves up the stack
and looks for an
exception handler in the caller, then in the caller's caller, and so on.
<p/>
Although the parameters to the <kw>rescue</kw> clause are typically the
names of <exception>Exception</exception> classes, they can actually be arbitrary
expressions (including method calls) that return an <classname>Exception</classname> class.
<subsection>Tidying Up</subsection>
<p/>
Sometimes you need to guarantee that some processing is done at the
end of a block of code, regardless of whether an exception was raised.
For example, you may have a file open on entry to the block, and you
need to make sure it gets closed as the block exits.
<p/>
The <kw>ensure</kw> clause does just this.
<kw>ensure</kw> goes after the last 
<kw>rescue</kw> clause and contains a chunk of code that will always be
executed as the block terminates. It doesn't matter if the block exits 
normally, if it raises and rescues an exception, or if it is terminated
by an uncaught exception---the <kw>ensure</kw> block will get run.
<p/>
<codefragment>
<alltt><fullcode><![CDATA[  f = File.open("testfile")
  begin
    # .. process
  rescue
    # .. handle error
  ensure
    f.close unless f.nil?
  end
]]></fullcode>
f<nbsp/>=<nbsp/>File.open("testfile")
begin
<nbsp/><nbsp/>#<nbsp/>..<nbsp/>process
rescue
<nbsp/><nbsp/>#<nbsp/>..<nbsp/>handle<nbsp/>error
ensure
<nbsp/><nbsp/>f.close<nbsp/>unless<nbsp/>f.nil?
end
</alltt>
</codefragment>
<p/>
The <kw>else</kw>
clause is a similar, although less useful, construct. If 
present, it goes after the <kw>rescue</kw> clauses and before any
<kw>ensure</kw>. The body of an <kw>else</kw> clause is executed only if no
exceptions are raised by the main body of code.
<p/>
<codefragment>
<alltt><fullcode><![CDATA[  f = File.open("testfile")
  begin
    # .. process
  rescue
    # .. handle error
  else
    puts "Congratulations-- no errors!"
  ensure
    f.close unless f.nil?
  end
]]></fullcode>
f<nbsp/>=<nbsp/>File.open("testfile")
begin
<nbsp/><nbsp/>#<nbsp/>..<nbsp/>process
rescue
<nbsp/><nbsp/>#<nbsp/>..<nbsp/>handle<nbsp/>error
else
<nbsp/><nbsp/>puts<nbsp/>"Congratulations--<nbsp/>no<nbsp/>errors!"
ensure
<nbsp/><nbsp/>f.close<nbsp/>unless<nbsp/>f.nil?
end
</alltt>
</codefragment>
<subsection>Play It Again</subsection>
<p/>
Sometimes you may be able to correct the cause of an exception. In
those cases, you can use the <kw>retry</kw> statement within a <kw>rescue</kw>
clause to repeat the entire <kw>begin</kw>/<kw>end</kw> block.
Clearly there
is tremendous scope for infinite loops here, so this is a feature to
use with caution (and with a finger resting lightly on the interrupt
key).
<p/>
As an example of code that retries on exceptions, have a look at the
following, adapted from Minero Aoki's <tt>net/smtp.rb</tt> library.
<p/>
<codefragment>
<alltt><fullcode><![CDATA[  @esmtp = true
  
  begin
    # First try an extended login. If it fails because the
    # server doesn't support it, fall back to a normal login
    
    if @esmtp then
      @command.ehlo(helodom)
    else
      @command.helo(helodom)
    end
    
  rescue ProtocolError
    if @esmtp then
      @esmtp = false
      retry
    else
      raise
    end
  end
]]></fullcode>
@esmtp<nbsp/>=<nbsp/>true
<p/>
begin
<nbsp/><nbsp/>#<nbsp/>First<nbsp/>try<nbsp/>an<nbsp/>extended<nbsp/>login.<nbsp/>If<nbsp/>it<nbsp/>fails<nbsp/>because<nbsp/>the
<nbsp/><nbsp/>#<nbsp/>server<nbsp/>doesn't<nbsp/>support<nbsp/>it,<nbsp/>fall<nbsp/>back<nbsp/>to<nbsp/>a<nbsp/>normal<nbsp/>login
<p/>
<nbsp/><nbsp/>if<nbsp/>@esmtp<nbsp/>then
<nbsp/><nbsp/><nbsp/><nbsp/>@command.ehlo(helodom)
<nbsp/><nbsp/>else
<nbsp/><nbsp/><nbsp/><nbsp/>@command.helo(helodom)
<nbsp/><nbsp/>end
<p/>
rescue<nbsp/>ProtocolError
<nbsp/><nbsp/>if<nbsp/>@esmtp<nbsp/>then
<nbsp/><nbsp/><nbsp/><nbsp/>@esmtp<nbsp/>=<nbsp/>false
<nbsp/><nbsp/><nbsp/><nbsp/>retry
<nbsp/><nbsp/>else
<nbsp/><nbsp/><nbsp/><nbsp/>raise
<nbsp/><nbsp/>end
end
</alltt>
</codefragment>
<p/>
This code tries first to connect to an SMTP server using the <tt>EHLO</tt> 
command, which is not universally supported. If the connection attempt
fails, the code sets the <var>@esmtp</var> variable to <const>false</const> and
retries the connection. If this fails again, the exception is reraised 
up to the caller.
<section>Raising Exceptions</section>
<p/>
So far we've been on the defensive, handling exceptions raised by
others.
It's time to turn the tables and go on the offensive. (There
are those that say your gentle authors are always offensive, but
that's a different book.)
<p/>
You can raise exceptions in your code with the <mmm><file>kernel</file><front>Kernel</front><back>raise</back><mref>raise</mref></mmm>
method.
<p/>
<codefragment>
<alltt><fullcode><![CDATA[raise
raise "bad mp3 encoding"
raise InterfaceException, "Keyboard failure", caller
]]></fullcode>
raise
raise<nbsp/>"bad<nbsp/>mp3<nbsp/>encoding"
raise<nbsp/>InterfaceException,<nbsp/>"Keyboard<nbsp/>failure",<nbsp/>caller
</alltt>
</codefragment>
<p/>
The first form simply reraises the current exception (or a
<exception>RuntimeError</exception> if there is no current exception). This is used in 
exception handlers that need to intercept an exception before passing
it on.
<p/>
The second form creates a new <exception>RuntimeError</exception> exception, setting its 
message to the given string. This exception is then raised up the call 
stack.
<p/>
The third form uses the first argument to create an exception and then
sets the associated message to the second argument and the stack
trace to the third argument. Typically the first argument will be either the
name of a class in the <exception>Exception</exception> hierarchy or a reference to an
object instance of one of these classes.<footnote>Technically, this
  argument can be any object that responds to the message
  <meth>exception</meth> by returning an object such that
  <tt>object.kind_of?(Exception)</tt> is true.</footnote> The stack trace is
normally produced using the <mmm><file>kernel</file><front>Kernel</front><back>caller</back><mref>caller</mref></mmm> method.
<p/>
Here are some typical examples of <meth>raise</meth> in action.
<p/>
<codefragment>
<alltt><fullcode><![CDATA[  raise

  raise "Missing name" if name.nil?

  if i >= myNames.size
    raise IndexError, "#{i} >= size (#{myNames.size})"
  end

  raise ArgumentError, "Name too big", caller
]]></fullcode>
raise
<p/>
raise<nbsp/>"Missing<nbsp/>name"<nbsp/>if<nbsp/>name.nil?
<p/>
if<nbsp/>i<nbsp/>&gt;=<nbsp/>myNames.size
<nbsp/><nbsp/>raise<nbsp/>IndexError,<nbsp/>"#{i}<nbsp/>&gt;=<nbsp/>size<nbsp/>(#{myNames.size})"
end
<p/>
raise<nbsp/>ArgumentError,<nbsp/>"Name<nbsp/>too<nbsp/>big",<nbsp/>caller
</alltt>
</codefragment>
<p/>
In the last example, we remove the current routine from the stack
backtrace, which is often useful in library modules. We can take this
further: the following code removes two routines from the backtrace.
<p/>
<codefragment>
<alltt><fullcode><![CDATA[  raise ArgumentError, "Name too big", caller[1..-1]
]]></fullcode>
raise<nbsp/>ArgumentError,<nbsp/>"Name<nbsp/>too<nbsp/>big",<nbsp/>caller[1..-1]
</alltt>
</codefragment>
<subsection>Adding Information to Exceptions</subsection>
<p/>
You can define your own exceptions to hold any information that you
need to pass out from the site of an error. For example, certain types 
of network errors might be transient depending on the circumstances.
If such an error occurs, and the circumstances are right, you could
set a flag in the exception to tell the handler that it might be worth 
retrying the operation.
<p/>
<codefragment>
<alltt><fullcode><![CDATA[  class RetryException < RuntimeError
    attr :okToRetry
    def initialize(okToRetry)
      @okToRetry = okToRetry
    end
  end
]]></fullcode>
class<nbsp/>RetryException<nbsp/>&lt;<nbsp/>RuntimeError
<nbsp/><nbsp/>attr<nbsp/>:okToRetry
<nbsp/><nbsp/>def<nbsp/>initialize(okToRetry)
<nbsp/><nbsp/><nbsp/><nbsp/>@okToRetry<nbsp/>=<nbsp/>okToRetry
<nbsp/><nbsp/>end
end
</alltt>
</codefragment>
<p/>
Somewhere down in the depths of the code, a transient error occurs.
<p/>
<codefragment>
<alltt><fullcode><![CDATA[!-  class RetryException < RuntimeError
!-    attr :okToRetry
!-    def initialize(okToRetry)
!-      @okToRetry = okToRetry
!-    end
!-  end
!-socket = STDIN
  def readData(socket)
    data = socket.read(512)
    if data.nil?
      raise RetryException.new(true), "transient read error"
    end
    # .. normal processing
  end
]]></fullcode>
def<nbsp/>readData(socket)
<nbsp/><nbsp/>data<nbsp/>=<nbsp/>socket.read(512)
<nbsp/><nbsp/>if<nbsp/>data.nil?
<nbsp/><nbsp/><nbsp/><nbsp/>raise<nbsp/>RetryException.new(true),<nbsp/>"transient<nbsp/>read<nbsp/>error"
<nbsp/><nbsp/>end
<nbsp/><nbsp/>#<nbsp/>..<nbsp/>normal<nbsp/>processing
end
</alltt>
</codefragment>
<p/>
Higher up the call stack, we handle the exception.
<p/>
<codefragment>
<alltt><fullcode><![CDATA[!-  class RetryException < RuntimeError
!-    attr :okToRetry
!-    def initialize(okToRetry)
!-      @okToRetry = okToRetry
!-    end
!-  end
!-  def readData(socket)
!-    data = socket.read(512)
!-    if data.nil?
!-      raise RetryException.new(true), "transient read error"
!-    end
!-    # .. normal processing
!-  end
!-    socket = STDIN
  begin
    stuff = readData(socket)
    # .. process stuff
  rescue RetryException => detail
    retry if detail.okToRetry
    raise
  end
]]></fullcode>
begin
<nbsp/><nbsp/>stuff<nbsp/>=<nbsp/>readData(socket)
<nbsp/><nbsp/>#<nbsp/>..<nbsp/>process<nbsp/>stuff
rescue<nbsp/>RetryException<nbsp/>=&gt;<nbsp/>detail
<nbsp/><nbsp/>retry<nbsp/>if<nbsp/>detail.okToRetry
<nbsp/><nbsp/>raise
end
</alltt>
</codefragment>
<section>Catch and Throw</section>
<p/>
While the exception mechanism of <kw>raise</kw> and <kw>rescue</kw> is great
for abandoning execution when things go wrong, it's sometimes nice to
be able to jump out of some deeply nested construct during normal
processing. This is where <kw>catch</kw> and <kw>throw</kw> come in handy.
<p/>
<codefragment>
<alltt><fullcode><![CDATA[!-songList = ""
!-def songList.play() end
catch (:done)  do
  while gets
    throw :done unless fields = split(/\t/)
    songList.add(Song.new(*fields))
  end
  songList.play
end
]]></fullcode>
catch<nbsp/>(:done)<nbsp/><nbsp/>do
<nbsp/><nbsp/>while<nbsp/>gets
<nbsp/><nbsp/><nbsp/><nbsp/>throw<nbsp/>:done<nbsp/>unless<nbsp/>fields<nbsp/>=<nbsp/>split(/\t/)
<nbsp/><nbsp/><nbsp/><nbsp/>songList.add(Song.new(*fields))
<nbsp/><nbsp/>end
<nbsp/><nbsp/>songList.play
end
</alltt>
</codefragment>
<p/>
<kw>catch</kw> defines a block that is labeled with the given name
(which may be a <classname>Symbol</classname> or a <classname>String</classname>). The block is executed
normally until a <kw>throw</kw> is encountered.
<p/>
When Ruby encounters a <kw>throw</kw>, it zips back up the call stack
looking for a <kw>catch</kw> block with a matching symbol.
When it finds
it, Ruby unwinds the stack to that point and terminates the block. If
the <kw>throw</kw> is called with the optional second parameter, that
value is returned as the value of the <kw>catch</kw>. So, in the previous
example, if the input does not contain correctly formatted lines, the
<kw>throw</kw> will skip to the end of the corresponding <kw>catch</kw>, not
only terminating the <tt>while</tt> loop but also skipping the playing of 
the song list.
<p/>
The following example uses a <kw>throw</kw> to terminate interaction with
the user if ``!'' is typed in response to any prompt.
<p/>
<codefragment>
<alltt><fullcode><![CDATA[def promptAndGet(prompt)
  print prompt
  res = readline.chomp
  throw :quitRequested if res == "!"
  return res
end

catch :quitRequested do
  name = promptAndGet("Name: ")
  age  = promptAndGet("Age:  ")
  sex  = promptAndGet("Sex:  ")
  # ..
  # process information
end
]]></fullcode>
def<nbsp/>promptAndGet(prompt)
<nbsp/><nbsp/>print<nbsp/>prompt
<nbsp/><nbsp/>res<nbsp/>=<nbsp/>readline.chomp
<nbsp/><nbsp/>throw<nbsp/>:quitRequested<nbsp/>if<nbsp/>res<nbsp/>==<nbsp/>"!"
<nbsp/><nbsp/>return<nbsp/>res
end
<p/>
catch<nbsp/>:quitRequested<nbsp/>do
<nbsp/><nbsp/>name<nbsp/>=<nbsp/>promptAndGet("Name:<nbsp/>")
<nbsp/><nbsp/>age<nbsp/><nbsp/>=<nbsp/>promptAndGet("Age:<nbsp/><nbsp/>")
<nbsp/><nbsp/>sex<nbsp/><nbsp/>=<nbsp/>promptAndGet("Sex:<nbsp/><nbsp/>")
<nbsp/><nbsp/>#<nbsp/>..
<nbsp/><nbsp/>#<nbsp/>process<nbsp/>information
end
</alltt>
</codefragment>
<p/>
As this example illustrates, the <kw>throw</kw> does not have to appear within the
static scope of the <kw>catch</kw>.
</chapter>
</ppdoc>