-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathtry.rkt
227 lines (222 loc) · 6.99 KB
/
try.rkt
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
#lang racket/base
(require (for-syntax racket/base
syntax/parse)
racket/function)
;; PURPOSE: Racket's with-handlers has poor end weight; it puts the
;; error conditions first, distracting from the actual point of the
;; code. try/catch is a better mechanism -- the code comes first
;; with the error handling tucked out of the way where you don't need
;; to look at it unless you actually need to look at it.
;; try/catch/finally also provides an easy way to do cleanup during
;; exception handling.
;
;; USAGE:
;;
;; Standard with-handlers. (NB: functions in the body are just examples, not real)
;; (with-handlers ([exn:fail:contract? (lambda (e) ...lots of stuff...)]
;; [string? (lambda (e) ...more stuff...)]
;; [exn:fail:tcp-connect? (lambda (e) ...even more stuff...)])
;;
;; (define server-handle (or (connect-to-server) (raise "could not connect")))
;; (define user (get-user-data server-handle (get-password)))
;; ...etc...
;; )
;;
;; ;With the above code you're four lines in before you find out what
;; ;the code actually does...and that's assuming that the exception
;; ;handlers are concise, one-line things, which they might not be.
;; ;Compare to an equivalent try/catch:
;;
;; (try [
;; (define server-handle (or (connect-to-server) (raise "could not connect")))
;; (define user (get-user-data server-handle (get-password)))
;; ...etc...
;; ]
;; [catch ([exn:fail:contract? (lambda (e) ...lots of stuff...)]
;; [string? (lambda (e) ...more stuff...)]
;; [exn:fail:tcp-connect? (lambda (e) ...even more stuff...)])])
;;
;; ; With the try/catch block you immediately know that you're
;; ; dealing with something that's connecting to a server and fetching
;; ; user data. You can read the happy path first to understand
;; ; what's supposed to happen, then look in the 'catch' block to see
;; ; how it handles problems.
;;
;;
;; ; But wait, there's more! Want to have some cleanup that is
;; ; guaranteed to happen even if there's an exception? We've got you
;; ; covered
;; (try [(displayln "foo") (raise-syntax-error 'name "message")]
;; [catch (exn:fail? (lambda (e) (displayln "caught!")))]
;; [finally (displayln "finally 1") (displayln "finally 2")]
;; )
;;
;; Output:
;; foo
;; finally 1
;; finally 2
;; caught!
;;
;; ; Still not enough? How about some preflight setup that is
;; ; guaranteed to happen before the body executes, even in the
;; ; presence of jumping in and out via continuation?
;; (try [(displayln "body") (raise "foo")]
;; [pre (displayln "pre")]
;; [catch (string? (lambda (e) (displayln "caught!")))]
;; [finally (say "finally")])
;;
;; Output:
;; pre
;; body
;; finally!
;; caught!
;;
;; ; 'try' still works inside a dynamic-wind even though it generates one:
;; (dynamic-wind
;; (thunk (displayln "outer pre"))
;; (thunk (try [(displayln "body") (raise "error!")]
;; [pre (displayln "body pre")]
;; [catch (string? (lambda (e) (displayln "body catch")))]
;; [finally (displayln "body finally")]))
;; (thunk (displayln "outer finally")))
;;
;; Output:
;; outer pre
;; body pre
;; body
;; body finally
;; body catch
;; outer finally
;;
;; The return value from a try is:
;; *) If no exception raised: The result of the 'try' block
;; *) If exception raised: The result of the 'catch' block
;;
;; The following combinations are legal:
;; try ; traps anything that's raised, returns it. (i.e., defatalizes it)
;; try + catch
;; try + pre + catch
;; try + catch + finally
;; try + pre + catch + finally
(define-syntax (try stx)
(syntax-parse stx
#:datum-literals (try catch pre finally)
[(try [body0:expr body1:expr ...])
#'(with-handlers ((identity identity))
body0
body1
...
)]
[(try [body0:expr body1:expr ...]
[catch catch0:expr catch1:expr ...])
#'(with-handlers (catch0 catch1 ...)
body0
body1
...
)]
[(try [pre p0:expr p1:expr ... ]
[body0:expr body1:expr ...])
#'(with-handlers ((identity identity))
(dynamic-wind
(thunk p0 p1 ...)
(thunk body0
body1
...)
(thunk '()))
)]
[(try [pre p0:expr p1:expr ... ]
[body0:expr body1:expr ...]
[catch catch0 catch1 ...])
#'(with-handlers (catch0 catch1 ...)
(dynamic-wind
(thunk p0 p1 ...)
(thunk body0
body1
...)
(thunk '()))
)]
[(try [pre p0:expr p1:expr ... ]
[body0:expr body1:expr ...]
[finally f0 f1 ...])
#'(with-handlers ((identity identity))
(dynamic-wind
(thunk p0 p1 ...)
(thunk body0
body1
...)
(thunk f0 f1 ...))
)]
[(try [pre p0:expr p1:expr ... ]
[body0:expr body1:expr ...]
[catch catch0 catch1 ...]
[finally f0 f1 ...])
#'(with-handlers (catch0 catch1 ...)
(dynamic-wind
(thunk p0 p1 ...)
(thunk body0
body1
...)
(thunk f0 f1 ...))
)]
[(try [body0 body1 ...]
[catch catch0 catch1 ...]
[finally f0 f1 ... ])
#'(with-handlers (catch0 catch1 ...)
(dynamic-wind
(thunk '())
(thunk body0
body1
...)
(thunk f0 f1 ...)
))]
[(try [body0 body1 ...]
[finally f0 f1 ... ])
#'(with-handlers ([(lambda (x) #t) raise])
(dynamic-wind
(thunk '())
(thunk body0
body1
...)
(thunk f0 f1 ...)
))]
[(try [body0 body1 ...]
[pre p0 p1 ... ]
[finally f0 f1 ... ])
#'(with-handlers ([(lambda (x) #t) raise])
(dynamic-wind
(thunk p0 p1 ...)
(thunk body0
body1
...)
(thunk f0 f1 ...)
))]
[(try [body0 body1 ...]
[pre pre0 pre1 ...]
[catch catch0 catch1 ... ])
#'(with-handlers (catch0 catch1 ...)
(dynamic-wind
(thunk pre0 pre1 ...)
(thunk body0 body1 ...)
(thunk '())
))]
[(try [body0 body1 ...]
[pre pre0 pre1 ...]
[catch catch0 catch1 ...]
[finally f0 f1 ... ])
#'(with-handlers (catch0 catch1 ...)
(dynamic-wind
(thunk pre0 pre1 ...)
(thunk body0
body1
...)
(thunk f0 f1 ...)
))]
))
(define-syntax (defatalize stx)
(syntax-parse stx
[(defatalize body0 body1 ...)
#'(with-handlers ([exn:break? raise]
[(lambda (e) #t) identity])
body0
body1 ...)]))
(provide (all-defined-out))