-
Notifications
You must be signed in to change notification settings - Fork 0
/
error_handling.slide
332 lines (172 loc) · 7.66 KB
/
error_handling.slide
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
Less verbose error handling
* Agenda
- How errors are handled now
- How we can do better
- How errors will be handled in the future
- Summary
* How errors are handled now
* Docker
.code examples/docker.go /START/,/END/
* ETCD
.code examples/etcd.go /START/,/END/
* Kubernetes
.code examples/kubernetes.go /START/,/END/
* and many more
.image resources/most_common_words.png
* How we can do better
* Example
.code standard/main.go /START getCommandsFromFile/,/END getCommandsFromFile/
* Example
.code standard/main.go /START readConfiguration/,/END readConfiguration/
* Example
.code standard/main.go /START parseConfiguration/,/END parseConfiguration/
* Example
.code standard/main.go /START calculateCommands/,/END calculateCommands/
* Example
.code standard/main.go /START calculateDownCommands/,/END calculateDownCommands/
* Example
.code standard/main.go /START calculateUpCommands/,/END calculateUpCommands/
* Example
.play standard/main.go /START main/,/END main/
* First improvement
* Error in struct
.code standard/main.go /START readConfiguration/,/END readConfiguration/
* Error in struct
we are using the same function twice
maybe we can move error handling there?
.code standard/main.go /START ReadLineReadConfiguration/,/END ReadLineReadConfiguration/
* Error in struct
we can create stateful reader with error
.code error_in_struct/main.go /START ErrorReader/,/END ErrorReader/
.code error_in_struct/main.go /START NewErrorReader/,/END NewErrorReader/
.code error_in_struct/main.go /START ErrorReader Err/,/END ErrorReader Err/
* Error in struct
and then check error inside ReadLine function
.code error_in_struct/main.go /START ErrorReader ReadLine/,/END ErrorReader ReadLine/
* Error in struct
.code standard/main.go /START readConfiguration/,/END readConfiguration/ HL_error_in_struct
* Error in struct
.code error_in_struct/main.go /START readConfiguration/,/END readConfiguration/ HL_error_in_struct
* Error in struct
.play error_in_struct/main.go /START main/,/END main/
* Second improvement
* Generated code for error in struct
.code standard/main.go /START parseConfiguration/,/END parseConfiguration/
* Generated code for error in struct
we could try using the same approach here,
but now we have two function instead of one
.code standard/main.go /START ErrorCheckerParseConfiguration/,/END ErrorCheckerParseConfiguration/
* Generated code for error in struct
Error checker will be similar
.code check/main.go /START NewErrorChecker/,/END NewErrorChecker/
.code check/main.go /START ErrorChecker Err/,/END ErrorChecker Err/
* Generated code for error in struct
but the library functions can be generated
.code check/main.go /START ErrorChecker StrconvAtoi/,/END ErrorChecker StrconvAtoi/
.code check/main.go /START ErrorChecker JsonUnmarshal/,/END ErrorChecker JsonUnmarshal/
* Generated code for error in struct
.code standard/main.go /START parseConfiguration/,/END parseConfiguration/ HL_check
* Generated code for error in struct
.code check/main.go /START parseConfiguration/,/END parseConfiguration/ HL_check
* Generated code for error in struct
.play check/main.go /START main/,/END main/
* Third improvement
* Monad
.code standard/main.go /START calculateCommands/,/END calculateCommands/
* Monad
we have multiple custom functions,
so the previous approaches doesn't work
.code standard/main.go /START MonadCalculateCommands/,/END MonadCalculateCommands/
* Monad
we can try creating a struct without error this time
.code monad/main.go /START ConfigurationCalculator/,/END ConfigurationCalculator/
.code monad/main.go /START NewConfigurationCalculator/,/END NewConfigurationCalculator/
.code monad/main.go /START ConfigurationCalculator GetCommands/,/END ConfigurationCalculator GetCommands/
* Monad
and return error instead
.code monad/main.go /START ConfigurationCalculator calculateDownCommands/,/END ConfigurationCalculator calculateDownCommands/
.code monad/main.go /START ConfigurationCalculator calculateUpCommands/,/END ConfigurationCalculator calculateUpCommands/
* Monad
then we can chain these calls until the first error
.code monad/main.go /START Do/,/END Do/
* Monad
.code standard/main.go /START calculateCommands/,/END calculateCommands/ HL_monad
* Monad
.code monad/main.go /START calculateCommands/,/END calculateCommands/ HL_monad
* Monad
.play monad/main.go /START main/,/END main/
* Fourth improvement
* Generic Monad
.code standard/main.go /START getCommandsFromFile/,/END getCommandsFromFile/
* Generic Monad
now we have multiple variables
each of which we are using only once
.code standard/main.go /START GenericMonadGetCommandsFromFile/,/END GenericMonadGetCommandsFromFile/
* Generic Monad
the previous approach gives too many variables
.code generic_monad/main.go /START CommandGetter/,/END CommandGetter/
* Generic Monad
and too much boilerplate with errors
.code generic_monad/main.go /START CommandGetter readConfiguration/,/END CommandGetter readConfiguration/
.code generic_monad/main.go /START CommandGetter parseConfiguration/,/END CommandGetter parseConfiguration OMIT/
.code generic_monad/main.go /START CommandGetter calculateCommands/,/END CommandGetter calculateCommands/
* Generic Monad
we could try using generic functions taking and returning `interface{}` arguments
.code generic_monad/main.go /START Func/,/END Func/
.code generic_monad/main.go /START DoEither/,/END DoEither/
* Generic Monad
unfortunately this does not compile
.play generic_monad_wrong/main.go /START getCommandsFromFile doesNotCompile/,/END getCommandsFromFile doesNotCompile/
* Generic Monad
however we can use reflect to wrap our functions
.code generic_monad/main.go /START EitherWrap/,/END EitherWrap/
* Generic Monad
to make this work we also need to make the result type
.code generic_monad/main.go /START TypeStringSlice/,/END TypeStringSlice/
* Generic Monad
.code standard/main.go /START getCommandsFromFile/,/END getCommandsFromFile/ HL_generic_monad
* Generic Monad
.code generic_monad/main.go /START getCommandsFromFile/,/END getCommandsFromFile/ HL_generic_monad
* Generic Monad
.play generic_monad/main.go /START main/,/END main/
* Go 2
* Go 2
in go 2 we will have `check` statement which will exit current scope with error when underlying function returns error
.code go2/go2.go /START getCommandsFromFile/,/END getCommandsFromFile/ HL_check
* Go 2
.code go2/go2.go /START readConfiguration/,/END readConfiguration/ HL_check
* Go 2
.code go2/go2.go /START parseConfiguration/,/END parseConfiguration/ HL_check
* Go 2
.code go2/go2.go /START calculateCommands/,/END calculateCommands/ HL_check
* Go 2
can we recreate this behavior in go 1?
* Go 2
we can, but we have to use panics
.code go2/main.go /START Error/,/END Error/
.code go2/main.go /START NewError/,/END NewError/
.code go2/main.go /START check/,/END check/
* Go 2
we have to have custom error to distinguish between our errors and different panics
.code go2/main.go /START handle/,/END handle/
* Go 2
we do not need reflect, we can use type assertions
.code go2/main.go /START getCommandsFromFile/,/END getCommandsFromFile/ HL_check
* Go 2
.play go2/main.go /START main/,/END main/
* Go 2
also we don't lose stack traces from panics with this approach
.code go2_panic/main.go /START calculateDownCommands/,/END calculateDownCommands/
.play go2_panic/main.go /START main/,/END main/
* Summary
* Summary
we have covered the following way of error handling:
- error in struct
- error in struct with generated code
- monads with stateful structs
- generic monads with functions
- go2 style checks
* Summary
some of these methods are used in practice
.code examples/bufio.go /START Writer/,/END Writer/
.code examples/bufio.go /START Writer Flush/,/END Writer Flush/